利用焊接完的电路板完成软件的设计,本次选的题目为温度控制器与蓝牙通信,最终达到手机端能收到温度控制器测得的实时温度,其中温度控制器的核心部件是DS18B20芯片和LCD1602A液晶显示芯片,蓝牙通信的核心部件是HC-08蓝牙串口通信模块,DS18B20芯片用来实现温度测量功能;LCD1602A液晶显示芯片用来实现测量温度显示功能,HC-08蓝牙串口通信模块作为上位安卓机同52单片机通信的桥梁,达到单片机能将所测得温度发送给上位机,上位机能向单片机发送一些指令的效果。
本次所选的题目,温度控制器实现了实时显示当前温度功能,可以设置上限阈值,即温度报警值,当温度达到阈值时蜂鸣器会响起滴答滴答的声音,同时LCD显示屏右上角显示出“W”代表进入报警状态此时的温度控制器仍然正常工作,当温度低于阈值时解除报警状态。除此之外,还设计了一个APP通过蓝牙模块同单片机通信,可以达到通过手机就能查看当前单片机测得的温度,查看阈值,调整阈值的功能。此项目综合实现了多方面的功能,运用了多个芯片和组件,对单片机知识的掌握是很好的一次考察,通过此项目的实现,提高了我对单片机知识的综合掌握程度。
具体实现温度控制器主要涉及到的硬件资源有STC89C52RC、LCD1602A显示屏芯片、DS18B20芯片、蜂鸣器、HC-08蓝牙模块。此次实验搭建的蓝牙通信APP使用的是基AppInventor2服务器的WXbit图像化编程工具,能够较为轻松的搭建出一个APP。
逻辑设计
按照既定计划,本项目的流程为,先实现读取DS18B20模块数据,送给LCD1602模块显示,再之后就是最困难的通信模块构建,本次是基于蓝牙模块实现单片机同上位机(安卓手机)之间的通信,这就需要考虑到它们之间波特率的问题,由于此次实验通过的单片机晶振是12Mhz,12Mhz晶振通信起来误差是非常大的,这是由于其并计算初值时存在的误差。而市面上的蓝牙模块其默认波特率大都为9600,采用9600波特率,单片机根本无法同蓝牙模块建立稳定的通信,所以就要求使用AT指令调整波特率,调整到4800,并且波特率翻倍的情况下才能实现误差为0.16%的通信,较为可靠。
其中最头疼的就是数据如何存储发送了,因为串口一次只能发送八位数据,而获取的温度值是有小数的,所以我尝试了两种方案,一是将数据分为整数部分和小数部分,分两次发送,二是将数据存为字符数组,直接发送字符数组,实践证明,第二种效果较好。
蓝牙模块同51系列单片机通信
最重要的方面就是波特率设置啦,这个方面刚开始做的时候说实话踩了不少坑,因为学校电子设计提供的板子上面的晶振是12Mhz,总所周知,12Mhz的晶振通信起来误差是非常大的,这是由于其并计算初值时存在的误差。
最开始,我采用了杨老师提供的蓝牙模块,调试开始!经过一晚上的摸索,始终没能成功通信,这个时候仔细查阅发现,这个蓝牙模块波特率是固定9600的,而12Mhz的晶振产生9600的波特率,误差会达到5%左右,这是无法接受的,所以理所应当的通信不上。然后我在我的另外一个11.0592Mhz的单片机上面尝试成功。真的头很大,搞了一晚上还以为是单片机串口坏了。
之后,我购买了一个波特率可调的蓝牙模块,提供AT指令将蓝牙模块的波特率调为了4800,(经过查阅资料,发现当51单片机波特率调为4800时,波特率翻倍)这个时候误差会来到0.16%,这个误差能勉强接受,经过调试之后,通过手机上的蓝牙调试软件控制单片机P1口灯开关,成功!!!
然后就开始思考如何完成自己想做。
具体代码//主函数 #include#include "DS18b20.h" #include "LCD1602.h" char RECEIVED_CMD; unsigned char flag = 0 ; // 数据接收的标志位 extern unsigned int tvalue; //温度值 extern unsigned char tflag; //温度正负标志 unsigned char disdata[7]; // 温度数据,使用8字节数组来存储 unsigned char chg[3]; //存放阈值 unsigned int wrong = 30; void UART_Init(); // 初始化串口 void UART_SendData(char dat); // 串口发送数据 void UART_SendStr(char* str); // 串口发送字符串 void ds1820disp(); // 温度显示 void change(); //转化阈值为字符数组 void main() { unsigned int temperature , old ; // 保存温度数值 int A=5000; UART_Init(); // 串口初始化 LCD_Init(); // 显示屏 初始化 LCD_ShowString(1,1,"Temperature"); P1_4 = 0; temperature = ReadTemperature(); old = temperature ; ds1820disp(); // 转换温度 LCD_ShowString(2,1,disdata); LCD_ShowNum(2,13,wrong,2); while(1){ temperature=ReadTemperature(); // 读取一次新的温度 LCD_ShowNum(2,13,wrong,2); //2行13列显示wrong值 LCD_ShowChar(2,16,'C'); if (temperature != old ) { old = temperature; ds1820disp(); // 转化温度 LCD_ShowString(2,1,disdata); // 显示温度 } if(temperature > wrong * 10) { P1_4 = !P1_4; //发出报警声 while(A--); A=5000; LCD_ShowChar(1,16,'W'); } else { LCD_ShowChar(1,16,'N'); P1_4 = 0; } if(flag) // 接收数据完毕一次,就会进入中断一次{ flag = 0 ; // 将标志位还原,使得串口又可以重新接收数据 if(RECEIVED_CMD == '0') { UART_SendStr(disdata);//向串口发送数据,发送的是当前温度 } else if(RECEIVED_CMD == '3') { change(); UART_SendStr(chg); //发送阈值 } else if(RECEIVED_CMD == '1') { wrong ++ ;//阈值加 } else if(RECEIVED_CMD == '2'){ wrong -- ;//阈值减 } RECEIVED_CMD =' '; } } } //串口初始化 void UART_Init() { SCON=0x50; PCON |= 0x80; TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x20; //设置定时器模式 TL1 = 0xF3; //设定定时初值 TH1 = 0xF3; //设定定时器重装值 ET1 = 0; //禁止定时器1中断 TR1 = 1; //启动定时器1 EA=1; ES=1; //打开两个外部中断 IT0 = 1; IT1 = 1; EX0 = 1; EX1 = 1; } void UART_Isr() interrupt 4 using 1 { // 串口接收中断处理 if(RI) { RI = 0 ; // 清除中断标志位 RECEIVED_CMD = SBUF ; // 保存串口接收的数据 flag = 1 ; // 接收结束,到循环中处理接收的数据 } // 串口发送中断处理 if(TI) { TI = 0 ; // 清发送中断标志位 } } //开关K1 void int0() interrupt 0 { wrong ++; } //开关K2 void int1() interrupt 2 { wrong --; } //通过串口发送一位数据 void UART_SendData(char dat) { ES = 0 ; // 串口工作的时候禁止中断 SBUF = dat ; // 待发送的数据放到SBUF中 while(!TI) ; // 等待发送完毕 TI = 0 ; // 清TI中断 ES = 1 ; // 打开中断 } //发送字符串 void UART_SendStr(char *str) { do{ UART_SendData(*str); }while(*str ++ != ' ' ); // 一直到字符串结束 } //温度转化函数,将测得的温度值转化为字符数组存放 void ds1820disp(){ unsigned char flagdat; if(tflag==0) flagdat=0x2b;//正温度显示符号 else flagdat=0x2d;//负温度显示负号:- disdata[1]=tvalue/1000+0x30;//百位数 disdata[2]=tvalue%1000/100+0x30;//十位数 disdata[3]=tvalue%100/10+0x30;//个位数 disdata[4]= 0x2E ;//小数点 disdata[5]=tvalue%10/1+0x30;//小数位 if(disdata[1]==0x30) // 如果百位为0{ disdata[0]= 0x20; // 第一位不显示 disdata[1]= flagdat; // 百位显示符号 if(disdata[2]==0x30) //如果百位为0,十位为0{ disdata[1]=0x20; // 百位不显示符号 disdata[2]=flagdat; // 10位显示符号 } } } //转换阈值 void change(){ chg[0] = wrong / 10 + 0x30; chg[1] = wrong % 10 + 0x30; }
//温度测量模块
#include "DS18b20.h"
unsigned int tvalue;//温度值
unsigned char tflag;//温度正负标志
//延时函数
void delay(unsigned int i){
while(i--);
}
//初始化DS18B20
void Init_DS18B20(void){
unsigned char x=0;
DQ = 1; //让DQ置1
delay(8);
DQ = 0; //DQ拉低
delay(80); //延时480-960us
DQ = 1; //释放总线
delay(14);
x=DQ;
delay(20);
}
unsigned char ReadOneChar(void){
unsigned char i=0;
unsigned char dat = 0;
for (i=8;i>0;i--) //循环八次得出数据{
DQ = 0; //发送启动信号
dat>>=1;
DQ = 1; //释放总线
if(DQ) //判断是否高电平
dat|=0x80; //若是dat最高位置1,不是则置0
delay(4);
}
return(dat);}
void WriteOneChar(unsigned char dat){
unsigned char i=0;
for (i=8; i>0; i--) //循环八次得出数据{
DQ = 0; //DQ先置低电平
DQ = dat&0x01; //取最低位
delay(5);
DQ = 1;
dat>>=1; //由低位向高位发送数据
}
}
int ReadTemperature(void)
{
unsigned char a=0;
unsigned char b=0;
Init_DS18B20(); //启动DS18B20
WriteOneChar(0xCC); //跳过读序列号的操作
WriteOneChar(0x44); //启动温度转化
Init_DS18B20(); //启动DS18B20
WriteOneChar(0xCC); //跳过读序列号的操作
WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器)前两个是温度
a=ReadOneChar(); //读取温度低位
b=ReadOneChar(); //读取温度高位
tvalue = b; //处理数据
tvalue <<= 8; //高位左移八位
tvalue = tvalue|a;
if(tvalue<0x0fff) //小于0x0fff为正数
tflag=0;
else{
tvalue=~tvalue+1; //取反加一
tflag=1; //负数
}
tvalue = tvalue*(0.625);//温度值扩大10倍,精确到1位小数
return(tvalue);
}
具体项目:https://github.com/KrealHtz/Studywork



