系列文章目录
51单片机SHT11温湿度传感器使用,仿真实现
前言
首先声明,为了写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 <regx52.h>
#include "LCD1602.h"
#include "I2C.h"
#include <intrins.h>
#include <math.h>
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 <reg52.h>
#include <intrins.h>
#define LCD_Bus P0
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
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!='\0')
{
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 <regx52.h>
#include <intrins.h>
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仿真及源程序,在低温时误差较小,可以放心使用,跪求点赞收藏关注。有问题可评论,私聊