STM32 / GD32第六章:使用软件I2C读取温度传感器LM75AD

关联:STM32总结超全笔记【秋招自用】

【引言】

学习IIC通讯协议之前,我们回忆一下之前学习的第一个通信协议USART串口。

串口通信协议的特点
是串行
的、
异步
的、
半双工
通信。

  • 串行:数据位按照顺序一个接一个地被发送和接收,而不是同时传输多个数据位。
  • 异步:发送和接收数据的时钟不是同步的,发送方和接收方的时钟不需要保持同步。
  • 半双工:数据可以在两个方向上进行传输,但不能同时进行。
  •  【问】串口通信有几根线?

    两根:TX和RX,一条收,一条发。

    【IIC特点】

    本章学习的IIC通信协议的特点是串行的、同步的、半双工的。

    IIC是同步的,说明它必须有一条时钟线SCL,另外数据线有几条呢?

    只有一条SDA数据线,所以决定了他是半双工的,来看一下图示:

    如图:设备有主机STM32和若干从机,每个连接到总线的从机都有唯一的地址,任何器件也都可以作为主机,但是同一时刻只能拥有一个主机。

    【问】之前在GPIO模式,讲到开漏输出的时候说到IIC特别适合开漏输出,为什么呢?

    这是由IIC总线内部结构决定的,总线内部使用漏极开漏输出驱动器,所以SDA和SCL两根线都使用一个上拉电阻(上图),使用的时候就拉低为低电平,但是不能被驱动为高电平,平时为高阻态。

    PS:这个上拉值有一个典型值4.7KΩ

    【IIC通信协议】

    【问】那么IIC的通信过程是什么样的?

    一般分为四个部分:

    1. 通信开始
    2. 地址传送(通过地址选择传送数据目标)
    3. 数据传送(收发数据)
    4. 通信结束

    我觉得大家可以思考一下,如果只给你两根线,一根时钟线一根数据线,条件是如此的艰难,那么要实现主机对从从机,或者是从机对主机的选择,以及数据的发送,数据的接收,甚至还需要应答机制,不然对方怎么知道数据收没收到??你会怎么做????

     【数据帧格式】

     数据帧格式,对应上方的通信过程。

     【起始和结束信号】

    【应答机制】

     【数据有效性】

     【总结】

    最后详细叙述一下通讯过程:

    1.开始:主设备把SDA从高拉低,再把SCL从高拉低,对总线上的从机说:我要开始和你们中的某个人通信了。

    2.主设备发送要与之通信的从机的7位地址(一般是7位),第八位是读写位(读1写0)

    3.总线上的从设备把主设备发送的地址与自己的地址比较,如果匹配:从设备将SDA拉低一位表示应答(此时从设备获得SDA的控制权,之前SDA都是主设备控制的)。如果不拉低,SDA为高就表示非应答。

    4.主设备发送(或接收)数据给从设备。SCL为高,读(写)数据,SCL为低,准备下一位的数据。

    5.数据传输完毕,从设备返回一个应答给主设备

    6.停止:主设备把SCL从低拉高,再把SDA从低拉高,表示停止通信。

    【软件模拟IIC】

    了解了通信协议,我们具体怎么使用呢?

    这里我们使用软件模拟IIC通信协议,软件模拟I2C不需要额外的硬件支持,只需要使用微控制器的GPIO引脚和软件实现即可。此外灵活性和可移植性非常高。

    我们一步步来,这章使用的是GD32F1103,但其实和STM32是完全一样的。

    首先我们要控制SDA和SCL的电平,就要封装一个写引脚SDA和SCL 1或0 的函数:

    (IIC的引脚都宏定义了,函数基本和STM32一样的)

    【起始条件】SDA从高拉低,再把SCL从高拉低 

    【结束条件】SCL从低拉高,再把SDA从低拉高

    【应答】从设备将SDA拉表示应答

    【非应答】从设备将SDA拉表示非应答

    【主机发送一个字节数据给从机】

    【主机读从机一个字节数据】

    【主机读应答】

    这个读SDA也就是把  gpio_input_bit_get  封装了一下:

     那么软件模拟IIC已经被我们封装好了,接下来做实验。

    【例程11】软件IIC读取温度传感器LM75AD

    首先硬件连接:  PB6 — SCL     PB7 — SDA

    实验目的: 温度寄存器的值拿出来转化为浮点型的数值,通过串口发送到主机

     【IIC和GPIO初始化】

    第一步不用想也是初始化

    #define I2C_SOFT_RCU   RCU_GPIOB
    #define I2C_SOFT_PORT  GPIOB
    #define I2C_SOFT_SCL_PIN   GPIO_PIN_6
    #define I2C_SOFT_SDA_PIN   GPIO_PIN_7
    
    //初始化函数
    void my_i2c_init(void){
        //打开IIC时钟
    	rcu_periph_clock_enable(I2C_SOFT_RCU);
        
        //初始化GPIO
    	gpio_init(I2C_SOFT_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, I2C_SOFT_SCL_PIN|I2C_SOFT_SDA_PIN);
    	
        //GPIO引脚置1(高阻态)
    	gpio_bit_set(I2C_SOFT_PORT, I2C_SOFT_SCL_PIN|I2C_SOFT_SDA_PIN);
    }
    

    【读温度寄存器的值(未转换)】

    首先封装一个函数,用来向IIC总线上写入器件地址以及要用到的温度寄存器地址

    uint8_t lm75a_write_addr(uint8_t id_rw, uint8_t reg_addr){
    	my_i2c_start();
    	my_i2c_send_byte(id_rw);
    	my_i2c_read_ack();
    	my_i2c_send_byte(reg_addr);
    	my_i2c_read_ack();
    	
    	return 0;
    }

    然后就是读温度寄存器的值。

    #define LM75A_I2C_ADDR  0x9E   //LM75A的从机地址
    #define LM75A_TEMP_REG  0x00   //温度寄存器的指针地址
    
    #define IIC_WRITE 0
    #define IIC_READ 1
    
    /***
    功能:读温度寄存器的值
    输入:
    	uint8_t lm75a_id: lm75a的iic从机地址
    	uint8_t reg:要操作的寄存器的指针
    	uint8_t *p:读取结果存放的位置
    	uint8_t len:寄存器的字节长度(1 or 2)
    返回:无
    *****/
    void lm75a_read_reg(uint8_t lm75a_id, uint8_t reg, uint8_t *p, uint8_t len){
    	//向iic总线上写入器件地址、指针字节
    	lm75a_write_addr(lm75a_id|IIC_WRITE, reg);
    	my_i2c_start();
    	my_i2c_send_byte(lm75a_id|IIC_READ);
    	my_i2c_read_ack();
    	
    	uint8_t i;
    	
    	for(i = 0; i < len; i++){
    		*p++ = my_i2c_read_byte();
    		if(i != (len-1))
    			my_i2c_ack();
    	}
    	my_i2c_nack();
    	
    	my_i2c_stop();
    }

    详细如下图:

    【读温度传感器的温度寄存器的值并转换为温度值】

    // 读温度传感器的温度寄存器的值并转换为温度值
    float lm75a_get_temp(void){
    	float temp_result;
    	//读温度寄存器值
    	uint8_t byte_data[2];
    	lm75a_read_reg(LM75A_I2C_ADDR, LM75A_TEMP_REG, byte_data, 2);
    	
    	//将温度寄存器值转为温度值
    	uint16_t temp_reg = byte_data[0]<<3 | byte_data[1]>>5;
    	
    	if((temp_reg & 0x0400) == 0){
    		temp_result = temp_reg * 0.125;
    	}else{
    		temp_reg = (~((temp_reg&0x03ff)-1)) & 0x03ff;    //补码到原码转换
    		temp_result = temp_reg * (-0.125);
    	}
    	
    	return temp_result;
    }
    

     查阅数据手册,得到温度寄存器和温度值的转换方法:

    判断这个寄存器的第十位是0还是1,对应着不同的计算方法:

    【主函数】

    lm75a_init();
    	
    	while(1){
    		temp_result = lm75a_get_temp();
    		sprintf(temp_string, "temperature is: %.3f C.\n", temp_result);
    		usart0_send_string((uint8_t *)temp_string); 
    		delay_1ms(1000);  //等待1s
    	}

    作者:XJHDZ

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 / GD32第六章:使用软件I2C读取温度传感器LM75AD

    发表回复