分享一个基于51单片机的湿度监测仿真和程序

控制/MCU

1878人已加入

描述

今天分享一个基于51单片机的湿度监测仿真和程序。

仿真所用的湿度传感器为SHT11。SHT11是瑞士Scnsirion公司推出的一款数字温湿度传感器芯片。该芯片广泛应用于暖通空调、汽车、消费电子、自动控制等领域。

 

其主要特点如下: 高度集成,将温度感测、湿度感测、信号变换、A/D转换和加热器等功能集成到一个芯片上;   

提供二线数字串行接口SCK和DATA,接口简单,支持CRC传输校验,传输可靠性高;   

测量精度可编程调节,内置A/D转换器(分辨率为8~12位,可以通过对芯片内部寄存器编程米选择);

51单片机

引脚 名称 功能
1 GND 地线
2 DATA 串行数据线
3 SCK 串行时钟线
4 VDD 电源正
NC NC 悬空

传输启动

数据传输初始化:

当SCK时钟为高电平时,DATA翻转位低电平,紧接着SCK变为低电平,

随后在SCK时钟高电平时,DATA翻转为高电平。  

51单片机

 

void start_sht11(void)  //启动
//--------------------------------------------------------
{  
   DATA=1; SCK=0;                   //数据为1,SCK=0
   _nop_();
   SCK=1;                          //第一个脉冲
   _nop_();
   DATA=0;                         //数据跌落
   _nop_ ();
   SCK=0;                         //完成一个脉冲
   _nop_(); _nop_(); _nop_();
   SCK=1;                         //再一个脉冲
   _nop_();
   DATA=1;                        //数据变为1       
   _nop_();
   SCK=0;                         //完成该脉冲       
}

 

读写数据

51单片机

51单片机

控制器向传感器发送命令的过程中,DATA在SCK上升沿有效,且在SCK高电平时必须保持稳定;DATA在SCK下降沿之后改变。(参考上图 DATA valid write 段)

从传感器读取数据读取数据的过程中,DATA Tv在SCK变低以后有效,且维持到下一个SCK的下降沿。(参考上图 DATA valid read 段)

 

char read(void) //读一个字节 返回应答信号
//----------------------------------------------------------------------------------
// reads a byte form the Sensibus and gives an acknowledge in case of "ack=1" 
{ 
  unsigned char i,val=0;
  temp_LL=0;
  temp_h=0;
  DATA=1;                           //释放数据总线
  for (i=0x80;i>0;i/=2)             //位移8位
  { 
    SCK=1;                          //上升沿读入
    if (DATA) val=(val | i);        //确定值  
    SCK=0;             
  }
  DATA=0;                        //读应答信号,有应答为1,为应答为0 通过CPU下拉为应答
  SCK=1;                            //第9个脉冲
  _nop_(); _nop_(); _nop_();          //pulswith approx. 5 us 
  SCK=0;                
  DATA=1;                           //释放数据总线
  temp_h=val;
  val=0;
 ////低8位/////////////////////////////
  DATA=1;                           //释放数据总线
  for (i=0x80;i>0;i/=2)             //位移8位
  { 
    SCK=1;                          //上升沿读入
    if (DATA) val=(val | i);        //确定值  
    SCK=0;             
  }
  DATA=1;//0;                       //不需要应答 通过CPU下拉为应答
  SCK=1;                            //第9个脉冲
  _nop_(); _nop_(); _nop_();          //pulswith approx. 5 us 
  SCK=0;                
  DATA=1;                           //释放数据总线
  temp_LL=val;
  return val ;
}
////////////




char write(unsigned char value) //写一个字节 返回应答信号
//---------------------------------------------------------
{ 
  unsigned char i ;
  ack=0;  
  for (i=0x80;i>0;i/=2)             //释放数据总线
  { if (i & value) DATA=1;          //写入值
    else DATA=0;                        
    SCK=1;                          //上升沿写入
     _nop_(); _nop_(); _nop_();        //延时  
    SCK=0;
  }
  DATA=1;                           //释放数据总线
  SCK=1;                            //第9个脉冲
  if  (DATA==1) ack=1;        //读应答信号
  SCK=0;        
  return ack;                     //error=1 表示没有应答
}

 

传感器复位

如果出现通讯中断,当DATA保持高电平时,触发SCK时钟9次或更多,然后发送“传输启动”时序就可以完成传感器的复位。

51单片机

 

//////////////////////////////////
void sht_rest(void)  //复位 
{  
  unsigned char i; 
  DATA=1; SCK=0;                    //数据为1 时钟为0
  for(i=0;i<9;i++)                  //9 个脉冲为 复位
  { SCK=1;
    SCK=0;
  }
  start_sht11();                   //启动
}

 

Proteus仿真如下图。主要功能是利用SHT11进行湿度的采集,并通过LCD1602显示 。在仿真中通过SHT11上面的操作按钮就可以改变SHT11的温度和湿度数值。该仿真没有对温度数据进行采集和现实。

51单片机

完整的程序如下

