• GD32F103x IIC通信


    1. IIC通信

    1.IIC的介绍

       IIC总线有两条串行线,其一是时钟线SCK(同步),其二是数据线SDA。只有一条数据线属于半双工。应用中,单片机常常作为主机,外围器件可以挂载多个。(当然主机也可以有多个。多个主机都要访问从机需要仲裁机制。)每一个从机器件都拥有唯一的一个地址。  这样才能区分主机与哪个从机通信。如下图:

    有两个从机与一个主机。


    由于在设置SDA,SCL两个串行时GPIO时都会设置成开漏模式。开漏模式需要通过上拉电阻拉到高电平。

    那么为什么不用复用推挽呢?都知道推挽能输出稳定的高低电平。当某一主机SDA为高,某一从机SDA为低。那么很可能短路。如果是开漏带上拉电阻的。就直接上拉电阻流向从机SDA低。这样是比较安全的。开漏模式组成线与的关系,任何一个器件都可以拉低电平。

    2. IIC的时序图 

    分为起始信号,数据传输,应答信号,停止信号

    1. 起始信号 ,停止信号

    2. 数据传输信号 

    在SCL高电平时SDA必须稳定不变。等待从机读取。所以在SCL低电平时才可以改变。

    3.应答信号。 

    总结:我们在起始信号之后,主机开始发送传输的数据;在串行时钟线 SCL 为低电平状态时,SDA 允许改变传输的数据位(1 为高电平,0 为低电平),在SCL 为高电平状态时,SDA 要求保持稳定,相当于一个时钟周期传输 1bit 数据,经过8 个时钟周期后,传输了 8bit 数据,即一个字节。第8 个时钟周期末,主机释放SDA 以使从机应答,在第 9 个时钟周期,从机将 SDA 拉低以应答;如果第 9 个时钟周期,SCL 为高电平时,SDA 未被检测到为低电,视为非应答,表明此次数据传输失败。第 9 个时钟周期末,从机释放 SDA 以使主机继续传输数据,如果主机发送停止信号,此次传输结束。我们要注意的是数据以8bit 即一个字节为单位串行发出,其最先发送的是字节的最高位。 

    3.IIC具体详细的数据帧格式

    具体注意主机给从机呢还是从机给主机发送。看颜色的不同。

     4. IIC 实现的方式

    对于有些51单片机可能没有IIC的硬件驱动。要用模拟GPIO的方式来实现。但现在的单片机都会有

    硬件的IIC驱动。直接用就好了。

    2. LM75温度传感器 

    1.LM75的概述

    1. LM75的AD转换原理 

    内部的温度传感器感知一个模拟信号。

    2. LM75框图

    内部有一个温度传感器。通过操作指针寄存器读取温度传感器的值。

    3. 温度怎么保存及怎么计算。 

     4. LM75寄存器介绍。

    1. 配置寄存器

    总结:具体详细的看LM75的手册。

    3. doem (IIC 读取LM75温度)

    模拟IIC的方式来获取LM75的温度。通过串口打印出来。

    硬件电路:用PB6,PB7GPIO口来模拟。

     

    1. LM75的唯一地址(查手册)

    1001  A0 = 1   A1 = 1  A2 =1 = 0x9E

    2. 指针寄存器操作

    指针寄存器包含一个 8 位的数据字节,低 2 位是其它 4 个寄存器的指针值,高 6 位等于 0 ,见指针寄 存器表格(表 3 )和指针值表格(表 4 )。指针寄存器对于用户来说是不可访问的,但通过将指针数据字节 包含到总线命令中可选择进行读/ 写操作的数据寄存器。

     配置寄存器(01):

    3. 读温度寄存器的流程

     

    4. 写配置寄存器的流程

     

    总结:代码都是按流程来的。配合流程一起看。 

    my_i2c_soft.h

    1. #ifndef MY_I2C_SOFT_H
    2. #define MY_I2C_SOFT_H
    3. /**
    4. @brief: 软件I2C的实现
    5. */
    6. #include "gd32f10x.h"
    7. #define I2C_SOFT_RCU RCU_GPIOB
    8. #define I2C_SOFT_PORT GPIOB
    9. #define I2C_SOFT_SCL_PIN GPIO_PIN_6
    10. #define I2C_SOFT_SDA_PIN GPIO_PIN_7
    11. void my_i2c_init(void); /* i2c的初始化 */
    12. void my_i2c_start(void); /* i2c的起始 */
    13. void my_i2c_stop(void); /* i2c的结束 */
    14. void my_i2c_send_byte(uint8_t byte_to_send); /* SDA发送一个byte */
    15. uint8_t my_i2c_read_byte(void); /* SDA读取一个byte */
    16. void my_i2c_ack(void); /* 主机应答 */
    17. void my_i2c_nack(void); /* 主机非应答 */
    18. uint8_t my_i2c_read_ack(void); /* 从机应答 */
    19. #endif

    my_i2c_soft.c

    1. #include "my_i2c_soft.h"
    2. #include "systick.h"
    3. void my_i2c_w_SDA(uint8_t bit_value)
    4. {
    5. gpio_bit_write(I2C_SOFT_PORT, I2C_SOFT_SDA_PIN, (bit_status)bit_value);
    6. delay_1us(10);
    7. }
    8. void my_i2c_w_SCL(uint8_t bit_value)
    9. {
    10. gpio_bit_write(I2C_SOFT_PORT, I2C_SOFT_SCL_PIN, (bit_status)bit_value);
    11. delay_1us(10);
    12. }
    13. uint8_t my_i2c_r_SDA(void){
    14. return gpio_input_bit_get(I2C_SOFT_PORT, I2C_SOFT_SDA_PIN);
    15. }
    16. //初始化函数
    17. void my_i2c_init(void){
    18. rcu_periph_clock_enable(I2C_SOFT_RCU);
    19. /* 开漏模式 */
    20. gpio_init(I2C_SOFT_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, I2C_SOFT_SCL_PIN|I2C_SOFT_SDA_PIN);
    21. gpio_bit_set(I2C_SOFT_PORT, I2C_SOFT_SCL_PIN|I2C_SOFT_SDA_PIN);
    22. }
    23. //i2c的起始
    24. void my_i2c_start(void){
    25. //SDA高,SCL高,SDA低,SCL低;
    26. my_i2c_w_SDA(1);
    27. my_i2c_w_SCL(1);
    28. my_i2c_w_SDA(0);
    29. my_i2c_w_SCL(0); /* 为传输数据做准备,只有SCL才可以改变数据 */
    30. }
    31. //i2c的结束
    32. void my_i2c_stop(void){
    33. my_i2c_w_SDA(0);
    34. my_i2c_w_SCL(1);
    35. my_i2c_w_SDA(1);
    36. }
    37. //SDA发送一个byte
    38. void my_i2c_send_byte(uint8_t byte_to_send){
    39. uint8_t i;
    40. for(i = 0; i < 8; i++){
    41. my_i2c_w_SDA(byte_to_send & (0x80>>i));
    42. my_i2c_w_SCL(1); /* 从机来读 */
    43. my_i2c_w_SCL(0);
    44. }
    45. }
    46. //SDA读一个byte
    47. uint8_t my_i2c_read_byte(void){
    48. uint8_t result = 0x00;
    49. my_i2c_w_SDA(1); /* 主机释放 */
    50. uint8_t i;
    51. for(i = 0; i < 8; i++){
    52. my_i2c_w_SCL(1);
    53. if(my_i2c_r_SDA())
    54. result = result | (0x80 >> i);//读SDA的bit值
    55. my_i2c_w_SCL(0);
    56. }
    57. return result;
    58. }
    59. //主机应答
    60. void my_i2c_ack(void){
    61. my_i2c_w_SCL(0);
    62. my_i2c_w_SDA(0);
    63. my_i2c_w_SCL(1);
    64. my_i2c_w_SCL(0);
    65. }
    66. //主机非应答
    67. void my_i2c_nack(void){
    68. my_i2c_w_SCL(0);
    69. my_i2c_w_SDA(1);
    70. my_i2c_w_SCL(1);
    71. my_i2c_w_SCL(0);
    72. }
    73. //主机读应答
    74. uint8_t my_i2c_read_ack(void){
    75. uint8_t ack_result;
    76. my_i2c_w_SDA(1); /* 主机释放 */
    77. my_i2c_w_SCL(1);
    78. ack_result = my_i2c_r_SDA();
    79. my_i2c_w_SCL(0);
    80. return ack_result;
    81. }

     lm75a_temp.h

    1. #ifndef __LM75A_TEMP_H
    2. #define __LM75A_TEMP_H
    3. #include "gd32f10x.h"
    4. #include "my_i2c_soft.h"
    5. #define LM75A_I2C_ADDR 0x9E /* LM75A的从机地址 */
    6. #define LM75A_TEMP_REG 0x00 /* 温度寄存器的指针地址 */
    7. #define LM75A_CONF_REG 0x01 /* 配置寄存器(Conf)*/
    8. #define LM75A_THYST_REG 0x10 /* 滞后寄存器(Thyst) */
    9. #define LM75A_TOS_REG 0x11 /* 过热关断寄存器(Tos) */
    10. #define IIC_WRITE 0
    11. #define IIC_READ 1
    12. void lm75a_init(void);
    13. float lm75a_get_temp(void); //获取温度传感器的温度值
    14. void lm75a_poweroff(uint8_t id_rw,uint8_t reg_addr,uint8_t data); //关断温度传感器
    15. void lm75a_read_reg(uint8_t lm75a_id, uint8_t reg, uint8_t *p, uint8_t len); //读温度寄存器的值
    16. uint8_t lm75a_write_addr(uint8_t id_rw, uint8_t reg_addr);
    17. #endif

     lm75a_temp.c

    1. #include "lm75a_temp.h"
    2. #include "systick.h"
    3. void lm75a_init(void){
    4. my_i2c_init();
    5. }
    6. // 读温度传感器的温度寄存器的值并转换为温度值
    7. float lm75a_get_temp(void){
    8. float temp_result;
    9. //读温度寄存器值
    10. uint8_t byte_data[2];
    11. lm75a_poweroff(LM75A_I2C_ADDR | IIC_WRITE, LM75A_CONF_REG,0x00); /* 开启LM75 */
    12. lm75a_read_reg(LM75A_I2C_ADDR, LM75A_TEMP_REG, byte_data, 2);
    13. delay_1ms(100); /* 需要延时100ms */
    14. lm75a_poweroff(LM75A_I2C_ADDR | IIC_WRITE, LM75A_CONF_REG,0x01); /* 关闭LM75 */
    15. //将温度寄存器值转为温度值
    16. uint16_t temp_reg = byte_data[0]<<3 | byte_data[1]>>5;
    17. /* 判断D10是正负还是负数 0:正 1::负数 */
    18. if((temp_reg & 0x0400) == 0){
    19. temp_result = temp_reg * 0.125;
    20. }else{
    21. temp_reg = (~((temp_reg&0x03ff)-1)) & 0x03ff; //补码到原码转换
    22. temp_result = temp_reg * (-0.125);
    23. }
    24. return temp_result;
    25. }
    26. /***
    27. 功能:读温度寄存器的值
    28. 输入:
    29. uint8_t lm75a_id: lm75a的iic从机地址
    30. uint8_t reg:要操作的寄存器的指针
    31. uint8_t *p:读取结果存放的位置
    32. uint8_t len:寄存器的字节长度(1 or 2)
    33. 返回:无
    34. *****/
    35. void lm75a_read_reg(uint8_t lm75a_id, uint8_t reg, uint8_t *p, uint8_t len){
    36. //向iic总线上写入器件地址、指针字节
    37. lm75a_write_addr(lm75a_id|IIC_WRITE, reg);
    38. my_i2c_start();
    39. my_i2c_send_byte(lm75a_id|IIC_READ);
    40. my_i2c_read_ack();
    41. uint8_t i;
    42. for(i = 0; i < len; i++){
    43. *p++ = my_i2c_read_byte();
    44. if(i != (len-1))
    45. my_i2c_ack();
    46. }
    47. my_i2c_nack();
    48. my_i2c_stop();
    49. }
    50. /***
    51. 输入:
    52. uint8_t id_rw:从机地址|读写标识
    53. **/
    54. uint8_t lm75a_write_addr(uint8_t id_rw, uint8_t reg_addr){
    55. my_i2c_start();
    56. my_i2c_send_byte(id_rw);
    57. my_i2c_read_ack();
    58. my_i2c_send_byte(reg_addr);
    59. my_i2c_read_ack();
    60. return 0;
    61. }
    62. void lm75a_poweroff(uint8_t id_rw,uint8_t reg_addr,uint8_t data) //关断温度传感器
    63. {
    64. lm75a_write_addr(id_rw, reg_addr);
    65. my_i2c_send_byte(data);
    66. my_i2c_read_ack();
    67. my_i2c_stop();
    68. }

     main.c

    1. #include
    2. #include "systick.h"
    3. #include "usart_comm.h"
    4. #include "lm75a_temp.h"
    5. int main(){
    6. systick_config();
    7. usart0_init(9600);
    8. float temp_result;
    9. char temp_string[80];
    10. lm75a_init();
    11. while(1){
    12. temp_result = lm75a_get_temp();
    13. sprintf(temp_string, "temperature is: %.3f C.\n", temp_result); /* 把浮点转成字符 */
    14. usart0_send_string((uint8_t *)temp_string); /* 打印温度 */
    15. delay_1ms(1000); //等待1s
    16. }
    17. }

     最终结果打印:

  • 相关阅读:
    每日三题 6.30
    Django实现音乐网站 ⒅
    力扣leetcode 827. 最大人工岛
    (iView)表格JSON字符串转为键值对,去掉对象的双引号和花括号,省略号隐藏,悬浮显示完整内容
    [BJDCTF2020]ZJCTF,不过如此
    ASP.NET Core高性能服务器HTTP.SYS
    BIP学习第一课,创建一个应用和实体并设计页面(图文)
    Vue路由守卫
    万变不离其宗:利用VSCode进行花式编译与调试
    网站降权的康复办法(详解百度SEO数据分析)
  • 原文地址:https://blog.csdn.net/qq_41328470/article/details/133524135