利用焊接完的电路板完成软件的设计,本次选的题目为温度控制器与蓝牙通信,最终达到手机端能收到温度控制器测得的实时温度,其中温度控制器的核心部件是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 ++ != '\0' ); // 一直到字符串结束
}
//温度转化函数,将测得的温度值转化为字符数组存放
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