#include 
#include
#define  uint unsigned int
#define uchar unsigned char   
/*------------------------------------------------
                 硬件端口定义
------------------------------------------------*/
sbit RS = P2^0;   //定义端口 
sbit RW = P2^1;
sbit EN = P2^2;
sbit LED = P2^4 ;


sbit RELAY = P1^0 ;


#define DataPort P0 
sbit DATA =P2^6;  //数据
sbit SCK=P2^7;    //时钟
uchar cnt = 0 ;


unsigned char temp_h  ;  //全局应答变量
unsigned char temp_LL  ;//全局应答变量
unsigned char error  ;  //全局错误变量
unsigned char ack  ;  //全局应答变量


#define RS_CLR RS=0 
#define RS_SET RS=1


#define RW_CLR RW=0 
#define RW_SET RW=1 


#define EN_CLR EN=0
#define EN_SET EN=1


#define TEMP_ML 0x03       //000   0001    1 温度命令
#define HUMI_ML 0x05       //000   0010    1 温度命令


unsigned  int  xianzhi_h=0;//湿度显值




uchar key ;
uchar key_buf ;
uchar temp ;
uchar frq_cnt = 0;
bit updat = 0 ;
uchar delay_cnt = 0 ;


#define  TH0_BUF 0x3c
#define  TL0_BUF 0xaf


void text_jisuan_humi(void);
/*------------------------------------------------
 uS延时函数,含有输入参数 unsigned char t,无返回值
 unsigned char 是定义无符号字符变量,其值的范围是
 0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
 长度如下 T=tx2+5 uS 
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{   
 while(--t);
}
/*------------------------------------------------
 mS延时函数,含有输入参数 unsigned char t,无返回值
 unsigned char 是定义无符号字符变量,其值的范围是
 0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
     
 while(t--)
 {
     //大致延时1mS
     DelayUs2x(245);
   DelayUs2x(245);
 }
}
/*------------------------------------------------
              判忙函数
------------------------------------------------*/
 bit LCD_Check_Busy(void) 
{ 
 DataPort= 0xFF; 
 RS_CLR; 
 RW_SET; 
 EN_CLR; 
 _nop_(); 
 EN_SET;
 return (bit)(DataPort & 0x80);
}
/*------------------------------------------------
              写入命令函数
------------------------------------------------*/
 void LCD_Write_Com(unsigned char com) 
{  
// while(LCD_Check_Busy()); //忙则等待
 DelayMs(5);
 RS_CLR; 
 RW_CLR; 
 EN_SET; 
 DataPort= com; 
 _nop_(); 
 EN_CLR;
 }
/*------------------------------------------------
              写入数据函数
------------------------------------------------*/
 void LCD_Write_Data(unsigned char Data) 
{ 
 //while(LCD_Check_Busy()); //忙则等待
 DelayMs(5);
 RS_SET; 
 RW_CLR; 
 EN_SET; 
 DataPort= Data; 
 _nop_();
 EN_CLR;
 }


/*------------------------------------------------
                清屏函数
------------------------------------------------*/
 void LCD_Clear(void) 
{ 
 LCD_Write_Com(0x01); 
 DelayMs(5);
 }
/*------------------------------------------------
              写入字符串函数
------------------------------------------------*/
 void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s) 
{     
 if (y == 0) 
   {     
   LCD_Write_Com(0x80 + x);     //表示第一行
   }
 else 
   {      
   LCD_Write_Com(0xC0 + x);      //表示第二行
   }        
 while (*s) 
   {     
 LCD_Write_Data( *s);     
 s ++;     
   }
 }
/*------------------------------------------------
              写入字符函数
------------------------------------------------*/
 void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) 
{     
 if (y == 0) 
   {     
   LCD_Write_Com(0x80 + x);     
   }    
 else 
   {     
   LCD_Write_Com(0xC0 + x);     
   }        
 LCD_Write_Data( Data);  
 }
/*------------------------------------------------
              初始化函数
------------------------------------------------*/
 void LCD_Init(void) 
{
   LCD_Write_Com(0x38);    /*显示模式设置*/ 
   DelayMs(5); 
   LCD_Write_Com(0x38); 
   DelayMs(5); 
   LCD_Write_Com(0x38); 
   DelayMs(5); 
   LCD_Write_Com(0x38);  
   LCD_Write_Com(0x08);    /*显示关闭*/ 
   LCD_Write_Com(0x01);    /*显示清屏*/ 
   LCD_Write_Com(0x06);    /*显示光标移动设置*/ 
   DelayMs(5); 
   LCD_Write_Com(0x0C);    /*显示开及光标设置*/
   }
/*------------------------------------------------
               定时器0中断
------------------------------------------------*/
void TIM0_ISR() interrupt 1
{
  TR0=0;      
  TH0 = TH0_BUF ; 
  TL0 = TL0_BUF ;
  if(cnt<5)
  {
    cnt ++ ;
  }else
  {
    cnt = 0 ;
    updat = 1 ;
  }
  TR0=1;
}
/*******************************基本驱动程    *************************************/


