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驱动中,可以快速实现驱动,同时只需要提供驱动的接口,可以实现低耦合的功能。
我要赚赏金
