简介:
SHT40是一款高性能的温湿度芯片,由业内知名的Sensirion公司推出,属于其第四代数字温湿度传感器系列1。这款芯片在测量范围内能够提供始终如一的高精确度,具有多种显著的优点和特性。
首先,SHT40在精度和稳定性方面表现出色。其温度精度可达±0.2°C(典型值),湿度精度可达±1.8%RH(典型值),相较于其前代产品SHT20有了进一步的提升2。同时,它还具有更好的长期稳定性,这主要得益于其内部增加的自校准功能,能够有效地补偿传感器老化和温度漂移1。
其次,SHT40在灵敏度和响应速度上也进行了优化,尤其在湿度测量方面,其响应时间更快,能够更迅速地捕捉到环境变化2。这种特性使其在需要快速响应的应用场景中非常适用。
此外,SHT40在集成度和功耗控制方面也进行了改进。它在继承了SHT20低功耗和紧凑封装的基础上,进一步优化了功耗控制,更适合于低功耗应用,如物联网(IoT)设备和能源敏感的系统2。其电源电压范围从1.08 V扩展到3.6 V,非常适合移动和电池驱动应用34。
SHT40还增强了抗干扰能力,能够更好地抵抗电磁干扰(EMI)和其他环境因素的影响,确保在复杂环境中也能提供可靠的测量结果1。
在应用方面,SHT40因其高精度、低功耗和快速响应等特性,被广泛应用于智能楼宇、天气站、仓库存储、养殖、孵化等场景1。同时,其坚固的DFN外壳和异常小的尺寸使得它易于集成到各种具有挑战性的设计中,同时满足最高的可靠性要求4。
SHT40是一款高性能、高精度、低功耗的温湿度芯片,适用于各种需要高精度测量的应用场景。
接下来看看如何实现驱动,以下提供了完整的全部代码以及原理图
温湿度传感器数据手册:
原理图:
I2C读写时序(此图是SHT30的仅供参考):
相关命令:
原理图以及伪代码驱动:
程序:
配置为开漏输出。
IO6--SDA
IO7--SCL
器件地址 0x44
ADDR引脚连接到 VDD 地址为0x45
ADDR引脚连接到VSS 地址为0x44
驱动代码:
#define slave_addr 0x44 //从机地址
#define IIC_SDA 6
#define IIC_SCL 7
const int buttonPin = 1; // the number of the pushbutton pin
const int I2C_SDA = 6;
const int I2C_SCL = 7;
//******************************************************************************
//功能: 延时函数,当ms=1时,
// 延时为1毫秒(@12MHz)
//入口: ms
//出口:无
//******************************************************************************
void DelayMs(uint ms)
{
delay(ms);
}
//******************************************************************************
//功能: 延时函数 延时微妙
//入口: j
//出口:无
//******************************************************************************
void DelayUs(unsigned int j)
{
delayMicroseconds(j);
}
/*-------------------------------------------------
* 函数名:IIC_Start
* 功能: 产生IIC起始信号
* 输入: 无
* 输出: 无
--------------------------------------------------*/
void IIC_Start(void)
{
pinMode(I2C_SDA,OUTPUT); //SDA线输出
digitalWrite(IIC_SDA,HIGH);
digitalWrite(IIC_SCL,HIGH);
DelayUs(10);
digitalWrite(IIC_SDA,LOW); //起始信号:当SCL为高电平时,SDA由高电平跳变为低电平
DelayUs(10);
digitalWrite(IIC_SCL,LOW); //钳住I2C总线,准备发送或接收数据
DelayUs(10);
}
/*-------------------------------------------------
* 函数名:IIC_Stop
* 功能: 产生IIC停止信号
* 输入: 无
* 输出: 无
--------------------------------------------------*/
void IIC_Stop(void)
{
pinMode(I2C_SDA,OUTPUT); ; //SDA线输出
digitalWrite(IIC_SCL,LOW);
digitalWrite(IIC_SDA,LOW); //终止信号:当SCL为高电平时,SDA有低电平跳变到高电平
DelayUs(10);
digitalWrite(IIC_SCL,HIGH);
DelayUs(10);
digitalWrite(IIC_SDA,HIGH); //发送I2C总线结束信号
DelayUs(10);
}
/*-------------------------------------------------
* 函数名:IIC_Wait_Ack
* 功能: 等待应答信号到来
* 输入: 无
* 输出: 返回值:1,接收应答失败
* 0,接收应答成功
--------------------------------------------------*/
unsigned char IIC_Wait_Ack(void)
{
unsigned char ucErrTime=0;
pinMode(I2C_SDA,INPUT); //SDA设置为输入
digitalWrite(IIC_SDA,HIGH);
DelayUs(5);
digitalWrite(IIC_SCL,HIGH);
DelayUs(5);
while(digitalRead(I2C_SDA)==HIGH)
{
ucErrTime++;
if(ucErrTime>250) //等待超时
{
IIC_Stop();
return 1;
}
}
digitalWrite(IIC_SCL,LOW); //时钟输出0
return 0;
}
/*-------------------------------------------------
* 函数名:IIC_Ack
* 功能: 产生ACK应答
* 输入: 无
* 输出: 无
--------------------------------------------------*/
void IIC_Ack(void)
{
digitalWrite(IIC_SCL,LOW);
pinMode(I2C_SDA,OUTPUT); ; //SDA线输出
digitalWrite(IIC_SDA,LOW);
DelayUs(5);
digitalWrite(IIC_SCL,HIGH);
DelayUs(5);
digitalWrite(IIC_SCL,LOW);
//digitalWrite(IIC_SDA,HIGH); //释放总线,修复湿度和精度问题
}
/*-------------------------------------------------
* 函数名:IIC_NAck
* 功能: 不产生ACK应答
* 输入: 无
* 输出: 无
--------------------------------------------------*/
void IIC_NAck(void)
{
digitalWrite(IIC_SCL,LOW);
pinMode(I2C_SDA,OUTPUT); ; //SDA线输出
digitalWrite(IIC_SDA,HIGH);
DelayUs(5);
digitalWrite(IIC_SCL,HIGH);
DelayUs(5);
digitalWrite(IIC_SCL,LOW);
}
/*-------------------------------------------------
* 函数名:IIC_Send_Byte
* 功能: IIC发送一个字节
* 输入: 写入要发送的一个人字节数据txd
* 输出: 无
--------------------------------------------------*/
void IIC_Send_Byte(unsigned char txd)
{
unsigned char t;
pinMode(I2C_SDA,OUTPUT); //SDA线输出
digitalWrite(IIC_SCL,LOW); //拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)>>7)
digitalWrite(IIC_SDA,HIGH);
else
digitalWrite(IIC_SDA,LOW);
txd<<=1;
DelayUs(5);
digitalWrite(IIC_SCL,HIGH);
DelayUs(5);
digitalWrite(IIC_SCL,LOW);
DelayUs(5);
}
}
/*-------------------------------------------------
* 函数名:IIC_Read_Byte
* 功能: IIC读一个字节
* 输入: 无
* 输出: 读出存储器里面的数据并返回receive
--------------------------------------------------*/
unsigned char IIC_Read_Byte(void)
{
unsigned char i,receive=0;
pinMode(I2C_SDA,INPUT); //SDA设置为输入
for(i=0;i<8;i++ )
{
digitalWrite(IIC_SCL,LOW);
DelayUs(5);
digitalWrite(IIC_SCL,HIGH);
receive<<=1;
if(digitalRead(I2C_SDA)==HIGH)receive++;
DelayUs(5);
}
//IIC_NAck(); //发送nACK 去除,修复后续读写应答问题
return receive;
}
void SHT40_Read_Temperature_Humidity(unsigned char addr)
{
unsigned int tem,hum;
unsigned char buff[6];
float Temperature=0;
float Humidity=0;
float humiture[2] = {0};
IIC_Start();
IIC_Send_Byte(addr<<1 | 0x00); //w 0 r 1
IIC_Wait_Ack();
IIC_Send_Byte(0x94); //复位
IIC_Wait_Ack();
IIC_Stop();
delay(10);
IIC_Start();
IIC_Send_Byte(addr<<1 | 0x00); //w 0 r 1
IIC_Wait_Ack();
IIC_Send_Byte(0xFD);
IIC_Wait_Ack();
// IIC_Send_Byte(0x06);
// IIC_Wait_Ack();
IIC_Stop();
delay(10);
IIC_Start();
IIC_Send_Byte(addr<<1 | 0x01); //w 0 r 1
if(IIC_Wait_Ack()==0)
{
buff[0]=IIC_Read_Byte();
IIC_Ack();
buff[1]=IIC_Read_Byte();
IIC_Ack();
buff[2]=IIC_Read_Byte();
IIC_Ack();
buff[3]=IIC_Read_Byte();
IIC_Ack();
buff[4]=IIC_Read_Byte();
IIC_Ack();
buff[5]=IIC_Read_Byte();
IIC_NAck();
IIC_Stop();
}
//计算最终结果
tem = buff[0] * 256 + buff[1];
//checksum_t = buff[2];
hum = buff[3] * 256 + buff[4];
//checksum_rh = buff[5];
Temperature = -45 + 175 * tem/65535.0;
Humidity = -6 + 125 * hum/65535.0;
if (Humidity > 100)
Humidity = 100;
if (Humidity < 0)
Humidity = 0;
if((Temperature>=-20)&&(Temperature<=125)&&(Humidity>=0)&&(Humidity<=100))
{
humiture[0]=Temperature;
humiture[2]=Humidity;
Serial.print(Temperature);
Serial.print(" C, ");
Serial.print(Humidity);
Serial.println(" HT ");
//打印原始数据查看
// for(int i=0;i<6;i++)
// Serial.print(buff[i]);
// Serial.print(" ");
}
IIC_Read_Byte();
IIC_Stop();
DelayMs(10);
hum=0;
tem=0;
}
/*************************************************************************************************
* 函 数 名: SHT40_Heater_200mW_1s
* 入口参数: 无
* 返回值:无
* 函数功能: 开始内部加热器,以200mW加热1秒(一秒)
* 说 明:加热时间不能超过运行时间的10%,否则会过热。详情说明请参考数据手册12页
*************************************************************************************************/
void SHT40_Heater_200mW_1s()
{
IIC_Start();
IIC_Send_Byte(0x44<<1 | 0x00); //w 0 r 1
IIC_Wait_Ack();
IIC_Send_Byte(0x39); //复位
IIC_Wait_Ack();
IIC_Stop();
delay(10);
}
/*************************************************************************************************
* 函 数 名: SHT40_Read_Serial_Number
* 入口参数: 无
* 返回值:32bit的序列号
* 函数功能: 读取SHT40的出场唯一序列号
* 说 明:无
*************************************************************************************************/
uint32_t SHT40_Read_Serial_Number()
{
uint32_t Serial_Number;
uint8_t I2C_Receive_Data[6];
IIC_Start();
IIC_Send_Byte(0x44<<1 | 0x00); //w 0 r 1
IIC_Wait_Ack();
IIC_Send_Byte(0x89); //SHT40_READ_SERIAL_NUMBER = 0x89
IIC_Wait_Ack();
IIC_Stop();
IIC_Start();
IIC_Send_Byte(0x44<<1 | 0x01); //w 0 r 1
if(IIC_Wait_Ack()==0)
{
I2C_Receive_Data[0]=IIC_Read_Byte();
IIC_Ack();
I2C_Receive_Data[1]=IIC_Read_Byte();
IIC_Ack();
I2C_Receive_Data[2]=IIC_Read_Byte();
IIC_Ack();
I2C_Receive_Data[3]=IIC_Read_Byte();
IIC_Ack();
I2C_Receive_Data[4]=IIC_Read_Byte();
IIC_Ack();
I2C_Receive_Data[5]=IIC_Read_Byte();
IIC_NAck();
IIC_Stop();
}
Serial_Number=(I2C_Receive_Data[0] << 24)|
(I2C_Receive_Data[1] << 16)|
(I2C_Receive_Data[3] << 8)|
(I2C_Receive_Data[4] << 0);
Serial.print("Serial_Number:");
Serial.println(Serial_Number);
return Serial_Number;
}
void setup() {
Serial.begin(115200);
// initialize the pin as an output:
pinMode(I2C_SDA, OUTPUT);
pinMode(I2C_SCL, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
//读取温湿度并打印
SHT40_Read_Temperature_Humidity(0x44);
// check if the pushbutton is pressed. If it is, the buttonState is LOW:
if (buttonState == LOW) {
delay(10);
if (digitalRead(buttonPin) == LOW) {
Serial.println("key press");
//加热1s,读序列号
SHT40_Heater_200mW_1s();
SHT40_Read_Serial_Number();
//等待按钮松开,跳出循环
while(digitalRead(buttonPin) == LOW)
{
}
}
}
// 检查是否有可用的串口数据
if (Serial.available() > 0) {
// 读取整个命令字符串
String command = Serial.readStringUntil(';');
// 在串口监视器中输出接收到的命令
Serial.println(command);
// 尝试解析命令
}
}
输出结果:
可能会遇到的问题:
读出6个字节数据时,只有第一字节有数据,其他的都是0xff
解决方法:
1.确认ack的处理是否正确
2.确保在ack后SDA引脚有设置为高电平,即要释放SDA的控制