51单片机SHT11温湿度传感器使用,仿真实现
文章目录
- 系列文章目录
- 前言
- 一、SHT11时序
- 1.传输时序
- 2、通信复位时序
- 3、发送一个字节
- 4、接收一个字节
- 5、测量温度湿度
- 6、温湿度补偿
- 二、仿真实现
- 三、代码
- 四、总结
前言
首先声明,为了写SHT11,走了很多弯路,看时序且参考网上程序,最终耗费大约两天搞明白怎么实现,虽有误差,但收获还是很多。也处于网上关于这个芯片的代码太少,要不不全,或者又要花钱,今天分享以下。主要练习了怎么通过时序图自己写程序,确实难度不小。
一、SHT11时序 1.传输时序
首先数据线拉高,时钟线拉低。接着时钟线拉高,数据线拉低,时钟线又拉低,时钟线再拉高,数据线拉高,时钟线拉低。
void I2C_Start()
{
I2C_SDA=1;
I2C_SCL=0;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
I2C_SCL=1;
I2C_SDA=1;
I2C_SCL=0;
}
2、通信复位时序
这个也很好理解
void Reset()
{
unsigned char i;
I2C_SDA=1;
I2C_SCL=0;
for(i=0;i<9;i++)
{
I2C_SCL=1;
I2C_SCL=0;
}
I2C_Start();
}
3、发送一个字节
首先发送一个字节(8位数据),再接收从机应答信号与通常I2C时序差不多,只是最后应答有区别。一定注意数据线只能在时钟线为低电平时变化,意思是在时钟线拉低时,数据线准备好数据(这里是主机准备数据),时钟线拉高时,从机读取数据线上的数据
char I2C_SendByte(unsigned char byte)
{
unsigned char i;
char error=0;
I2C_SCL=0;
//拉低时钟线,目的是让主机准备数据,数据线只能在时钟为低电平时变化
for(i=0;i<8;i++)
{
if(byte&(0x80>>i))//主机准备数据
I2C_SDA=1;
else
I2C_SDA=0;
I2C_SCL=1;//从机读取数据,只有在时钟为高电平时,才会读取数据
I2C_SCL=0;
}
I2C_SDA=1;//主机释放SDA
//从机准备应答数据
I2C_SCL=1;
error=I2C_SDA;//主机读取应答数据
I2C_SCL=0;//这一点最特殊,这是第9个时钟下降沿
I2C_SDA=1;//从机释放SDA
return error;
}
4、接收一个字节
主机接收一个字节,然后主机发送应答信号。接收字节与发送同理。时钟线低电平时,从机准备数据,时钟线高电平时,主机读取数据。
unsigned char I2C_ReceiveByte(bit ACK)
{
unsigned char i,byte=0x00;
I2C_SCL=0;//可加可不加,加上容易理解
I2C_SDA=1;//主机释放总线,从机准备好数据
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA)//主机读取从机数据
byte|=(0x80>>i);
I2C_SCL=0;
}
I2C_SDA=ACK;//主机准备好应答数据
I2C_SCL=1;//从机读取应答数据
I2C_SCL=0;
I2C_SDA=1;//主机释放总线,可加可不加
return byte;
}
5、测量温度湿度
参考前人程序
char s_measure(unsigned char *p_value,unsigned char mode)
{
char error=0;
unsigned int i;
I2C_Start();
switch(mode)
{ //选择发送命令
case 0 : error+=I2C_SendByte(0x03); break;
case 1 : error+=I2C_SendByte(0x05); break;
default : break;
}
for (i=0;i<65535;i++)
if(I2C_SDA==0)
break; //等待测量结束,测量温湿度需要时间
if(I2C_SDA)
error+=1; // 如果长时间数据线没有拉低,说明测量错误
*(p_value) =I2C_ReceiveByte(0); //读第一个字节,高字节 (MSB)
*(p_value+1)=I2C_ReceiveByte(1); //读第二个字节,低字节 (LSB)
//这里发送非应答信号,不用CRC校验
return error;
}
6、温湿度补偿
传入两个指针,下面是数据手册公式
void calc_SHT11(float *p_humidity ,float *p_temperature) //const表示常量,不允许修改里面的内容
{ const float C1=-4.0; // 12位湿度精度 修正公式
const float C2=+0.0405; // 12位湿度精度 修正公式
const float C3=-0.0000028; // 12位湿度精度 修正公式
const float T1=+0.01; // 14位温度精度 5V条件 修正公式
const float T2=+0.00008; // 14位温度精度 5V条件 修正公式
float rh=*p_humidity; // rh: Humidity 12 Bit
float t=*p_temperature; // t: Temperature 14 Bit
float rh_lin; // rh_lin: Humidity linear
float rh_true; // rh_true: Temperature compensated humidity
float t_C; // t_C : Temperature [C]
t_C=t*0.01 - 40; //补偿温度,14位温度精度 5V条件 修正公式
rh_lin=C3*rh*rh + C2*rh + C1; //相对湿度非线性补偿
rh_true=(t_C-25)*(T1+T2*rh)+rh_lin-3; //相对湿度对于温度依赖性补偿
if(rh_true>100)rh_true=100; //湿度最大修正
if(rh_true<0.1)rh_true=0.1; //湿度最小修正
*p_temperature=t_C; //返回温度结果
*p_humidity=rh_true; //返回湿度结果
}
二、仿真实现
三、代码
采用模块化编程
//…main.c…//
#include#include "LCD1602.h" #include "I2C.h" #include #include typedef union //定义了两个共用体:如果没有typedef那么就是普通的定义了匿名联合的一个变量value.加了typedef后, 定义的就是类型别名, 当类型一样用 { unsigned int i; //i表示测量得到的温湿度数据(int 形式保存的数据) float f; //f表示测量得到的温湿度数据(float 形式保存的数据) } value; void delay_n10us(unsigned int n) //延时n个10us@12M晶振 { unsigned int i; for(i=n;i>0;i--) { _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); } } void main(void) { value humi_val,temp_val; //185行,定义两个共同体,一个用于湿度,一个用于温度 unsigned char error; //用于检验是否出现错误 unsigned int wendu,shidu; //最终,一位小数温湿度的值 LCD_Init(); Reset(); while(1) {//int为两个字节,这里没看懂,高字节赋给int低字节什么操作,其他都没问题,看到网上程序全是这么写的 error=0; //初始化error=0,即没有错误 error+=s_measure((unsigned char*) &humi_val.i,1); //measure humidity error+=s_measure((unsigned char*) &temp_val.i,0); //measure temperature if(error!=0) Reset(); //in case of an error: connection reset else { humi_val.f=(float)humi_val.i; //converts integer to float temp_val.f=(float)temp_val.i; //converts integer to float calc_SHT11(&humi_val.f,&temp_val.f); //calculate humidity, temperature LCD_ShowString(1,1,"TE:"); LCD_ShowString(2,1,"RH:"); LCD_ShowString(1,7,"."); LCD_ShowString(2,7,"."); LCD_ShowString(1,9,"C "); LCD_ShowString(2,9,"% "); wendu=10*temp_val.f; //例如温度109.1→1091 LCD_ShowChar(1,4,abs(wendu)/1000+'0'); //显示温度百位,加“0”是为了将字符的ASCII码大于48(即字符0的ASCII值),一般是将数字0,1,2……,9转换为字符“0”,“1”……,“9”; LCD_ShowChar(1,5,abs(wendu)%1000/100+'0'); //显示温度十位 LCD_ShowChar(1,6,abs(wendu)%100/10+'0'); //显示温度个位 LCD_ShowChar(1,8,abs(wendu)%10+'0'); //显示温度小数点后第一位 shidu=10*humi_val.f; LCD_ShowChar(2,4,shidu/1000+'0'); //显示湿度百位 LCD_ShowChar(2,5,(shidu%1000)/100+'0'); //显示湿度十位 LCD_ShowChar(2,6,(shidu%100)/10+'0'); //显示湿度个位 LCD_ShowChar(2,8,(shidu%10)+'0'); //显示湿度小数点后第一位 } delay_n10us(800); } }
//…LCD1602.c…//
#include#include #define LCD_Bus P0 sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_EN=P2^7; //函数定义: void LCD_Delay() { unsigned char i, j; i = 2; j = 239; do { while (--j); } while (--i); } void LCD_WriteCommand(unsigned char command) { LCD_RS=0; LCD_RW=0; LCD_EN=0; LCD_Bus=command; LCD_EN=1; LCD_Delay(); LCD_EN=0; LCD_Delay(); } void LCD_WriteData(unsigned char Data) { LCD_RS=1; LCD_RW=0; LCD_EN=0; LCD_Bus=Data; LCD_EN=1; LCD_Delay(); LCD_EN=0; LCD_Delay(); } void LCD_Init() { LCD_WriteCommand(0x38); LCD_WriteCommand(0x06); LCD_WriteCommand(0x0c); LCD_WriteCommand(0x01); } void LCD_SetAddress(unsigned char line,unsigned char column) { if(line==1) LCD_WriteCommand(0x80+column-1); else LCD_WriteCommand(0x80+0x40+column-1); } void LCD_ShowChar(unsigned char line,unsigned char column,char Char) { LCD_SetAddress(line,column); LCD_WriteData(Char); } void LCD_ShowString(unsigned char line,unsigned char column,char*String) { LCD_SetAddress(line,column); while(*String!=' ') { LCD_WriteData(*String++); } } int pow(int x,int y) { int result=1; while(y-->0) { result*=x; } return result; } void LCD_ShowNumber(unsigned char line,unsigned char column,unsigned int number,unsigned char length) { unsigned char i; LCD_SetAddress(line,column); for(i=length;i>0;i--) { LCD_WriteData(number/pow(10,i-1)%10+'0'); } }
//…LCD1602.h…//
#ifndef __LCD1602_H__ #define __LCD1602_H__ void LCD_Init(); void LCD_ShowChar(unsigned char line,unsigned char column,char Char); void LCD_ShowString(unsigned char line,unsigned char column,char*String); void LCD_ShowNumber(unsigned char line,unsigned char column,unsigned int number,unsigned char length); #endif
//…I2C.c…//
#include#include sbit I2C_SCL=P2^1; sbit I2C_SDA=P2^0; #define MEASURE_TEMP 0x03 #define MEASURE_HUMI 0x05 void I2C_Start() { I2C_SDA=1; I2C_SCL=0; I2C_SCL=1; I2C_SDA=0; I2C_SCL=0; I2C_SCL=1; I2C_SDA=1; I2C_SCL=0; } void Reset() { unsigned char i; I2C_SDA=1; I2C_SCL=0; for(i=0;i<9;i++) { I2C_SCL=1; I2C_SCL=0; } I2C_Start(); } char I2C_SendByte(unsigned char byte) { unsigned char i; char error=0; I2C_SCL=0; for(i=0;i<8;i++) { if(byte&(0x80>>i))//主机准备数据 I2C_SDA=1; else I2C_SDA=0; I2C_SCL=1;//从机读取数据 I2C_SCL=0; } I2C_SDA=1;//主机释放SDA //从机准备应答数据 I2C_SCL=1; error=I2C_SDA;//主机读取应答数据 I2C_SCL=0; I2C_SDA=1;//从机释放SDA return error; } unsigned char I2C_ReceiveByte(bit ACK) { unsigned char i,byte=0x00; I2C_SDA=1;//主机释放总线 for(i=0;i<8;i++) { I2C_SCL=1; if(I2C_SDA)//主机读取从机数据 byte|=(0x80>>i); I2C_SCL=0; } I2C_SDA=ACK; I2C_SCL=1;//从机读取应答数据 I2C_SCL=0; I2C_SDA=1;//主机释放总线 return byte; } char s_measure(unsigned char *p_value,unsigned char mode) { char error=0; unsigned int i; I2C_Start(); switch(mode) { //选择发送命令 case 0 : error+=I2C_SendByte(MEASURE_TEMP); break; case 1 : error+=I2C_SendByte(MEASURE_HUMI); break; default : break; } for (i=0;i<65535;i++) if(I2C_SDA==0) break; //等待测量结束 if(I2C_SDA) error+=1; // 如果长时间数据线没有拉低,说明测量错误 *(p_value) =I2C_ReceiveByte(0); //读第一个字节,高字节 (MSB) *(p_value+1)=I2C_ReceiveByte(1); //读第二个字节,低字节 (LSB) return error; } void calc_SHT11(float *p_humidity ,float *p_temperature) //const表示常量,不允许修改里面的内容 { const float C1=-4.0; // 12位湿度精度 修正公式 const float C2=+0.0405; // 12位湿度精度 修正公式 const float C3=-0.0000028; // 12位湿度精度 修正公式 const float T1=+0.01; // 14位温度精度 5V条件 修正公式 const float T2=+0.00008; // 14位温度精度 5V条件 修正公式 float rh=*p_humidity; // rh: Humidity [Ticks] 12 Bit float t=*p_temperature; // t: Temperature [Ticks] 14 Bit float rh_lin; // rh_lin: Humidity linear float rh_true; // rh_true: Temperature compensated humidity float t_C; // t_C : Temperature [C] t_C=t*0.01 - 40; //补偿温度,14位温度精度 5V条件 修正公式 rh_lin=C3*rh*rh + C2*rh + C1; //相对湿度非线性补偿 rh_true=(t_C-25)*(T1+T2*rh)+rh_lin-3; //相对湿度对于温度依赖性补偿 if(rh_true>100)rh_true=100; //湿度最大修正 if(rh_true<0.1)rh_true=0.1; //湿度最小修正 *p_temperature=t_C; //返回温度结果 *p_humidity=rh_true; //返回湿度结果 }
//…I2c.h…//
#ifndef __I2C_H__ #define __I2C_H__ void Reset(); char s_measure(unsigned char *p_value,unsigned char mode); void calc_SHT11(float *p_humidity ,float *p_temperature); #endif四、总结
以上关于SHT11仿真及源程序,在低温时误差较小,可以放心使用,跪求点赞收藏关注。有问题可评论,私聊



