Bootstrap

基于51单片机,蓝牙控制,LCD-1602显示的温湿度控制系统

这里我们将要实现用51单片机来控制温湿度模块,蓝牙,LCD1602显示的功能。

没有蓝牙的参与控制时,当温度高于29℃,打开风扇散热,低于29℃,关闭风扇。如果温度低于29℃,我们在蓝牙中输入OP打开风扇,他会旋转3秒,然后关闭;如果温度高于29℃,我们在蓝牙中输入CL关闭风扇,他会关闭1秒,然后继续工作,直到温度低于29℃后,关闭风扇。

温湿度模块初始化

从图上我们可以看出来要想完成dht11的初始化,需要让dht对应的IO口发生电位变化才可以触发。VCC代表高电平,GND代表低电平。

    dht=1;
    dht=0;
    Delay30ms();//图上说明至少需要18ms,我们直接给它30ms
    dht=1;
    while(dht);高电平时间在20~40us,但是到底是多少,我们谁也不知道,因此我们用while循环,直到dht=0退出循环。同理,下面的两个循环对应着两次跳变。
    while(!dht);
    while(dht);

温湿度模块的通讯全过程

红线左侧是温湿度模块的初始化,右侧是通讯过程

下图中我们可以看到,模块想要发送0,则再在开始传送数据(即50us之后),高电平要持续26us~28us,模块想要发送1,则再在开始传送数据(即50us之后),高电平要持续70us

     int i;
    int j;
    char temp;
    char flag;
    DHT_Init();
    for(i=0;i<5;i++)
    {
        for(j=0;j<8;j++)
        {
            while(!dht);//完成低电平到高电平的跳变
            Delay40us();
            if(dht==1)//延迟40us,如果dht变为低电平,说明dht发送0;如果dht还是为高电平,说明dht发送1 
            {
                flag=1;
                while(dht);//等到dht变为低电平,退出循环,为下一位做准备
            }
            else
                {
                    flag=0;    
                }
                temp = temp << 1;//最高位不断变化,不断左移
                temp|=flag;//右边低位空出的0不断被flag代替,产出我们所需要的5个8位数据
        }
        datas[i]=temp;//datas数组在全部的程序你会看到,我们要存5组数据,分别代表着下图的五部分
    }

      

LCD16202模块

检查忙状态

说明书告诉我们,RS=0,RW=1时,可以读取忙信号

char temp=0x80;
    databuffer=0x80;
    while(temp&0x80)//为什么是0x80?因为0x80对应1000 0000,上面的说明书告诉我们要检查最高位D7,如果是高电平则表示忙,则不断循环;如果是低电平,则表示不忙,可以退出循环,执行别的命令。
    {
        RS=0;//从读的图上,我们可以看出,想要读数据的话寻要满足RS=0,RW=1至少30ns,EN要完成从低电平到高电平的跳变,且低电平至少持续25ns,(一个_nop_()时间是1.085ns,即一个指令周期,完全够用,要是不放心,你可以像我一样,再加一个)
    RW=1;
    EN=0;
    _nop_();
        _nop_();
    EN=1;
    _nop_();    高电平持续150ns
    temp=databuffer;//databuffer是p0口的八位,头文件有说明,将p0口的八位赋值给temp,第一次是0x80,但是请仔细看读操作时序图,p0口的八位数字是具有时效性的,EN低电平过后,20ns,这八位值就作废了,全部变为零。
    EN=0;
    _nop_();
    }

写入指令/数据 

写入指令或者数据,本质上是一样的,分析方法同上不再赘述,但是要说明一点,仔细看看时序图,就可以注意到在EN=0或在EN=1时均可写数据,不必疑惑,而读只能发生在EN=1.其余的大家看我后面的代码和时序图就可以理解了。

要强调的是一个骚操作,是执行写的时候,不不你是写数据还是写命令,倒要注意在你原来的数值上机上0x80,因为说明书上说的很清楚,在写的时候D7恒为1。在LCD_1602.C的LCD1602_showline(char row,char col,char*string)函数里我会有具体的操作。

初始化

除了延时函数,其余的指令通过电泳指令函数写入对应的指令即可完成初始化

main.c

#include "reg52.h"
#include "intrins.h"
#include "delay.h"
#include "uart.h"
#include "dth11.h"
#include "LCD_1602.h"
#include "config.h"

