SHT11源程序分享及51单片机仿真实现

本文详细介绍了如何使用51单片机配合SHT11传感器进行温湿度测量,包括传感器的时序分析、通信复位、数据发送与接收等步骤,并提供了完整的仿真实现和代码示例。同时,文章讨论了温湿度的补偿计算方法,适合初学者参考学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章目录

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仿真及源程序,在低温时误差较小,可以放心使用,跪求点赞收藏关注。有问题可评论,私聊

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值