"智能检测,精准把控。温湿度检测系统,为您的生活带来全方位的健康保障。"#非标协议项目【上】
前言
** 本篇博文介绍的是用51单片机的非标准写协议项目温湿度检测系统【上】,包含温湿度检测系统需求,代码整合,构造DHT11温湿度数据处理函数,主函数调用API顺序,完整工程代码,项目运行结果。看到这篇博文的朋友,可以先赞再看吗?**
预备知识
** 一、基本电路标识识别和接线,例如VCC,GND。**
** 二、电脑基本操作复制粘贴**
** 三、C变量
四、基本输入输出
五、流程控制
六、函数**
** 七、指针
八,字符串**
** 如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!**
1温湿度检测系统需求
- 能够实时通过51单片机串口发送温湿度信息
- 能够实时通过51单片机驱动LCD1602显示温湿度信息
2.代码整合
2.1找到编程实现LCD1602显示一行工程
,打开代码文件
,将所需的LCD1602驱动代码
拷贝到温湿度检测系统_1602显示串口发送工程
内。
- LCD1602 IO口定义代码
c
//数据线定义
//D0到D7 -- P0.1到P0.7
#define dataBuffer P0
//控制线定义
/*
RS--P1.0
RW--P1.1
EN--P1.4
*/
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
- LCD1602初始化必要延时代码
c
void Delay15ms() //@11.0592MHz
{
unsigned char i, j;
i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}
- 检测LCD1602忙代码
c
void checkBusy()
{
char tmp = 0x80;
dataBuffer = 0x80;
while(tmp & 0x80) //只要dataBuffer中的第8位为1就为忙,因为BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
{
RS = 0; //根据手册中指令表可知RS=0,RW=1
RW = 1; //RW=1为读的操作,所以需要配置读的时序图
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
EN = 1; //根据手册中的读时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
tmp = dataBuffer; //读取dataBuffer中的忙信号
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
}
- 向LCD1602写地址也就是指令代码和写数据代码
c
void writeCmd(char cmd)
{
checkBusy(); //检测忙信号
RS = 0; //RS等于零,是写指令操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cmd; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void weiteData(char cData)
{
checkBusy(); //检测忙信号
RS = 1; //RS等于1,是写数据操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cData; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
- 初始化LCD1602代码
c
void initLCD1602()
{
//(1) 延时 15ms
Delay15ms();
//(2) 写指令 38H(不检测忙信号)
writeCmd(0x38);
//(3) 延时 5ms
Delay5ms();
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置
writeCmd(0x38);
//(6) 写指令 08H:显示关闭
writeCmd(0x08);
//(7) 写指令 01H:显示清屏
writeCmd(0x01);
//(8) 写指令 06H:显示光标移动设置
writeCmd(0x06);
//(9) 写指令 0CH:显示开及光标设置
writeCmd(0x0C);
}
- 让LCD1602显示一行字符的代码
c
void LCD1602ShowARow(char line,char column,char *str)
{
switch(line) //选择哪行显示
{
case 1: //第一行显示
writeCmd(0x80 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数
while(*str) //判断字符串指针变量不为空执行循环,不用判断字符串结束标志
{
weiteData(*str); //发送字符串单个字符
str++; //字符串指针变量偏移
}
break;
case 2: //第二行显示
writeCmd(0x80 + 0x40 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数,又因为第二行的列从0x40开始
while(*str) //所以是从0x80 + 0x40 + 列数(特别注意)
{
weiteData(*str);
str++;
}
break;
}
}
- 最后将以上代码整合到
温湿度检测系统_1602显示串口发送工程
内。
2.2找到串口编程03_PC发送指令控制LED
工程,打开代码文件
,将所需的LCD1602驱动代码
拷贝到温湿度检测系统_1602显示串口发送工程
内。
- 串口初识化代码
c
void UartInit(void) //自己配
{
//配置串口工作方式为方式1,从只收不发改为能收能发
SCON = 0x50;
//配置辅助寄存器,减少电磁辐射,稳定晶振频率
AUXR = 0x01;
//设置定时器工作方式为定时器1的8位自动重装
TMOD &= 0x0F;
TMOD |= 0x20;
//设置串口波特率为9600,0误差
TH1 = 0xFD;
TL1 = 0xFD;
//打开定时器1
TR1 = 1;
}
- 发送字节和发送字符串代码
c
void sendByte(char data_mas)
{
SBUF = data_mas;
while(!TI);
TI = 0; //一定要软件置零,不然会出现乱序
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
2.3找到温湿度通过串口传到PC显示
工程,打开代码文件
,将所需的LCD1602驱动代码
拷贝到温湿度检测系统_1602显示串口发送工程
内。
- 存放温湿度数据代码
c
char THdata[5]; //存放温湿度数据变量
- 打开DHT11高速模式和读取DHT11温湿度数据必要延时代码
c
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay40us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 15;
while (--i);
}
- 打开DHT11高速模式代码
c
void startDHT()
{
Data = 1;
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
Data = 1;
//检测d点
while(Data);
//检测e点
while(!Data);
//检测f点
while(Data);
}
- 读取DHT11温湿度数据代码
c
void readDHTData()
{
char i; //轮次
char j; //次数
char flag;
char tmp;
//打开DHT11高速模式
startDHT();
for(i=0; i<5; i++)
{
for(j=0; j<8; j++)
{
//检测G点
while(!Data);
//根据传送1和0的时间不同,0是26us,1是70us。等待60us后,如果Data = 1,就传1,Data = 0;就传0
//Delay60us(); 延时60微妙太长了,可能在读0时读到下一个发送序列了,延时40微妙
Delay40us();
if(Data == 1)
{
flag = 1;
while(Data); //传1的时间比较久,所以要等传1结束
}
else
{
flag = 0;
}
tmp = tmp << 1;
tmp |= flag;
}
THdata[i] = tmp;
}
}
3.构造DHT11温湿度数据处理函数
3.1定义存放温度
的字符数组
和存放湿度
的字符数组
c
char Temperature[9]; //定义温度字符数组
char Humidness[9]; //定义湿度字符数组
3.2在DHT11温湿度数据处理函数内将温湿度数据存入相应字符数组。
c
void THDataHandler()
{
//湿度数据处理
Humidness[0] = 'H';
Humidness[1] = ':';
Humidness[2] = THdata[0]/10 + 0x30;
Humidness[3] = THdata[0]%10 + 0x30;
Humidness[4] = '.';
Humidness[5] = THdata[1]/10 + 0x30;
Humidness[6] = THdata[1]%10 + 0x30;
Humidness[7] = '%';
Humidness[8] = '\0';
//温度处理
Temperature[0] = 'T';
Temperature[1] = ':';
Temperature[2] = THdata[2]/10 + 0x30;
Temperature[3] = THdata[2]%10 + 0x30;
Temperature[4] = '.';
Temperature[5] = THdata[3]/10 + 0x30;
Temperature[6] = THdata[3]%10 + 0x30;
Temperature[7] = 'C';
Temperature[8] = '\0';
}
4.主函数调用API顺序
4.1延时1秒,稳定系统
c
Delay1000ms();
4.2初始化串口
c
UartInit();
4.3初始化LCD1602
c
initLCD1602();
4.4延时2秒,稳定DHT11模块
c
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
4.5在while(1)
死循环内调用延时一秒函数,实现每隔1秒刷新温湿度数据
c
Delay1000ms();
4.6在while(1)
死循环内调用读取DHT11温湿度函数
c
readDHTData();
4.7在while(1)
死循环内调用DHT11温湿度数据处理函数
c
THDataHandler();
4.8在while(1)
死循环内多次调用发送字符串函数,实现向PC发送温湿度数据
c
//通过串口发送温湿度数据
sendString(Humidness);
sendString("\r\n");
sendString(Temperature);
sendString("\r\n");
sendString("\r\n");
4.9在while(1)
死循环内多次调用LCD1602显示一行字符函数,实现在LCD1602上显示温湿度数据
c
LCD1602ShowARow(1,4,Humidness);
LCD1602ShowARow(2,4,Temperature);
5.完整工程代码
c
#include "reg52.h"
#include "intrins.h"
//数据线定义
//D0到D7 -- P0.1到P0.7
#define dataBuffer P0
sbit LED1 = P3^7; //用尾定义声明LED1
sbit Data = P3^3; //把DHT11接在单片机的P1.0口
sfr AUXR = 0x8e; //声明AUXR寄存器地址
//控制线定义
/*
RS--P1.0
RW--P1.1
EN--P1.4
*/
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
char THdata[5]; //存放温湿度数据变量
char Temperature[9]; //定义温度字符数组
char Humidness[9]; //定义湿度字符数组
void Delay15ms() //@11.0592MHz
{
unsigned char i, j;
i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay40us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 15;
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);
}
void checkBusy()
{
char tmp = 0x80;
dataBuffer = 0x80;
while(tmp & 0x80) //只要dataBuffer中的第8位为1就为忙,因为BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
{
RS = 0; //根据手册中指令表可知RS=0,RW=1
RW = 1; //RW=1为读的操作,所以需要配置读的时序图
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
EN = 1; //根据手册中的读时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
tmp = dataBuffer; //读取dataBuffer中的忙信号
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
}
void writeCmd(char cmd)
{
checkBusy(); //检测忙信号
RS = 0; //RS等于零,是写指令操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cmd; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void weiteData(char cData)
{
checkBusy(); //检测忙信号
RS = 1; //RS等于1,是写数据操作
RW = 0; //RW在手册中写的时序图是可以全程低电平
EN = 0; //根据手册中的写时序图,EN开始为低电平
_nop_(); //执行空函数等待EN拉高
dataBuffer = cData; //根据手册中的写时序图,数据在EN还在低电平时就建立数据
EN = 1; //根据手册中的写时序图,EN被拉高
_nop_(); //执行空函数等待tpw和tF高电平过程
_nop_();
EN = 0; //根据手册中的写时序图,EN被拉低
_nop_(); //根据手册中的写时序图,让EN保持一段时间的低电平
}
void initLCD1602()
{
//(1) 延时 15ms
Delay15ms();
//(2) 写指令 38H(不检测忙信号)
writeCmd(0x38);
//(3) 延时 5ms
Delay5ms();
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置
writeCmd(0x38);
//(6) 写指令 08H:显示关闭
writeCmd(0x08);
//(7) 写指令 01H:显示清屏
writeCmd(0x01);
//(8) 写指令 06H:显示光标移动设置
writeCmd(0x06);
//(9) 写指令 0CH:显示开及光标设置
writeCmd(0x0C);
}
void UartInit(void) //自己配
{
//配置串口工作方式为方式1,从只收不发改为能收能发
SCON = 0x50;
//配置辅助寄存器,减少电磁辐射,稳定晶振频率
AUXR = 0x01;
//设置定时器工作方式为定时器1的8位自动重装
TMOD &= 0x0F;
TMOD |= 0x20;
//设置串口波特率为9600,0误差
TH1 = 0xFD;
TL1 = 0xFD;
//打开定时器1
TR1 = 1;
}
void sendByte(char data_mas)
{
SBUF = data_mas;
while(!TI);
TI = 0; //一定要软件置零,不然会出现乱序
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
void startDHT()
{
Data = 1;
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
Data = 1;
//检测d点
while(Data);
//检测e点
while(!Data);
//检测f点
while(Data);
}
void LCD1602ShowARow(char line,char column,char *str)
{
switch(line) //选择哪行显示
{
case 1: //第一行显示
writeCmd(0x80 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数
while(*str) //判断字符串指针变量不为空执行循环,不用判断字符串结束标志
{
weiteData(*str); //发送字符串单个字符
str++; //字符串指针变量偏移
}
break;
case 2: //第二行显示
writeCmd(0x80 + 0x40 + column); //选择哪个位置显示,因为位置地址的最高位必须为1,所以0x80+列数,又因为第二行的列从0x40开始
while(*str) //所以是从0x80 + 0x40 + 列数(特别注意)
{
weiteData(*str);
str++;
}
break;
}
}
void readDHTData()
{
char i; //轮次
char j; //次数
char flag;
char tmp;
//打开DHT11高速模式
startDHT();
for(i=0; i<5; i++)
{
for(j=0; j<8; j++)
{
//检测G点
while(!Data);
//根据传送1和0的时间不同,0是26us,1是70us。等待60us后,如果Data = 1,就传1,Data = 0;就传0
//Delay60us(); 延时60微妙太长了,可能在读0时读到下一个发送序列了,延时40微妙
Delay40us();
if(Data == 1)
{
flag = 1;
while(Data); //传1的时间比较久,所以要等传1结束
}
else
{
flag = 0;
}
tmp = tmp << 1;
tmp |= flag;
}
THdata[i] = tmp;
}
}
void THDataHandler()
{
//湿度数据处理
Humidness[0] = 'H';
Humidness[1] = ':';
Humidness[2] = THdata[0]/10 + 0x30;
Humidness[3] = THdata[0]%10 + 0x30;
Humidness[4] = '.';
Humidness[5] = THdata[1]/10 + 0x30;
Humidness[6] = THdata[1]%10 + 0x30;
Humidness[7] = '%';
Humidness[8] = '\0';
//温度处理
Temperature[0] = 'T';
Temperature[1] = ':';
Temperature[2] = THdata[2]/10 + 0x30;
Temperature[3] = THdata[2]%10 + 0x30;
Temperature[4] = '.';
Temperature[5] = THdata[3]/10 + 0x30;
Temperature[6] = THdata[3]%10 + 0x30;
Temperature[7] = 'C';
Temperature[8] = '\0';
}
void main()
{
LED1 = 1; //一上电就让灯灭
Delay1000ms();
UartInit(); //初始化串口
initLCD1602(); //初始化LCD1602
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
while(1) //防止程序退出主函数,导致LED1微弱闪烁
{
Delay1000ms(); //间隔1秒读一次
readDHTData(); //读取DHT11温湿度数据
THDataHandler(); //将DHT11传回的数据装入温湿度字符数组
//通过串口发送温湿度数据
sendString(Humidness);
sendString("\r\n");
sendString(Temperature);
sendString("\r\n");
sendString("\r\n");
LCD1602ShowARow(1,4,Humidness);
LCD1602ShowARow(2,4,Temperature);
}
}
6项目运行结果

结束语
** 很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!**