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打印到上位机。经测试可完美工作,有图为证:
图1. 串口助手收到的信息
总结:
为了实现上述功能,先花一周,系统学习了STM32 Reference manual上的I2C interface;仔细读了SHT35的Data sheet;读了芯片的官方的例程,这个是直接操作寄存器实现的,虽然最后没选用,但对初学者理解I2C协议非常有帮助。
SHT35功能还是很强大的,精度很高,芯片非常小,可选设置比较多。诸如超限报警,阈值设置,周期性测量,内置加热器控制等功能会逐步添加,并逐步实现在上位机设置这些功能。
这是自学STM32以来第一个可以work的硬件控制练习。虽是坐井观天,但自我感觉收获良多。子曰:路漫漫其修远兮,不积跬步无以至千里,……