背景
项目实际使用SHT3x进行温湿度测量,主控芯片采用STM8S003F3P6,并且使用模拟IIC接口的硬件连接。
原理图
如下图所示,使用STM8S003F3P6管脚PB4/PB5进行SHT3x数据接口
SHT3x-DIS是Sensirion新一代的温湿度传感器,精度为±2%RH和±0.3℃,输入电压范围从2.4V到5.5V,采用IIC总线接口,速率可达1MHz。测量温湿度范围分别为是-40℃ ~ 125℃和0 ~ 100%。
从下图可以看到SHT3x内部集成了湿度传感器和温度传感器,通过ADC采样输入到数据处理和线性化单元,同时带有校正储存器,处理环境对器件测量的影响。通过数字接口IIC读取数据。带警报引脚,可通过修改寄存器的值设定阈值,当测量的温湿度超过阈值时它会被置位。
软件设计
STM8S003F3P6基本配置如下
时钟配置,这里必须要提一下时钟配置,如果不提时钟配置,对SHT3x模拟IIC接口的时序延时
就不好判定
/************************************************
函数名称 : CLK_Configuration
功 能 : 时钟配置
参 数 : 无
返 回 值 : 无
作 者 : strongerHuang
*************************************************/
void CLK_Configuration(void)
{
/*
ErrorStatus clk_return_status;
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV8); //HSI = 16M (8分频)=2MHZ
//切换内部低速时钟128khz
clk_return_status = CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_LSI, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
if (clk_return_status == SUCCESS) //SUCCESS or ERROR
{
CLK_ClockSwitchCmd(ENABLE);
CLK_LSICmd(ENABLE);
CLK_ClockSwitchCmd(DISABLE);
}*/
// CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); //HSI = 16M (1分频)
//ErrorStatus clk_return_status;
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); //HSI = 16M (8分频)=2MHZ
/*
//切换内部低速时钟8M
clk_return_status = CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
if (clk_return_status == SUCCESS) //SUCCESS or ERROR
{
CLK_ClockSwitchCmd(ENABLE);
CLK_HSECmd(ENABLE);
CLK_ClockSwitchCmd(DISABLE);
}*/
CLK_DeInit();//设置为默认值
CLK_HSICmd(ENABLE);//启用HSI
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);//HSI分频
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);//CPU分频
}
使用IIC管脚PB4/PB5的管脚操作宏定义如下
#define SHT30_SCL (0x01<<4)
#define SHT30_SDA (0x01<<5)
#define SHT30_SCL0_O GPIOB->DDR |= SHT30_SCL; GPIOB->CR1 |= SHT30_SCL //GPIOB4 推免输出
#define SHT30_SCL0_H GPIOB->ODR |= SHT30_SCL
#define SHT30_SCL0_L GPIOB->ODR &= ~SHT30_SCL
#define SHT30_SCL0_I GPIOB->DDR &= ~SHT30_SCL; GPIOB->CR1 &= ~SHT30_SCL //GPIOB4 浮空输入
#define SHT30_SCL0_DAT ( (GPIOB->IDR>>4) & 0x01)
#define SHT30_SDA0_O GPIOB->DDR |= SHT30_SDA; GPIOB->CR1 |= SHT30_SDA //GPIOB5 推挽输出
#define SHT30_SDA0_H GPIOB->ODR |= SHT30_SDA
#define SHT30_SDA0_L GPIOB->ODR &= ~SHT30_SDA
#define SHT30_SDA0_I GPIOB->DDR &= ~SHT30_SDA; GPIOB->CR1 &= ~SHT30_SDA //GPIOB5 浮空输入
#define SHT30_SDA0_DAT ( (GPIOB->IDR>>5) & 0x01)
#define SHT30_SlaveAddress (0x44<<1) //7位地址0x44 左移1位 0x45 -- 0x8A
#define noACK 0 //用于判断是否结束通讯
#define ACK 1 //结束数据传输
在main函数开始前,需要对使用的管脚进行初始化配置如下
配置PB4/PB5初始化为输出
uint8 SHT30_Init(vid)
{
uint8 vRval = 0x00;
SHT30_SCL0_O; //设置SCLK为输出
SHT30_SDA0_O; //设置SDA为输出
SHT30_SCL0_H;
SHT30_SCL0_L;
SHT30_SDA0_H;
SHT30_SDA0_L;
vRval += SHT30_Soft_Reset();
SHT30_DelayMs(1);
vRval += SHT30_ClearStaus();
SHT30_DelayMs(1);
//SHT30_Periodic_Measure(SHT30_PERIODOC_H_MEASURE_1S);
return vRval;
}
单次获取数据指令
单次获取数据指令(Measurement Commands for Single Shot Data Acquisition Mode)的详细数据格式如下图。首先从表格最上面开始,Repeatability指的是重复性(重复性越高精度越高,可参考手册的Sensor Performance部分),Clock Stretching指的是时钟延伸,它们的作用下面再讲述。数据流动过程如下。
发送起始信号以及由高7位的器件地址和最低位的写信号(WR=0) 组成的一字节地址,等待应答信号。(注意地址位于高7位,所以传址的时候需要将地址左移一位并加上读1/写0信号,ADDR<<1 | WR);
发送指令的高字节(Most Significant Byte,MSB)并等待应答信号;
发送指令的低字节(Least Significant Byte,LSB)并等待应答信号,之后发送停止信号;
等待一段时间(测量正在进行);
发送起始信号以及由高7位的器件地址和最低位的读信号(RD=1) 组成的一字节地址,然后根据选择的Clock Stretching从两个方向选择。假如失能了时钟延长功能,则等待非应答信号,发送停止信号,延迟一段时间(这步很重要!!延迟时间大约为50ms左右)等待转换结束,然后发送八位的应答信号并等待应答信号,之后便是逐字节分别读取温度和湿度的高字节、低字节以及CRC校验字节,每字节接收完都要发送应答信号,最后发送停止信号即可。而如果使能了是时钟延长功能,则总线的SCL由SHT3x控制,我们只需要通过while(SCL==0)阻塞程序,等待其释放总线然后MCU读取数据即可;
针对SHT3x的一些时序函数如下
/*---------------------------------------------------------------------
功能描述: SHT30 测量结果计算
参数说明: vTemSymbol [out] - 返回温度符号
vTem [out] - 温度
vHum [out] - 湿度
函数返回: 无
---------------------------------------------------------------------*/
uint8 SHT30_Get_TH(uint8 *vTemSymbol, uint16 *vTem, uint16 *vHum)
{
uint8 vDat[8];
uint8 vRval = 0;
vRval = SHT30_Single_Measure(vDat);
if (!vRval) SHT30_calc(vDat, vTemSymbol, vTem, vHum);
return vRval;
}
/*---------------------------------------------------------------------
功能描述: SHT30单次测量
参数说明: vBuf [out] - 测量读取结果
函数返回: 0 - 成功 大于1出错
---------------------------------------------------------------------*/
uint8 SHT30_Single_Measure(uint8 *vBuf)
{
uint8 vRval = 0;
uint8 i = 0;
SHT30_Start();
vRval |= SHT30_SendByte(SHT30_SlaveAddress+0); //地址写
if (!vRval) vRval |= SHT30_SendByte( (SHT30_SINGLE_H_MEASURE_EN>>8)&0xFF ); //使能高精度采集
if (!vRval) vRval |= SHT30_SendByte( (SHT30_SINGLE_H_MEASURE_EN)&0xFF );
SHT30_Stop();
if (vRval) return vRval;
SHT30_SCL0_H;
SHT30_DelayMs(15); //15Ms
SHT30_Start();
if (!vRval) vRval |= SHT30_SendByte(SHT30_SlaveAddress+1); //地址读
if (vRval) return vRval;
for(i=0; i<6; i++)
{
vBuf[i] = SHT30_RecvByte(); //存储数据
if (i == 0x06)
{
SHT30_SendACK(1); //最后一个数据需要回NOACK
}
else
{
SHT30_SendACK(0); //回应ACK
}
}
SHT30_Stop();
return vRval;
}
/*---------------------------------------------------------------------
功能描述: SHT30 测量结果计算
参数说明: vBuf [in] - 测量读取结果
vTemSymbol [out] - 返回温度符号
vTem [out] - 温度
vHum [out] - 湿度
函数返回: 无
---------------------------------------------------------------------*/
void SHT30_calc(uint8 *vBuf, uint8 *vTemSymbol, uint16 *vTem, uint16 *vHum)
{
uint16 vVal = 0x00;
uint8 vCrc = 0x00;
float vTemp = 0.00;
//温度
vCrc = SHT30_CheckCrc8(vBuf, 2);
if (vCrc == vBuf[2])
{
vVal = vBuf[0];
vVal<<=8;
vVal |= vBuf[1];
vTemp = 175.0*vVal/(65536.0-1.0);
if (vTemp >= 45)
{
*vTemSymbol = 1;
*vTem = (uint16)((vTemp - 45.0)*10.0);
}
else
{
*vTemSymbol = 0;
*vTem = (uint16)((45.0 - vTemp)*10.0);
}
}
vBuf += 3;
vVal = 0x00;
vCrc = SHT30_CheckCrc8(vBuf, 2);
if (vCrc == vBuf[2])
{
vVal = vBuf[0];
vVal<<=8;
vVal |= vBuf[1];
vTemp = 100.0*vVal/(65536.0-1.0);
*vHum = (uint16)(vTemp*10);
}
}