温湿度检测是嵌入式编程中经常应用到的一项功能。在我们的产品中亦经常使用。SHT2x系列温湿度传感器作为一种高精度低成本的集成模块,一直应用于我们的产品中。在这里我们讨论如何封装SHT2x系列温湿度传感器的驱动。
SHT20配有一个全新设计的CMOSens芯片、一个经过改进的电容式湿度传感元件和一个标准的能隙温度传感元件,其性能已经大大提升甚至超出了前一代传感器(SHT1x和SHT7x)的可靠性水平。
SHT2x湿度和温度传感器采用了新的封装模式和数字接口,每一个传感器都经过校准和测试。在产品表面印有产品批号,同时在芯片内存储了电子识别码-可以通过输入命令读出这些识别码。此外,SHT2x温湿度传感器的分辨率可以通过输入命令进行改变(8/12bit乃至12/14bit的RH/T),传感器可以检测到电池低电量 状态,并且输出校验和,有助于提高通信的可靠性。SHT2x温湿度传感器的封装及引脚定义如下图:
SHT2x 温湿度传感器的供电范围为 2.1-3.6V,推荐电压为 3.0V。电源(VDD)和接地(VSS)之间须连接一个100nF的去耦电容,且电容的位置应尽可能靠近传感器。数字接口为标准I2C总线。接线方式如下图:
SHT2x温湿度传感器采用标准的 I2C协议进行通讯。在启动传输后,随后传输的I2C首字节包括 7位的I2C设备地址(B-范例地址“1000’000”)和一个SDA方向位(读 R:“1”,写 W:“0”)。在第 8个SCL时钟下降沿之后,通过拉低 SDA引脚(ACK位),指示传感器数据接收正常。在发出测量命令之后(“1110’0011”代表温度测量,“1110’0101”代表相对湿度测量),MCU必须等待测量完成。基本的命令列表如下:
从上表我们知道,读取SHT2x温湿度传感器数据有两种不同的方式可选,主机模式或非主机模式。在主机模式下,在测量的过程中, SCL线被封锁(由 传感器进行控制),在非主机模式下,当传 感器在执行测量任务时,SCL线仍然保持开 放状态,可进行其他通讯。非主机模式允许 传感器进行测量时在总线上处理其他 I2C总 线通讯任务。
传感器内部设置的默认分辨率为相对湿度12位和温度14位。SDA的输出数据被转换成两 个字节的数据包,高字节MSB在前(左对齐)。每个字节后面都跟随一个应答位。两个状态位,即LSB的后两位在进行物理计算前须置“0”。
不论基于哪种分辨率,相对湿度RH都可以根据SDA输出的相对湿度信号 SRH通过如下公式计算获得(结果以%RH表示)。
以上所给出的 RH物理值对应于世界气象组织(WMO)所规定的基于液态水的相对湿度。基于冰的相对湿度RHi可通过特定温度t下基于水的 相对湿度 RHw转换而来,同样参照应用说明
相对湿度单位为 %RH温度单位为°C for temperature. 相关系数如下:
不论基于哪种分辨率,温度 T都可以通过将温度输 出信号 ST代入到下面的公式计算得到(结果以温 度°C表示):
我们已经了解了SHT2x系列温湿度传感器基本技术特性,接下来我们进一步考虑如何设计并实现SHT2x系列温湿度传感器的驱动。
在使用一个对象之前我们需要获得一个对象。同样的我们想要SHT2x系列温湿度传感器就需要先定义SHT2x系列温湿度传感器的对象。
我们要得到SHT2x系列温湿度传感器对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下SHT2x系列温湿度传感器的对象。
先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑SHT2x系列温湿度传感器对象属性。首先对于I2C设备会有一个地址,用于表示总线上不同的通讯节点,所以我们将设备地址作为SHT2x系列温湿度传感器对象的一个属性。用户寄存器标识了设备的配置状态,所以我们也将其作为SHT2x系列温湿度传感器对象的一个属性。同样的每个SHT2X都有一个电子识别码,用以标识SHT2X设备的身份,所以我们也将其作为SHT2x系列温湿度传感器对象的一个属性。此外我们也将读出来的温湿度的数字编码所以我们也将其作为SHT2x系列温湿度传感器对象的属性以记录当前状态。
接着我们还需要考虑SHT2x系列温湿度传感器对象的操作问题。通过I2C总线发送数据或者接收数据均依赖于具体的操作平台,所以我们将向SHT2x发送数据以及从SHT2x获取数据均作为SHT2x系列温湿度传感器对象的操作。此外,在驱动中会用到延时操作函数,延时操作一般也会依赖于具体平台的实现,所以我们也将其作为SHT2x系列温湿度传感器对象的操作。
根据上述我们对SHT2x温湿度传感器的分析,我们可以定义SHT2x温湿度传感器的对象类型如下:
/*定义SHT2x对象*/
typedef struct SHT2xObject{
uint8_t userReg; //用户寄存器
uint8_t devAddress; //设备地址
uint8_t sn[8]; //设备序列号
uint16_t tempCode; //温度的数值码
uint16_t humiCode; //湿度的数值码
void (*Delayms)(volatile uint32_t nTime); //毫秒延时操作指针
void (*Transmit)(struct SHT2xObject *sht,uint8_t *tData,uint16_t tSize); //发送数据操作指针
void (*Receive)(struct SHT2xObject *sht,uint8_t *rData,uint16_t rSize); //接收数据操作指针
}SHT2xObjectType;
我们知道,一个对象仅作声明是不能使用的,我们需要先对其进行初始化,所以这里我们来考虑SHT2x系列温湿度传感器对象的初始化函数。一般来说,初始化函数需要处理几个方面的问题。一是检查输入参数是否合理;二是为对象的属性赋初值;三是对对象作必要的初始化配置。据此我们设计SHT2x系列温湿度传感器对象的初始化函数如下:
/* 初始化配置SHT2x */
void SHT2xInitialization(SHT2xObjectType *sht,
SHT2xDPIType dpi,
SHT2xBatteryType endBat,
SHT2xHeaterType heater,
SHT2xOTPType otp,
SHT2xTransmit write,
SHT2xReceive read,
SHT2xDelayms delayms)
{
uint8_t userReg=0;
uint8_t dpiSet[4]={DPI_RH12_T14,DPI_RH8_T13,DPI_RH10_T12,DPI_RH11_T11};
uint8_t batEnd[2]={END_OF_BATTERY_H,END_OF_BATTERY_L};
uint8_t heaterSet[2]={ONCHIPHEATERDISABLE,ONCHIPHEATERENABLE};
uint8_t otpSet[2]={OTPENABLE,OTPDISABLE};
if((sht==NULL)||(write==NULL)||(read==NULL)||(delayms==NULL))
{
return;
}
sht->Transmit=write;
sht->Receive=read;
sht->Delayms=delayms;
sht->devAddress=SHT2X_ADDRESS;
sht->tempCode=0;
sht->humiCode=0;
userReg=dpiSet[dpi]|batEnd[endBat]|heaterSet[heater]|otpSet[otp];
SetSHT2xUserRegister(sht,userReg); //配置用户寄存器
sht->Delayms(20);
GetSHT2xUserRegister(sht); //读取用户寄存器
GetSHT2xSerialNumber(sht); //获取电子识别码
}
我们已经完成了SHT2x系列温湿度传感器对象类型的定义和对象初始化函数的设计。但我们的主要目标是获取对象的信息,接下来我们还要实现面向SHT2x温湿度传感器的各类操作。
软件复位命令用于在无需关闭和再次打开电源的情况下,重新启动传感器系统。在接收到这个命令之后,传感器系统开始重新初始化,并恢复默认设置状态,用户寄存器的加热器位除外。软复位所需时间不超过15毫秒。命令格式如下:
/*软件复位*/
void SoftResetSHT2x(SHT2xObjectType *sht)
{
uint8_t tData=SOFT_RESET;
sht->Transmit(sht,&tData,1);
}
用户寄存器用于配置SHT2X的工作特性,包括测量分辨率、电池电压、OTP加载和片内加热器配置等。用户寄存器的结构及各位的定义如下:
电池电量不足警报在电源电压下降到 2.25V 以下时激活。内部加热器用于传感器功能性诊断,温度升高时相对湿度降低。OTP重加载为一个安全功能,可以在每次测量前将整个OTP设置加载到寄存器,加热器位除外。SHT2X中此功能默认为禁止状态,且不推荐用户使用。请采用软复位代替它包含OTP重加载。
/*配置用户寄存器*/
static void SetSHT2xUserRegister(SHT2xObjectType *sht,uint8_t cmd)
{
uint8_t status;
uint8_t command;
uint8_t pData[2];
GetSHT2xUserRegister(sht);
status=sht->userReg&0x38;
command=cmd&0xC7;
command=command|status;
if(command!=sht->userReg)
{
pData[0]=WRITE_USER_REGISTER;
pData[1]=command;
sht->Transmit(sht,pData,2);
sht->Delayms(10);
GetSHT2xUserRegister(sht);
}
}
在配置用户寄存器时,需注意不得变更预留位且相关的预留位的默认值以后可能会改变。因此,在进行任何写寄存器的操作之前,必须先读预留位的默认值。之后,用户寄存器字节由对应的预留位的默认值和其他剩余位的默认值或者写入值组成。
SHT2x温湿度传感器数据获取有主机模式或非主机模式两种。我们在这里先看看非主机模式,在非主机模式下,MCU需要对传感器状态进行查询。此过程通过发送一个启动传输时序,之后紧接着是如下图所示的I2C首字节(1000’0001)来完成。如果内部处理工作完成,单片机查询到传感器发出的确认信号后,相关数据就可以通过MCU进行读取。如果测量处理工作没有完成,传感器无确认位(ACK)输出,此时必须重新发送启动传输时序。
根据上述描述和时序图我们可以得到获取数据的函数如下:
/*读取SHT2x的湿度数据*/
float GetSHT2xHumidityValue(SHT2xObjectType *sht,uint8_t cmd)
{
uint8_t data[3]={0,0,0};
uint16_t result=0;
/*设置转换命令*/
sht->Transmit(sht,&cmd,1);
sht->Delayms(29);
/*读取数据*/
sht->Receive(sht,data,3);
if(CheckCRC8ForSHT2x(data,2,data[2]))
{
result=(uint16_t)(data[0]);
result=(result<<8)+(uint16_t)(data[1]);
}
sht->humiCode=result;
return CalcSHT2xHumidity(result);
}
我们已经设计并实现了SHT2x温湿度传感器驱动,接下来我们还需要对这一驱动进行验证,所以我们要基于此驱动设计一个简单的应用。
使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的SHT2X温湿度传感器对象类型声明一个SHT2X温湿度传感器对象变量,具体操作格式如下:
SHT2xObjectType sht2x;
声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:
SHT2xObjectType *sht,SHT2X对象变量
SHT2xDPIType dpi,测量分辨率配置
SHT2xBatteryType endBat,电池结束状态配置
SHT2xHeaterType heater,加热器是否启用配置
SHT2xOTPType otp,是否加载OTP配置
SHT2xTransmit write,写操作指针
SHT2xReceive read,读操作指针
SHT2xDelayms delayms,毫秒延时指针
对于这些参数,对象变量我们已经定义了。所使用的测量分辨率配置、电池结束状态配置、加热器是否启用配置以及是否加载OTP配置均为枚举,根据实际情况选择就好了。主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型如下:
/* 毫秒延时函数指针类型 */
typedef void (*SHT2xDelayms)(volatile uint32_t nTime);
/* 发送数据函数指针类型 */
typedef void (*SHT2xTransmit)(struct SHT2xObject *sht,uint8_t *tData,uint16_t tSize);
/* 接收数据函数指针类型 */
typedef void (*SHT2xReceive)(struct SHT2xObject *sht,uint8_t *rData,uint16_t rSize);
对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。所以我们定义在STM32平台下使用硬件接口I2C收发器的函数。具体函数定义如下:
/*从SHT20接收数据*/
static void ReceiveFromSHT20(SHT2xObjectType *sht,uint8_t *rData,uint16_t rSize)
{
HAL_I2C_Master_Receive(&sht2xi2c, sht->devAddress,rData, rSize, 1000);
}
/*向SHT20传送数据*/
static void TransmitToSHT20(SHT2xObjectType *sht,uint8_t *tData,uint16_t tSize)
{
HAL_I2C_Master_Transmit(&sht2xi2c,sht->devAddress,tData,tSize,1000);
}
对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下:
SHT2xInitialization(&sht2x,SHT2x_DPI_RH12_T14,SHT2x_End_High,SHT2xHEATERDISABLE,SHT2xOTPDISABLE,TransmitToSHT20,ReceiveFromSHT20,HAL_Delay);
在这里我们将SHT2x温湿度控制器对象变量初始化为:最高分辨率,在大于2.25V结束电池状态,不加载OTP,不使用加热器。
我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为相应的物理量值,接下来我们使用这一驱动开发我们的应用实例。
/* 获取SHT2x温适度数据 */
void GetSHT2xMeasureData(void)
{
float humidity;
float temperature;
humidity=GetSHT2xHumidityValue(&sht2x,MEASURE_RH_COMMAND_NOHOST);
temperature=GetSHT2xTemperatureValue(&sht2x,MEASURE_T_COMMAND_NOHOST);
}
这里我们设计了一个简单的应用,直接获取SHT2x温湿度传感器测量到的温湿度数据。
因为SHT2x温湿度传感器采用的是标准I2C接口,所以在驱动设计中没有考虑硬件接口相关的内容。我们只专注于SHT2x温湿度传感器的配置与操作。所以在使用驱动程序时,无论是使用硬件I2C收发控制器还是使用GPIO模拟的软件收发控制都需要单独做相应的配置。
我们已经设计并实现了SHT2x温湿度传感器的驱动程序,也使用这一驱动程序实现了读取SHT2x温湿度传感器温度、湿度数据的简单应用。经我们测试所得到的结果是符合我们期望的,这也说明驱动程序的设计是成功的。
在使用驱动时还需注意,在做初始化配置时,对用户寄存器的写操作需注意不得变更预留位且相关的预留位的默认值以后可能会改变。因此,在进行任何写用户寄存器的操作之前,必须先读预留位的默认值。而且在初始化配置时,还要注意SHT2X 中OTP功能默认为禁止状态,且不推荐用户使用。