这里我们将要实现用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
最后烧录程序,打开单片机和蓝牙进行控制。