糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > STM32 GPIO模拟i2c通信实现sht20的温湿度采样 并以JSON格式上报(串口调试助手为例)

STM32 GPIO模拟i2c通信实现sht20的温湿度采样 并以JSON格式上报(串口调试助手为例)

时间:2021-11-03 15:51:12

相关推荐

STM32 GPIO模拟i2c通信实现sht20的温湿度采样 并以JSON格式上报(串口调试助手为例)

一、先了解I2C协议

由时钟线SCL和数据线SDA构成的通信线路,利用上拉电阻将它们拉成高电平(表示总线空闲)

I2C总线可以有多个从设备,且每个从设备都有一个唯一的7bit地址物理识别,因为I2C地址全0为广播地址,所以I2C总线理论上最多能带2^7-1=127个从设备

(I2C:半双工通信的同步串行通信协议,采用电平信号,数据传输采用大端方式MSB,先发高位数据)

I2C总线通信时序:

I2C协议的起始信号(start):当SCL保持高电平时,SDA出现一个下降沿,产生起始位

I2C协议的停止信号(stop):当SCL保持高电平时,SDA出现一个上升沿,产生停止位

(停止通信后,总线空闲,处于高电平)

主设备向从设备发送从设备地址信号,在收到从设备的应答信号后通讯连接才建立成功

若未收到应答则表示寻址失败。

希望继续,则给出“应答(ACK)”信号,即SDA为低电平

不希望继续,则给出“非应答(NACK)”信号,即SDA为高电平

(建立通信后才开始传输数据位)

(读写方向位:RW,0为写操作,1为读操作)

主机发送数据流程:

1、主机在检测到总线为空闲时,发送一个启动信号"S",开始一次通信的开始

2、主机接着发送一个从设备地址,它由7bit物理地址和1bit读写控制位R/W组成(此时RW=0)(发送的地址有7位的物理地址和1位的读写方向号)

3、相对应的从机收到命令字节后向主机回馈应答信号ACK(ACK=D)

4、主机收到从机的应答信号后开始发送第一个字节的数据;

5、从机收到数据后返回一个应答信号ACK;

6、主机收到应答信号后再发送下一个数据字节;

7、主机发完最后一个字节并收到ACK后,向从机发送一个停止信号P结束本次通信并释放总线;

8、从机收到p信号后也退出与主机之间的通信;

主机接收数据流程:

1、主机发送启动信号后,接着发送地址字节(其中R/W=1) :

2、对应的从机收到地址字节后,返回一个应答信号并向主机发送数据;

3、主机收到数据后向从机反馈一个应答信号ACK:

4、从机收到应答信号后再向主机发送下一个数据:

5、当主机完成接收数据后,向从机发送一个NAK,从机收到非应答信号后便停止发送;

6、主机发送非应答信号后,再发送一个停止信号,释放总线结束通信

stm32l431rct6的i2c引脚分配(本例我们使用引脚PB6和PB7为例)

二、了解sht20

stm32l431rct6的温湿度传感器引脚分配

三、开整

记得把串口使能了(这里我使用的是串口1),如下,其他的我相信你们都配好了(ctr+s就可以直接生成代码哦)

1、在 Core/Src 下创建并编写 SHT20 温湿度传感器的驱动源文件 sht20.c

sht20.c代码如下

#include<stdio.h>#include "stm32l4xx_hal.h"#include "sht20.h"#include "tim.h"/* 通过该宏控制是使用 HAL 库里的 I2C 接口还是使用 GPIO 模拟串口的接口*/#define CONFIG_GPIO_I2C#ifdef CONFIG_GPIO_I2C#include "gpioi2c.h"#else#include "i2c.h"#endif/*采样*/int SHT20_Sample_data(uint8_t cmd, float *data){uint8_t buff[2];float sht20_data=0.0;intrv;/*主设备向发送从设备地址信号,这里sht20的写地址是0x80,成功则返回1个字节*/rv=I2C_Master_Transmit(0x80,&cmd,1); if(0!=rv){return -1;}if(cmd==0xf3){HAL_Delay(85);}else if(cmd==0xf5){HAL_Delay(29);}/*主设备向发送从设备地址信号,这里sht20的读地址是0x81,成功则返回2个字节,分别是温湿度的整数*和小数,并且数据放在buff中*/rv=I2C_Master_Receive(0x81,buff,2);if(0!=rv){return -1;}sht20_data=buff[0];sht20_data=ldexp(sht20_data,8);sht20_data+=buff[1]&0xFC;if(cmd==0xf3) //0xf3为读温度的信号{*data=(-46.85+175.72*sht20_data/65536);//计算温度的公式}else if(cmd==0xf5) //0xf5为读湿度的信号{*data=(-6.0+125.0*sht20_data/65536);//计算湿度的公式}return *data;}

