快捷搜索:  汽车  科技

sht11温湿度传感器图(I2C通信实例-SHTx温湿度传感器)

sht11温湿度传感器图(I2C通信实例-SHTx温湿度传感器)可以看到,SHT35_ReadData函数中并无写入指令。这是因为不同测量模式对应不同的指令,为了提高代码的通用性,发指令的任务被提到了上一层的函数中。以带时钟延长单次测量为例,上层函数如下:void SHT35_ReadData(float *temperature float *humidity) { uint8_t dataBuff[6] = {0U}; uint16_t rawData; uint8_t checksum crcbytes[2]; etError error; if (HAL_I2C_Master_Receive( &hi2c2 SHT35_ADDR_READ dataBuff 6 0xFFFF) !=HAL_OK) { Error_Handler(); } crcBytes[0] = dataBuff[0]; crcByt

前文回顾:
  • I2C通信实例 - SHTx温湿度传感器 01
  • I2C通信实例 - SHTx温湿度传感器 02

现在我们来编写驱动代码。

SHT35的生产厂商提供了一个驱动程序样本代码。可以到sensirion官网,搜索SHT35,然后在芯片说明页面的Technical Dowloads部分,可以找到该Sample code。这个例程代码是基于操作寄存器来实现的,而且没有利用MCU的I2C接口,通信指令都是根据I2C的通讯协议,通过操控SDA和SCL引脚的电平,逐位发送的。好像是为没有I2C接口的MCU开发的。读一下这些代码对深入理解I2C通讯很有帮助。用HAL库开发时,集成这种代码进去可能会导致以后维护起来非常麻烦。这里我们根据前述工作原理,利用MCU自带的I2C接口,基于HAL库来重新实现。不用担心网上传言的HAL库效率低之类的评价,任何API都是对寄存器操作的封装,真遇到了效率瓶颈,可以照着库函数中的寄存器操作实现一遍,删繁就简,程序没问题后去掉那些无用的验证,把通用版整成专用版就可以啦。

1. 定义指令别名

为了提高程序的可读性,需要为控制指令定义别名。大部分可以从官方例程包中的shtx.h中复制,缺少的指令模仿着添加上即可。在前面创建的sht35.h中定义如下枚举类:

// Sensor Commands typedef enum { CMD_READ_SERIALNBR = 0x3780 // read serial number CMD_READ_STATUS = 0xF32D // read status register CMD_CLEAR_STATUS = 0x3041 // clear status register CMD_HEATER_ENABLE = 0x306D // enabled heater CMD_HEATER_DISABLE = 0x3066 // disable heater CMD_SOFT_RESET = 0x30A2 // soft reset // Single shot mode CMD_MEAS_CLOCKSTR_H = 0x2C06 // measurement: clock stretching high repeatability CMD_MEAS_CLOCKSTR_M = 0x2C0D // measurement: clock stretching medium repeatability CMD_MEAS_CLOCKSTR_L = 0x2C10 // measurement: clock stretching low repeatability CMD_MEAS_POLLING_H = 0x2400 // measurement: polling high repeatability CMD_MEAS_POLLING_M = 0x240B // measurement: polling medium repeatability CMD_MEAS_POLLING_L = 0x2416 // measurement: polling low repeatability // Periodic Data Acquisition mode CMD_MEAS_PERI_05_H = 0x2032 // measurement: periodic 0.5 mps high repeatability CMD_MEAS_PERI_05_M = 0x2024 // measurement: periodic 0.5 mps medium repeatability CMD_MEAS_PERI_05_L = 0x202F // measurement: periodic 0.5 mps low repeatability CMD_MEAS_PERI_1_H = 0x2130 // measurement: periodic 1 mps high repeatability CMD_MEAS_PERI_1_M = 0x2126 // measurement: periodic 1 mps medium repeatability CMD_MEAS_PERI_1_L = 0x212D // measurement: periodic 1 mps low repeatability CMD_MEAS_PERI_2_H = 0x2236 // measurement: periodic 2 mps high repeatability CMD_MEAS_PERI_2_M = 0x2220 // measurement: periodic 2 mps medium repeatability CMD_MEAS_PERI_2_L = 0x222B // measurement: periodic 2 mps low repeatability CMD_MEAS_PERI_4_H = 0x2334 // measurement: periodic 4 mps high repeatability CMD_MEAS_PERI_4_M = 0x2322 // measurement: periodic 4 mps medium repeatability CMD_MEAS_PERI_4_L = 0x2329 // measurement: periodic 4 mps low repeatability CMD_MEAS_PERI_10_H = 0x2737 // measurement: periodic 10 mps high repeatability CMD_MEAS_PERI_10_M = 0x2721 // measurement: periodic 10 mps medium repeatability CMD_MEAS_PERI_10_L = 0x272A // measurement: periodic 10 mps low repeatability CMD_FETCH_DATA = 0xE000 // readout measurements for periodic mode CMD_BREAK_PERI = 0x3093 // stop the periodic data acquisition CMD_MEAS_ART = 0x2B32 // accelerated response time - boost the acquisition to 4Hz CMD_R_AL_LIM_LS = 0xE102 // read alert limits low set CMD_R_AL_LIM_LC = 0xE109 // read alert limits low clear CMD_R_AL_LIM_HS = 0xE11F // read alert limits high set CMD_R_AL_LIM_HC = 0xE114 // read alert limits high clear CMD_W_AL_LIM_HS = 0x611D // write alert limits high set CMD_W_AL_LIM_HC = 0x6116 // write alert limits high clear CMD_W_AL_LIM_LC = 0x610B // write alert limits low clear CMD_W_AL_LIM_LS = 0x6100 // write alert limits low set CMD_NO_SLEEP = 0x303E } SHT35_CMD; // Measurement Mode typedef enum { MODE_CLKSTRETCH // clock stretching MODE_POLLING // polling } etMode; // Frequency Choice typedef enum { FREQUENCY_HZ5 // 0.5 measurements per seconds FREQUENCY_1HZ // 1.0 measurements per seconds FREQUENCY_2HZ // 2.0 measurements per seconds FREQUENCY_4HZ // 4.0 measurements per seconds FREQUENCY_10HZ // 10.0 measurements per seconds } etFrequency;

其中SHT35_CMD中为指令定义了别名,etMode定义的是测量模式名称,etFrequency定义的是周期性测量模式下的测量频率选项名称。目前有这三个枚举类就足够了。

2. 编写读写函数

根据前文所述原理,我们需要通过I2C接口向传感器发送指令、读取数据。在sht35.c中定义如下宏和函数:

#define SHT35_ADDR_WRITE 0x44<<1 //1000 1000 #define SHT35_ADDR_READ (0x44<<1)|0x01 //1000 1001,根据用户手册的指示,这个就是SHT30的读取地址 // Generator polynomial for CRC #define POLYNOMIAL 0x131 // P(x) = x^8 x^5 x^4 1 = 100110001 //----------------------------------------------------------------------------- static void SHT35_WriteCommand(SHT35_CMD cmd) { uint8_t cmd_buffer[2]; cmd_buffer[0] = cmd>>8; //MSB cmd_buffer[1] = cmd; //LSB if (HAL_I2C_Master_Transmit( &hi2c2 SHT35_ADDR_WRITE (uint8_t*) cmd_buffer 2 0xFFFF)!=HAL_OK) { Error_Handler(); } }

sht35.c中首先需要定义三个宏。分别是写入地址,读取地址和CRC多项式,这些产品手册上都有说明。因为ADDR接了地,所以默认地址是0x44。函数代码很简单,先把指令的高位和低位分别存入一个缓存数组中,然后调用HAL库的发送函数,通过句柄&hi2c2指向的I2C主机将两个字节发送到指定地址。之所以这么处理,是因为指令是16-bit的,而I2C发送时是按字节发送的。注意,写入时地址要用SHT35_ADDR_WRITE。SHT35_WriteCommand是一个通用函数,可以写入传感器支持的所有指令。但这个函数不需要被外部调用,因此声明为了static。

再来看读取数据的函数:

void SHT35_ReadData(float *temperature float *humidity) { uint8_t dataBuff[6] = {0U}; uint16_t rawData; uint8_t checksum crcbytes[2]; etError error; if (HAL_I2C_Master_Receive( &hi2c2 SHT35_ADDR_READ dataBuff 6 0xFFFF) !=HAL_OK) { Error_Handler(); } crcBytes[0] = dataBuff[0]; crcBytes[1] = dataBuff[1]; checksum = dataBuff[3]; error = SHT35_CheckCrc(crcBytes 2 checksum); crcBytes[0] = dataBuff[3]; crcBytes[1] = dataBuff[4]; checksum = dataBuff[5]; error = SHT35_CheckCrc(crcBytes 2 checksum); if (error!=NO_ERROR) { Error_Handler(); } //Convert to float rawData = dataBuff[0]<<8|dataBuff[1]; *temperature = SHT35_CalcTemperature(rawData); rawData = dataBuff[3]<<8|dataBuff[4]; *humidity = SHT35_CalcHumidity(rawData); }

代码也很简单。每次读取要返回6个字节:前2个字节为温度原始数据,第3个为温度数据的CRC,第4-5个字节为湿度原始数据,第6个字节为湿度数据的CRC。函数中首先将原始数据分开,进行CRC校验,最后转化为浮点格式的温度和湿度。

可以看到,SHT35_ReadData函数中并无写入指令。这是因为不同测量模式对应不同的指令,为了提高代码的通用性,发指令的任务被提到了上一层的函数中。以带时钟延长单次测量为例,上层函数如下:

void SHT35_GetTempAndHumiClkStretch(float *temperature float *humidity etRepeatability repeatability uint8_t timeout) { switch (repeatability) { case REPEATAB_LOW: SHT35_WriteCommand(CMD_MEAS_CLOCKSTR_L); break; case REPEATAB_MEDIUM: SHT35_WriteCommand(CMD_MEAS_CLOCKSTR_M); break; case REPEATAB_HIGH: SHT35_WriteCommand(CMD_MEAS_CLOCKSTR_H); break; default: printf("Undefined repeatability!\r\n"); break; } SHT35_ReadData(temperature humidity); }

这个函数根据可靠性高中低,分三种情况发出对应测量指令,最后调用SHT35_ReadData读取数据。对禁用时钟延长的情况,可以写一个类似的函数,再用一个上层函数包装这两种情况。以此类推。由于前面设置的是启用时钟延长,这里暂不考虑其他情况。

3. CRC校验函数

CRC校验是提高通信可靠性的一种方式。发送和接收端按相同的公式,对字节逐位串行计算CRC码,接收端对收到的CRC与本地计算的比对,如果不一致则说明数据传输出错。这个CRC校验不是非有不可的,可按需取舍。校验函数代码如下:

//----------------------------------------------------------------------------- static SHT35_CheckCrc(uint8_t data[] uint8_t nbrOfBytes uint8_t checksum) { uint8_t crc; // calculated checksum // calculates 8-Bit checksum crc = SHT35_CalcCrc(data nbrOfBytes); // verify checksum if (crc!=checksum){ Error_Handler(); } } //----------------------------------------------------------------------------- static uint8_t SHT35_CalcCrc(uint8_t data[] uint8_t nbrOfBytes) { uint8_t bit; // bit mask uint8_t crc = 0xFF; // calculated checksum uint8_t byteCtr; // byte counter // calculates 8-Bit checksum with given polynomial for (byteCtr = 0; byteCtr<nbrOfBytes; byteCtr ) { crc ^= (data[byteCtr]); for (bit = 8; bit>0; --bit) { if (crc&0x80) crc = (crc<<1)^POLYNOMIAL; else crc = (crc<<1); } } return crc; }

这两个函数是直接从官方例程copy过来的。可完美工作。

4. 功能的实现

由于选用是单次测量模式,我们通过按键触发来调用并返回结果。在HAL_GPIO_EXTI_Callback函数中,根据按键动作,执行如下代码:

............. case GPIO_PIN_4: /* KEY0 pressed - PE4 */ SHT35_GetTempAndHumiClkStretch(&Tm &RH REPEATAB_HIGH 0xFF); printf("SHT35 T = %2.2f degC.\r\n" Tm); printf("SHT35 RH = %2.2f %%.\r\n\r\n" RH); break;

先Get数据,然后分成整数部分和小数部分,调用printf打印到上位机。经测试可完美工作,有图为证:

sht11温湿度传感器图(I2C通信实例-SHTx温湿度传感器)(1)

图1. 串口助手收到的信息


总结:

为了实现上述功能,先花一周,系统学习了STM32 Reference manual上的I2C interface;仔细读了SHT35的Data sheet;读了芯片的官方的例程,这个是直接操作寄存器实现的,虽然最后没选用,但对初学者理解I2C协议非常有帮助。

SHT35功能还是很强大的,精度很高,芯片非常小,可选设置比较多。诸如超限报警,阈值设置,周期性测量,内置加热器控制等功能会逐步添加,并逐步实现在上位机设置这些功能。

这是自学STM32以来第一个可以work的硬件控制练习。虽是坐井观天,但自我感觉收获良多。子曰:路漫漫其修远兮,不积跬步无以至千里,……

猜您喜欢: