SHT3X是一款优秀的温湿度计。他采用i2c接口,地址为0x44。
他的驱动与原理的讲解非常的丰富,网上也有非多的现成的代码,我这里不做过多的阐述。
本篇的主要特点是在前面OLED驱动的前提下,对sht31进行面向对象的编程方式进行讲解。
【前提】
在OLED驱动之中,我已经初始化了I2C的总线,所以有总线驱动在本篇没有做,如果是使用spi或者其他的方式进行数据展示,那么需要加上对i2c总线进行初始化。
【实现步骤】
1、创建sensor.h,在其中创建结构体
typedef struct SensorDevice { char *name; uint8_t slaveAddr; //从机地址 float Temp; float Hum; unsigned short read_status; int (*Init)(void); /* 硬件初始化 */ void (*Read)(struct SensorDevice *ptDev); //读取温湿度 } SensorDevice, *PSensorDevice;
2、创建drv_sensor.c
首先实例化
static SensorDevice gSht31Dev = { .name = "SHT31", .slaveAddr = 0x44, //.Init = Sht3xDrvGpioInit, .Temp = 0, .Hum = 0, .read_status = 0, .Read = sht3x_read, }; struct SensorDevice *SHT31GetDevice(void) { return &gSht31Dev; }
接下来编写发送、接收超时函数
static void I2C2WaitTxCplt(void) { uint16_t wTimeOut = 1; while(!gI2C2TxCplt && wTimeOut) { HAL_Delay(1); wTimeOut--; } gI2C2TxCplt = false; } static void I2C2WaitRxCplt(void) { uint16_t wTimeOut = 10; while(!gI2C2RxCplt && wTimeOut) { HAL_Delay(1); wTimeOut--; } gI2C2RxCplt = false; }
为了验证读取的温度与数据是否正确,编写CRC函数如下:
/* * @name CRC_8 * @brief CRC-8校验 * @param Crc_ptr -> 校验数据首地址 LEN -> 校验数据长度 * @retval CRC_Value -> 校验值 */ static uint8_t CRC_8(uint8_t *Crc_ptr,uint8_t LEN) { uint8_t CRC_Value = 0xFF; uint8_t i = 0,j = 0; for(i=0;i<LEN;i++) { CRC_Value ^= *(Crc_ptr+i); for(j=0;j<8;j++) { if(CRC_Value & 0x80) CRC_Value = (CRC_Value << 1) ^ 0x31; else CRC_Value = (CRC_Value << 1); } } return CRC_Value; }
在读取温度的函数中首先向sht31发送开始转换命令0x2400,我将他封装在一个数据中,使用R_SAU_I2C_Write进行一次性发送。当然由于总线上有SSD1306操作时他的g_sau_i2c_master_ctrl的从机地址是SSD1306的0x3c所以,需要先配置sht31的地址,他的发送数据转换命令如下:
g_sau_i2c_master_ctrl.slave = ptDev->slaveAddr; err = R_SAU_I2C_Write(&g_sau_i2c_master_ctrl,cmd, 2, true); I2C2WaitTxCplt(); if(FSP_SUCCESS != err ) { ptDev->read_status = 0; printf("Error when open i2c1 device!\r\n"); return ; }
由于转换需要一段时间,需要延时。
延时之后,我一次读取6位数据,如果获取数据成功,则进行CRC并进行float转换,转换成功后放到结构体中,并更新状态:整个获取数据的代码如下:
static void sht3x_read(struct SensorDevice *ptDev) { if(NULL == ptDev) return; fsp_err_t err; uint32_t temp_uint; //写入开始转换的命令 //0xE000是向SHT30取数据的指令,主机发送该指令后开始读取SHT30的温湿度数据 uint8_t temp_array[6] = {0}; uint8_t cmd[] = { 0x24,0x00 }; g_sau_i2c_master_ctrl.slave = ptDev->slaveAddr; err = R_SAU_I2C_Write(&g_sau_i2c_master_ctrl,cmd, 2, true); I2C2WaitTxCplt(); if(FSP_SUCCESS != err ) { ptDev->read_status = 0; printf("Error when open i2c1 device!\r\n"); return ; } HAL_Delay(2); err = R_SAU_I2C_Read(&g_sau_i2c_master_ctrl, temp_array, 6, true); I2C2WaitRxCplt(); if(FSP_SUCCESS != err ) { ptDev->read_status = 0; printf("Error when open i2c1 device!\r\n"); return ; } else { //计算温度 if(CRC_8(temp_array,2) == temp_array[2]) //进行CRC-8校验 { temp_uint = temp_array[0]*256+temp_array[1]; //取出16位的温度值 ptDev->Temp = ((float)temp_uint)*0.267032-4500; //根据手册公式计算,为了精度,计算数值先*100 ptDev->Temp = ptDev->Temp*0.01; //再除以100,得到正常温度值 } //计算湿度 if(CRC_8(&temp_array[3],2) == temp_array[5]) //进行CRC-8校验 { temp_uint = temp_array[3]*256+temp_array[4]; //取出16位的湿度值 ptDev->Hum = ((float)temp_uint)*0.152590; //根据手册公式计算 ptDev->Hum = (uint8_t)(ptDev->Hum*0.01); //除以100,得到正常湿度值 ptDev->read_status = 1; } } }
【数据展示】
在主函数中编写测试代码如下,如果获取到温度数据则显示温湿度,如果获取出错则显示EEROR,其代码如下:
void led_blink(void) { UartDevicesRegister(); DisplayDevice *ptDispDev = OLEDGetDevice(); if(NULL == ptDispDev ) { printf("Failed to get OLED Display Device!\r\n"); return; } uint8_t cnt; ptDispDev->Init(ptDispDev); ptDispDev->GUI_Set_Horizontal_Mode(ptDispDev,0,128,0,64); SensorDevice *ptSHT31Dev = SHT31GetDevice(); if(NULL == ptSHT31Dev) { printf("Failed to get Sht31 Device!\r\n"); return; } while(1) { ptSHT31Dev->Read(ptSHT31Dev); if(cnt>96) cnt = 0; ptDispDev->GUI_clear(ptDispDev,0); if(1 == ptSHT31Dev->read_status) { ptDispDev->GUI_ShowString(ptDispDev,10,16, (uint8_t *)"TEMP:",16,1); ptDispDev->GUI_ShowNum(ptDispDev,64,16,(int)ptSHT31Dev->Temp,2,16,1); ptDispDev->GUI_ShowString(ptDispDev,16,32, (uint8_t *)"HUM:",16,1); ptDispDev->GUI_ShowNum(ptDispDev,64,32,(int)ptSHT31Dev->Hum,2,16,1); } else { ptDispDev->GUI_ShowString(ptDispDev,10,16, (uint8_t *)"TEMP:ERROR",16,1); } ptDispDev->GUI_DrawLine(ptDispDev,0,0,cnt,0,1); ptDispDev->GUI_Display(ptDispDev); //HAL_Delay(1); cnt ++; } }
【实验效果】
【总结】
使用面向对象对sht31的驱动进行封装,在瑞萨的FSP驱动中,可以快速实现驱动,同时只需要提供驱动的接口,可以实现低耦合的功能。