2、同理,在 Core/Inc下创建并编写 SHT20 温湿度传感器的驱动头文件 sht20.h

sht20.h代码如下:

#ifndef INC_SHT20_H_#define INC_SHT20_H_#include "stm32l4xx_hal.h"#define add_w 0x80 //地址写#define add_r 0x81 //地址读#define measure_temp 0xf3 //读取温度#define measure_hum 0xf5 //读取湿度#define user_code_w 0xe6#define user_code_r 0xe7#define RST_code 0xfe//复位int SHT20_Sample_data(uint8_t cmd, float *data);#endif /* INC_SHT20_H_ */

3、同理,在 Core/Src下创建并编写GPIO 模拟 I2C 驱动源文件 gpioi2c.c

gpioi2c.c代码如下:

#include <stdio.h>#include "stm32l4xx_hal.h"#include "tim.h" /* delay_us() implement */#include "gpio.h"#include "gpioi2c.h"#define I2C_CLK_STRETCH_TIMEOUT 50#define CONFIG_GPIO_I2C_DEBUG#ifdef CONFIG_GPIO_I2C_DEBUG#define i2c_print(format,args...) printf(format, ##args)#else#define i2c_print(format,args...) do{} while(0)#endif/* GPIO Simulate I2C Bus pins */typedef struct i2c_gpio_s{GPIO_TypeDef *group;uint16_t scl; /* SCL */uint16_t sda; /* SDA */} i2c_gpio_t;static i2c_gpio_t i2c_pins = { GPIOB, GPIO_PIN_6/*SCL*/, GPIO_PIN_7/*SDA*/ };#define SDA_IN() do{ GPIO_InitTypeDef GPIO_InitStruct = {0}; \GPIO_InitStruct.Pin = i2c_pins.sda; \GPIO_InitStruct.Mode = GPIO_MODE_INPUT; \GPIO_InitStruct.Pull = GPIO_PULLUP; \GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct); \}while(0)#define SDA_OUT() do{ GPIO_InitTypeDef GPIO_InitStruct = {0}; \GPIO_InitStruct.Pin = i2c_pins.sda; \GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \GPIO_InitStruct.Pull = GPIO_PULLUP; \GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct); \}while(0)#define SCL_OUT() do{ GPIO_InitTypeDef GPIO_InitStruct = {0}; \GPIO_InitStruct.Pin = i2c_pins.scl; \GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \GPIO_InitStruct.Pull = GPIO_PULLUP; \GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \HAL_GPIO_Init(i2c_pins.group, &GPIO_InitStruct); \}while(0)#define SCL_H() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.scl, GPIO_PIN_SET)#define SCL_L() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.scl, GPIO_PIN_RESET)#define SDA_H() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.sda, GPIO_PIN_SET)#define SDA_L() HAL_GPIO_WritePin(i2c_pins.group, i2c_pins.sda, GPIO_PIN_RESET)#define READ_SDA() HAL_GPIO_ReadPin(i2c_pins.group, i2c_pins.sda)#define READ_SCL() HAL_GPIO_ReadPin(i2c_pins.group, i2c_pins.scl)static inline uint8_t I2c_WaitWhileClockStretching(uint16_t timeout){while( timeout-- > 0 ){if( READ_SCL() )break;delay_us(1);}return timeout ? NO_ERROR : BUS_ERROR;}/* StartCondition(S) */uint8_t I2c_StartCondition(){uint8_t rv = NO_ERROR;SDA_OUT();SCL_OUT();/* StartCondition(S): A high to low transition on the SDA line while SCL is high._______SCL: |________SDA: |_____*/SDA_H();delay_us(1);SCL_H();delay_us(1);#ifdef I2C_CLK_STRETCH_TIMEOUTrv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C bus busy\n", __func__);return rv;}#endifSDA_L();delay_us(2);SCL_L();delay_us(2);return rv;}/* StopCondition(P) */uint8_t I2c_StopCondition(void){uint8_t rv = NO_ERROR;SDA_OUT();/* StopCondition(P): A low to high transition on the SDA line while SCL is high._____SCL: ___|_____SDA: ______|*/SCL_L();SDA_L();delay_us(2);SCL_H();delay_us(2);#ifdef I2C_CLK_STRETCH_TIMEOUTrv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C bus busy\n", __func__);}#endifSDA_H();delay_us(2);return rv;}uint8_t I2c_WriteByte(uint8_t byte){uint8_t rv = NO_ERROR;uint8_t mask;/* Data line changes must happened when SCL is low */SDA_OUT();SCL_L();/* 1Byte=8bit, MSB send: bit[7]-->bit[0] */for(mask=0x80; mask>0; mask>>=1){if((mask & byte) == 0){SDA_L();}else{SDA_H();}delay_us(5); // data set-up time (t_SU;DAT)SCL_H();delay_us(5); // SCL high time (t_HIGH)#ifdef I2C_CLK_STRETCH_TIMEOUTrv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C bus busy\n", __func__);goto OUT;}#endifSCL_L();delay_us(5); // data hold time(t_HD;DAT)}/* clk #9 wait ACK/NAK from slave */SDA_IN();SCL_H(); // clk #9 for ackdelay_us(5); // data set-up time (t_SU;DAT)#ifdef I2C_CLK_STRETCH_TIMEOUTrv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C bus busy\n", __func__);goto OUT;}#endif/* High level means NAK */if( READ_SDA() )rv = ACK_ERROR;OUT:SCL_L();delay_us(20);return rv;}uint8_t I2c_ReadByte(uint8_t *byte, uint8_t ack){uint8_t rv = NO_ERROR;uint8_t mask;*byte = 0x00;SDA_IN();/* 1Byte=8bit, MSB send: bit[7]-->bit[0] */for(mask = 0x80; mask > 0; mask >>= 1){SCL_H(); // start clock on SCL-linedelay_us(1); // clock set-up time (t_SU;CLK)#ifdef I2C_CLK_STRETCH_TIMEOUTrv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C bus busy\n", __func__);goto OUT;}#endifif(READ_SDA())*byte |= mask; // read bitSCL_L();delay_us(2); // data hold time(t_HD;DAT)}/* clk #9 send ACK/NAK to slave */if(ack == ACK){SDA_OUT();SDA_L(); // send Acknowledge if necessary}else if( ack == NAK ){SDA_OUT();SDA_H(); // send NotAcknowledge if necessary}delay_us(1); // data set-up time (t_SU;DAT)SCL_H(); // clk #9 for ackdelay_us(2); // SCL high time (t_HIGH)#ifdef I2C_CLK_STRETCH_TIMEOUTrv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C bus busy\n", __func__);}#endifOUT:SCL_L();delay_us(2); // wait to see byte package on scopereturn rv;}uint8_t I2c_SendAddress(uint8_t addr){return I2c_WriteByte(addr);}int I2C_Master_Receive(uint8_t addr, uint8_t *buf, int len){int i;int rv = NO_ERROR;uint8_t byte;I2c_StartCondition();rv = I2c_SendAddress(addr);if( rv ){i2c_print("Send I2C read address failure, rv=%d\n", rv);goto OUT;}#ifdef I2C_CLK_STRETCH_TIMEOUT/* wait while clock streching */rv = I2c_WaitWhileClockStretching(I2C_CLK_STRETCH_TIMEOUT);if( rv ){i2c_print("ERROR: %s() I2C wait clock stretching failure, rv=%d\n", __func__, rv);return rv;}#endiffor (i=0; i<len; i++){if( !I2c_ReadByte(&byte, ACK) ){buf[i] = byte;}elsegoto OUT;}OUT:I2c_StopCondition();return rv;}int I2C_Master_Transmit(uint8_t addr, uint8_t *data, int bytes){int i;int rv = NO_ERROR;if(!data){return PARM_ERROR;}i2c_print("I2C Mastr start transimit [%d] bytes data to addr [0x%02x]\n", bytes, addr);I2c_StartCondition();rv = I2c_SendAddress(addr);if( rv ){goto OUT;}for (i=0; i<bytes; i++){if( NO_ERROR != (rv=I2c_WriteByte(data[i])) ){break;}}OUT:I2c_StopCondition();return rv;}

4、同理,在 Core/Inc下创建并编写GPIO 模拟 I2C 头文件 gpioi2c.h

gpioi2c.h代码如下:

enum{NO_ERROR = 0x00, // no errorPARM_ERROR = 0x01, // parameter out of range errorACK_ERROR = 0x02, // no acknowledgment errorCHECKSUM_ERROR = 0x04, // checksum mismatch errorTIMEOUT_ERROR = 0x08, // timeout errorBUS_ERROR = 0x10, // bus busy};enum{ACK_NONE, /* Without ACK/NAK Reply */ACK, /* Reply with ACK */NAK, /* Reply with NAK */};extern int I2C_Master_Receive(uint8_t addr, uint8_t *buf, int len);extern int I2C_Master_Transmit(uint8_t addr, uint8_t *data, int bytes);

5、修改main.c中的代码,找到下列相应位置,添加如图代码即可

编写好之后,点击运行代码
将程序烧录至开发板
串口调试助手连接板子,在板子上按复位键就可以看到打印信息了,如下:

如果觉得《STM32 GPIO模拟i2c通信实现sht20的温湿度采样 并以JSON格式上报(串口调试助手为例)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。