char temper[8];
char huma[8];
extern char datas[5];

void Bulid_Datas(char *datas)
{
    huma[0]='H';
    huma[1]=datas[0]/10+0x30;//为什么要加0x30,我们得到的datas里的值是数字0,1,2等,但是显示屏上显示的是字符。所有的数字与对应的字符都相差48,即0x30
    huma[2]=datas[0]%10+0x30;//例如通过查ASCII码表,可以看出0与字符‘0’相差48
    huma[3]='.';
    huma[4]=datas[1]/10+0x30;
    huma[5]=datas[1]%10+0x30;
    huma[6]='%';
    huma[7]='\0';
    
    temper[0]='T';
    temper[1]=datas[2]/10+0x30;
    temper[2]=datas[2]%10+0x30;
    temper[3]='.';
    temper[4]=datas[3]/10+0x30;
    temper[5]=datas[3]%10+0x30;
    temper[6]='%';
    temper[7]='\0';
}

void main()
{
    Delay1000ms();
    UartInit();
    LCD1602_Init();
    Delay1000ms();
  led1=0;
    while(1)
    {
    
        Delay1000ms();
        Read_data();

        Bulid_Datas( datas);
        sendString(huma);//串口发送湿度
        sendString("\r\n");
        sendString(temper);//串口发送温度
        sendString("\r\n");
        LCD1602_showline(1,2,huma);
        LCD1602_showline(2,2,temper);
    }
}

uart.c

#include "uart.h"

char buffer[10];//不要定义的过大,否则RAM中无法存储别的变量
void UartInit(void) //[email protected]
{
AUXR = 0x01;
SCON = 0x50; //配置串口工作方式1,REN使能接收
TMOD &= 0x0F;
TMOD |= 0x20;//定时器1工作方式位8位自动重装,低八位
TH1 = 0xFD;
TL1 = 0xFD;//9600波特率的初值
TR1 = 1;//启动定时器
EA = 1;//开启总中断
ES = 1;//开启串口中断
}

void sendByte(char data_msg)//通过写SBUF输入一个字符
{
    SBUF = data_msg;
    while(!TI);
    TI = 0;
}

void sendString(char* str)//输入字符串,并且每输入一个字符就会触发中断
{
    while( *str != '\0'){
        sendByte(*str);
        str++;
    }
}
void Uart_Hangdler() interrupt 4
{
    static int i=0;//静态变量,被初始化一次
    char tmp;
    if(RI==1)//中断处理函数中,对于接收中断的响应
        {
            RI=0;//清除接收中断标志位
            tmp = SBUF;
      if(tmp == 'O'||tmp == 'C')
            {
      i = 0;
      }
      buffer[i++] = tmp;
            if(i==5)
            {
                i=0;
            }
        if(buffer[0] == 'O' && buffer[1] == 'P')//输入OP打开风扇
            {
         Fengshang=0;
                
         memset(buffer,'\0', 5);
      }

        if(buffer[0] == 'C' && buffer[1] == 'L')//输入CL关闭风扇
            {
         Fengshang=1;
                
         memset(buffer,'\0',5);
      }
       } 
}
 


LCD_1602.C

#include "LCD_1602.h"

void check_busy()//检查忙状态,
{
    char temp=0x80;
    databuffer=0x80;
    while(temp&0x80)
    {
        RS=0;
    RW=1;
    EN=0;
    _nop_();
        _nop_();
    EN=1;
    _nop_();    
    temp=databuffer;
    EN=0;
    _nop_();
    }
}
void Write_Cmd(char cmd)//写入指令
{
    check_busy();
    RS=0;
    RW=0;
    EN=0;
    _nop_();
    _nop_();
    EN=1;
    databuffer=cmd;//p0口接收命令
    _nop_();
    EN=0;
    _nop_();
}

void Write_data(char datashow)//写入数据
{
    check_busy();
  RS=1;
    RW=0;
    EN=0;
    _nop_();
    _nop_();
    EN=1;
    databuffer=datashow;//p0口接收数据
    _nop_();
    EN=0;
    _nop_();
}
void LCD1602_Init()
{
    Delay15ms();
  Write_Cmd(0X38);
    Delay5ms();
     Write_Cmd(0X38);
     Write_Cmd(0X08);
     Write_Cmd(0X01);
     Write_Cmd(0X06);
     Write_Cmd(0X0c);
    
}