/////////////////
//////////////////////
char read(void) //读一个字节 返回应答信号
//----------------------------------------------------------------------------------
// reads a byte form the Sensibus and gives an acknowledge in case of "ack=1" 
{ 
  unsigned char i,val=0;
  temp_LL=0;
  temp_h=0;
  DATA=1;                           //释放数据总线
  for (i=0x80;i>0;i/=2)             //位移8位
  { 
    SCK=1;                          //上升沿读入
    if (DATA) val=(val | i);        //确定值  
    SCK=0;             
  }
  DATA=0;                        //读应答信号,有应答为1,为应答为0 通过CPU下拉为应答
  SCK=1;                            //第9个脉冲
  _nop_(); _nop_(); _nop_();          //pulswith approx. 5 us 
  SCK=0;                
  DATA=1;                           //释放数据总线
  temp_h=val;
  val=0;
 ////低8位/////////////////////////////
  DATA=1;                           //释放数据总线
  for (i=0x80;i>0;i/=2)             //位移8位
  { 
    SCK=1;                          //上升沿读入
    if (DATA) val=(val | i);        //确定值  
    SCK=0;             
  }
  DATA=1;//0;                       //不需要应答 通过CPU下拉为应答
  SCK=1;                            //第9个脉冲
  _nop_(); _nop_(); _nop_();          //pulswith approx. 5 us 
  SCK=0;                
  DATA=1;                           //释放数据总线
  temp_LL=val;
  return val ;
}
////////////




char write(unsigned char value) //写一个字节 返回应答信号
//---------------------------------------------------------
{ 
  unsigned char i ;
  ack=0;  
  for (i=0x80;i>0;i/=2)             //释放数据总线
  { if (i & value) DATA=1;          //写入值
    else DATA=0;                        
    SCK=1;                          //上升沿写入
     _nop_(); _nop_(); _nop_();        //延时  
    SCK=0;
  }
  DATA=1;                           //释放数据总线
  SCK=1;                            //第9个脉冲
  if  (DATA==1) ack=1;        //读应答信号
  SCK=0;        
  return ack;                     //error=1 表示没有应答
}
////////


void start_sht11(void)  //启动
//--------------------------------------------------------
{  
   DATA=1; SCK=0;                   //数据为1,SCK=0
   _nop_();
   SCK=1;                          //第一个脉冲
   _nop_();
   DATA=0;                         //数据跌落
   _nop_ ();
   SCK=0;                         //完成一个脉冲
   _nop_(); _nop_(); _nop_();
   SCK=1;                         //再一个脉冲
   _nop_();
   DATA=1;                        //数据变为1       
   _nop_();
   SCK=0;                         //完成该脉冲       
}
//////////////////////////////////
void sht_rest(void)  //复位 
{  
  unsigned char i; 
  DATA=1; SCK=0;                    //数据为1 时钟为0
  for(i=0;i<9;i++)                  //9 个脉冲为 复位
  { SCK=1;
    SCK=0;
  }
  start_sht11();                   //启动
}


////////////////////////////////




//测量温度或者是温度,返回校验值
void convert_data(unsigned char ml)
{ 
   unsigned int i;
   start_sht11();                   //启动
   write(ml);//写入测温度
  if(ack==1) 
  {
    sht_rest() ;//复位
        write(ml);//写入测温度
    }  
  for (i=0;i<55535;i++){ if(DATA==0) break; }
   read();//读温度
}




/////////湿度采集处理//////


void get_humidata(void)
{
    error=0;
    ack=0;
      sht_rest() ;      //复位
     convert_data(HUMI_ML);
      text_jisuan_humi();
}


///////计算湿度//////
void text_jisuan_humi(void)
{
    float aa=0,bb=0,humi_zi;
  int   abcd=0;
  abcd = temp_h ;
  abcd = abcd <<8|temp_LL;
  aa = (float) abcd ;
   bb=aa*aa*2.8/1000000;
   aa=0.0405*aa;
   aa=aa-4-bb;
   humi_zi=aa;
    humi_zi=(humi_zi-3.3)*10;
     xianzhi_h=(int)humi_zi;
}


void Timer_Init(void)
{
  TMOD|=0x01;   //置定时器0工作方式1
  EA=1;         //打开全局中断
  ET0=1;        //打开 定时器0 中断
  TR0=1;
}
void main(void)
{
  char delay_cnt = 0;
  LED = 0 ;
  RELAY = 0 ;
  LCD_Init();       //液晶初始化
  LCD_Clear();    //清屏
  LCD_Write_String(0,0,"Humi:00.0%");        //初始显示内容
  Timer_Init();      //定时器初始化
  while(1)
    {  
    if(updat)       //数更新
    {
      updat = 0 ;
      get_humidata();
      LCD_Write_Char(5,0,0x30+xianzhi_h/100%10);
      LCD_Write_Char(6,0,0x30+xianzhi_h/10%10);
      LCD_Write_Char(8,0,0x30+xianzhi_h%10);
    }
     } 
}






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分