void LCD1602_showline(char row,char col,char*string)//显示温度和湿度,row代表行,col代表列
{
    switch(row)
{
    case 1:
         Write_Cmd(0x80+col);//执行写命令的时候,最高位D7强制是1,因此要加0x80
    while(*string)
    {
        Write_data(*string);
        string++;
    }
        break;
    case 2:
        Write_Cmd(0x80+0x40+col);//执行写命令的时候,最高位D7强制是1,因此要加0x80,同时第二行的起始地址是0x40要加上才可以表示第二行
    while(*string)
    {
        Write_data(*string);
        string++;
    }
        
        break;
}
}

dth11.c

#include "dth11.h"
char datas[5];//需要一个一维数组储存5个八位数据,上面的分析说过了,模块原理图理有

void DHT_Init()//温湿度模块初始化
{
    dht=1;
    dht=0;
    Delay30ms();
    dht=1;
    while(dht);
    while(!dht);
    while(dht);
}
void Read_data()
{
    
    int i;
    int j;
    char temp;
    char flag;
    DHT_Init();
    for(i=0;i<5;i++)
    {
        for(j=0;j<8;j++)
        {
            while(!dht);
            Delay40us();
            if(dht==1)
            {
                flag=1;
                while(dht);
            }
            else
                {
                    flag=0;    
                }
                temp = temp << 1;
                temp|=flag;
        }
        datas[i]=temp;
    }
      EA=1;//外部中断1触发中断的条件
        EX1=1;
       IT1=0;
       wbzd1=1;
        if((datas[2]<29)&&(Fengshang==0))//温度低于29,但是蓝牙输入OP,触发中断打开风扇3秒后关闭风扇
        {
            Delay1000ms();
            Delay1000ms();
            Delay1000ms();
            Fengshang=1;
        }
     if((datas[2]>29)&&(Fengshang==1))//温度高于29,但是蓝牙输入CL,触发中断关闭风扇1秒后打开风扇
        {
            Delay1000ms();
            Fengshang=0;
        }
}
void DTH_Hangdler() interrupt 2//中断里面先将数据判断,防止数据出现问题死机
{   
    wbzd1=0;
    
     if(datas[2]>29)
        {
            Fengshang=0;
        }
        else
        {
            Fengshang=1;
        }
}


delay.c

#include "delay.h"

void Delay40us()        //@11.0592MHz
{
    unsigned char i;

    _nop_();
    i = 15;
    while (--i);
}
void Delay5ms()        //@11.0592MHz
{
    unsigned char i, j;

    i = 9;
    j = 244;
    do
    {
        while (--j);
    } while (--i);
}

void Delay15ms()        //@11.0592MHz
{
    unsigned char i, j;

    i = 27;
    j = 226;
    do
    {
        while (--j);
    } while (--i);
}

void Delay30ms()        //@11.0592MHz
{
    unsigned char i, j;

    i = 54;
    j = 199;
    do
    {
        while (--j);
    } while (--i);
}

void Delay1000ms()        //@11.0592MHz
{
    unsigned char i, j, k;

    _nop_();
    i = 8;
    j = 1;
    k = 243;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

uart.h

#ifndef _UART_H
#define _UART_H
#include "reg52.h"
#include "config.h"

sfr  AUXR=0x8E;
sbit D5=P3^6;
void UartInit(void);
void sendByte(char data_msg);
void sendString(char* str);
#endif

LCD_1602.h

#ifndef _LCD_1602_H
#define _LCD_1602_H

#include "reg52.h"
#include "intrins.h"
#include "delay.h"
#define databuffer P0
sbit RW=P1^1;
sbit EN=P1^4;
sbit RS=P1^6;
void check_busy();
void Write_Cmd(char cmd);
void Write_data(char datashow);
void LCD1602_Init();
void LCD1602_showline(char row,char col,char*string);
#endif

dth11.h

#ifndef _DTH11_H
#define _DTH11_H
#include "reg52.h"
#include "delay.h"
sbit dht=P3^4;
sbit wbzd1=P3^3;//外部中断1触发IO口

void DHT_Init();
void Read_data();

#endif

#endif

config.h

#ifndef _CONFIG_H
#define _CONFIG_H
#include<reg52.h>
sbit Fengshang=P1^0;
sbit led1 = P3^7;
#endif

最后烧录程序,打开单片机和蓝牙进行控制。

;