1.1项目功能
(1)温度实时显示(LCD2864)
(2)温度上限下线调节
(3)万年历功能
(4)超温报警
(5)年月日时分秒可调节
(6)温度测量精度0.0625℃
(7)节日自动判定
1.2所需器材
STC89c52,DS18B20,DS1302,按键模块。LCD12864
1.3程序架构
1.4程序源码
main.c
#include#include "DS18B20.H" #include "LCD12864.H" #include "DS1302.h" #include uchar table1[]="现温度 "; uchar table2[]="上限值 度"; uchar table3[]="下限值 度"; uchar table4[]=" 警告温度超出 "; uchar table5[]=" 温度稳定 "; uchar table6[]="负温度- "; uchar table7[]="SpringFestival"; uchar table8[]="duanwuFestival"; uchar table10[]=" 温度计"; unsigned char Time_buf[7]={0x20,0x00,0x00,0x15,0x11,0x09,0x21};//年,秒,分,时,日,月,年;//时间缓冲数组 unsigned char tempbuf; //存储接收到的信息 unsigned char tt;//传输数据 unsigned char receiveData; sbit BUZZ = P2^6; //定义蜂鸣器引脚(起报警作用) sbit key1 = P3^3; //独立按键右一切换到温度显示界面 sbit key2 = P3^0; //上限加一--------bug原因(普中科技开发板与LCD12864液晶重合了) sbit key3 = P3^1; //左一切换到时间界面 sbit key4 = P3^2;//上限减一-----------bug原因(普中科技开发板与LCD12864液晶重合了) sbit deng=P2^2; //报警的光报警 void SetTime(void) { uint i; switch(array()) //矩阵按键扫描 { case 0: Time_buf[6] = Time_buf[6] + 0x01;break; //年+ case 8: Time_buf[5] = Time_buf[5] + 0x01;break; //月+ case 1: Time_buf[4] = Time_buf[4] + 0x01;break; //日+ case 9: Time_buf[3] = Time_buf[3] + 0x01;break; //时+ case 2: Time_buf[2] = Time_buf[2] + 0x01;break; //分+ case 4: Time_buf[6] = Time_buf[6] - 0x01;break; //年- case 12: Time_buf[5] = Time_buf[5] - 0x01;break; //月- case 5: Time_buf[4] = Time_buf[4] - 0x01;break; //日- case 13:Time_buf[3] = Time_buf[3] - 0x01;break; //时- case 6:Time_buf[2] = Time_buf[2] - 0x01;break; //分- default:; } for( i = 0; i < 7;i++) { if( (Time_buf[i] & 0x0F) > 9) { Time_buf[i] = ((Time_buf[i]+0x10)&0xF0); } } DS1302_Data_Input(DS1302_CONTROL_ADD,0x00); //关闭写保护 DS1302_Data_Input(DS1302_MIN_ADD,Time_buf[2]); //分 DS1302_Data_Input(DS1302_HR_ADD,Time_buf[3]); //时 DS1302_Data_Input(DS1302_DAY_ADD,Time_buf[4]); //日 DS1302_Data_Input(DS1302_MONTH_ADD,Time_buf[5]); //月 DS1302_Data_Input(DS1302_YEAR_ADD,Time_buf[6]); //年 DS1302_Data_Input(DS1302_CONTROL_ADD,0x80); //打开写保护 } // //串口通信初始化 void UART_init(void) { SCON = 0x50; //串口方式1 TMOD = 0x20; // 定时器使用方式2自动重载 TH1 = 0xFD; //9600波特率对应的预设数,定时器方式2下,TH1=TL1 TL1 = 0xFD; ES=1 ; EA=1 ; TR1 = 1;//开启定时器,开始产生波特率 } //---------------------------------------------------------------------------------- void main() { unsigned char sec_buf = 0x00; //用来保存上一次的秒数 uint i; int hight=27,low=20; //定义上限为27度,下限为20度。 uchar L, H; init_12864(); //12864初始化 DS1302_Init(); //DS1302初始化 DS1302_Write_Time(); //初始化数据 UART_init(); //串口通信初始化函数 get: print(1,0," 年 月 日"); print(2,0," 时 分 秒"); print(3,0," 长江大学"); while(1) { DS1302_Read_Time(); SetTime(); //设定时间 if(Time_buf[1] != sec_buf) //当前时间与原来时间不同 { sec_buf = Time_buf[1]; //把当前的秒数赋值给临时变量 display_int(1,0,ChangeTime(Time_buf[0])); //年 display_int(1,1,ChangeTime(Time_buf[6])); //年 display_int(1,3,ChangeTime(Time_buf[5])); //月 display_int(1,5,ChangeTime(Time_buf[4])); //日 display_int(2,1,ChangeTime(Time_buf[3])); //时 display_int(2,3,ChangeTime(Time_buf[2])); //分 display_int(2,5,ChangeTime(Time_buf[1])); //秒 } if((Time_buf[5]==1)&&(Time_buf[4]==1)) //春节 display(4,0,&table7); else if((Time_buf[5]==5)&&(Time_buf[4]==5)) //端午节 display(4,0,&table8); else display(4,0,&table10); if(key1 == 0)//切换到温度显示界面 { init_12864(); break;//按下切换到温度显示界面 } } //----------------------------------------------------------- //----------------------------------------------------------- //作用: 将三行汉子固定显示,不刷新 display(1,0,&table1); //在第1行显示 display(2,0,&table2); display(3,0,&table3); //========================================================= //温度显示↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ while(1) { Init_DS18B20(); //初始化DS18B20 Write_Byte(0xcc); //发送跳跃ROM指令 Write_Byte(0x44); //发送温度转换指令 Delay_8us(100); Init_DS18B20(); //初始化DS18B20 Write_Byte(0xcc); //发送跳跃ROM指令 Write_Byte(0xbe); //读取DS18B20暂存器值 L = Read_Byte(); //读高速暂存器的byte0(低字节,二进制数) H = Read_Byte(); //读高速暂存器的byte1(高字节,二进制数) i = H; //高8位赋值给i i <<= 8; // 低8位的数据整体左移8位 i |= L; // 高8位的数据填充低8位移走的空位,组成16位的二进制赋值给i if(i>0xfff)//温度为负数时候取反+1 { i=~i+1; display(1,0,&table6);//超过范围在其前面显示“——”表示负温度 } i = i * 0.0625 * 10 + 0.5; //读到的二进制数据转换温度的算法 // receiveData= convert(i); //转换为字符串 LcdDisplay(i); //温度显示函数 LcdDisplay_hight(hight); LcdDisplay_low(low); if((i>=hight*10)|(i<=low*10)) //温度高于上下限就报警 { display(4,0,&table4); //这里还可以加入呼吸灯进行报警,或者其他的一些报警功能 BUZZ = 0; //拉低蜂鸣器响 deng=0; } else { BUZZ=1; deng=1; display(4,0,&table5);//在第四行显示,即显示温度稳定(没有超过范围) } //--------------------------------------------------------------------- //按键调上限 if(array()==3) //当key2按下一次时,上限温度加一,一直按一直加 { hight++; } if(array()==7) //当key4按下一次时,上限温度减一,一直按一直减 { hight--; } //----------------------------------------------------------------------- //调下限 if(array()==11) //当key11按下一次时,下限限温度加一,一直按一直加 { low++; } if(array()==15) //当key15按下一次时,下限温度减一,一直按一直减 { low--; } //-------------------------------------------------------------------- if(key3 == 0) { Delay_8us(20);//软件延时 if(key3==0) {init_12864 (); goto get;} break;//按下切换到时间显示界面,但不初始化时间,接着开机时计算(后台走时) } } }
LCD12864.c (主要一些延时和LCD12864的驱动)
#include "lcd12864.h" #includevoid delay(uint x)//延时函数 { uint i; for(i=x;i>0;i--); } void Delay50ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); _nop_(); //i = 2; //j = 26; //k = 223; i=1; j=1; k=120; do { do { while (--k); } while (--j); } while (--i); } void writecommand(uchar command)//写指令函数,对应指令值为其参数command { check_busy(); E=0; //初始化使能端 RS=0;//选择命令 RW=0;//选择写入 out=command;//放置数据 E=1;//写时序 _nop_(); E=0; } void check_busy()//查忙(液晶查忙) { uchar dt; do { dt=0xff; E=0; RS=0;//选择命令 RW=1;//选择读取 E=1; dt=out; } while(dt&0x80); E=0; } void writedate (uchar date ) { check_busy(); //检测忙标志 E=0; //写数据时,E=1,所以先置E=0; RS=1; RW=0; out=date; //将数据写入P0口,即写入LCD E=1; _nop_(); //硬件反应时间 E=0; Delay50ms(); } void init_12864 () //初始化12864 { PSB=1;//并行 writecommand(0x30); //设定为基本功能 writecommand(0x0c); // 命令0x0c:设置开显示,不显示光标(开启整体显示) writecommand(0x06); // 命令0x06:写一个字符后地址指针加1,整屏显示不移动 writecommand(0x01); // 显示清屏 } void place(uchar x,uchar y)//指定位置 { uchar sum; if(x == 1) //第一行 x = 0x80; if(x == 2) //第二行 x = 0x90; if(x == 3) //第三行 x = 0x88; if(x == 4) //第四行 x = 0x98; sum = x + y; //x为行 ,y 该行为第几位 writecommand(sum); //将行数与列数整合 } void display(uchar x, uchar y, uchar *p) { place(x,y); //调用位置函数 while(*p != ' ') //判断是否到最后一个字符 { writedate(*p); //读取数据 p++; //指向向下一个元素 } } void LcdDisplay(int temp) //温度显示函数 { unsigned char datas[] ={9,8,2}; //数组存温度数据 unsigned char charper [6]= "摄氏度"; unsigned char i; datas[0] = temp / 100; //获取十位 datas[1] = temp % 100 / 10; //获取个位 datas[2] = temp % 10 ; //显示小数点后一位 writecommand(0x84); writedate('0'+datas[0]); SBUF='0'+datas[0]; while(!TI); TI=0; writecommand(0x85); writedate('0'+datas[1]); SBUF='0'+datas[1]; while(!TI); TI=0; writecommand(0x86); writedate('.'); SBUF='.'; while(!TI); TI=0; writecommand(0x87); writedate('0'+datas[2]); SBUF='0'+datas[2]; while(!TI); TI=0; for(i=0;i<6;i++) { SBUF=charper[i]; while(!TI); TI=0; } } void LcdDisplay_hight(int temp) //上限温度显示函数 { unsigned char datas[] ={0,0}; //数组上限存温度数据 datas[0] = temp / 10; //获取十位 datas[1] = temp % 10; //获取个位 writecommand(0x94); writedate('0'+datas[0]); writecommand(0x95); writedate('0'+datas[1]); } void LcdDisplay_low(int temp) //温度下限显示函数 { unsigned char datas[] ={0,0}; //数组下限存温度数据 datas[0] = temp / 10; //获取十位 datas[1] = temp % 10; //获取个位 writecommand(0x8c); writedate('0'+datas[0]); writecommand(0x8d); writedate('0'+datas[1]); } //---------------------------------------------------------------- void ad_ope(uchar x,uchar y)//地址选择 { uchar num; switch(x) { case 1: x=0x80; break; case 2: x=0x90; break; case 3: x=0x88; break; case 4: x=0x98; break; } num=x+y; writecommand(num); } void print(uchar x,uchar y,uchar *s)//输出 { uint i = 0; ad_ope(x,y); while(*s>0) { writedate(*s++); i++; delay(10); } if(i == 1) writedate(' '); //如果只有一位数就把这位数后面的一个空位覆盖为空格 } void display_int(uchar x,uchar y, int voltage)//整数输出 { unsigned char value[8]; sprintf(value, "%d", voltage); print(x,y,value); delay(1); } uchar array(void)//矩阵键盘,判断按键是否被按下,那个按键被按下 { uchar x,y,z; uchar key = 16; P1=0x0f; if(P1!=0x0f) { delay(2500);//软件延时 if(P1!=0x0f)//是否有按键按下 { x = P1&0x0f; //检测列 P1 = 0xf0; delay(2500);//软件延时 y = P1&0xf0;//接着检测行 z = x|y;//按位相与后判断具体那一位被按下(普中科技开发板对应的按键值加1既可) switch(z) { case 0xee: key=0; break; case 0xde: key=1; break; case 0xbe: key=2; break; case 0x7e: key=3; break; case 0xed: key=4; break; case 0xdd: key=5; break; case 0xbd: key=6; break; case 0x7d: key=7; break; case 0xeb: key=8; break; case 0xdb: key=9; break; case 0xbb: key=10; break; case 0x7b: key=11; break; case 0xe7: key=12; break; case 0xd7: key=13; break; case 0xb7: key=14; break; case 0x77: key=15; break; } } } return key; }
LCD12864.h (主要一些函数的声明)
#ifndef _lcd12864_H_ #define _lcd12864_H_ #include#include #include #define uchar unsigned char #define uint unsigned int #define out P0 sbit E=P2^7;//使能 sbit RW=P2^5;//读写 sbit RS=P2^6;//寄存器选择输入 sbit PSB=P3^2;//串并行方式选择,1并,0串 extern void Delay50ms(); extern void Delay100ms(); extern void condition(); extern void writecommand(); extern void check_busy(); extern void writedate (); extern void init_12864(); extern uchar array(void);//矩阵按键扫描 void place(uchar x,uchar y);//选择在12864显示位置 void display(uchar x, uchar y, uchar *p); void LcdDisplay(int temp); void LcdDisplay_hight(int temp); void LcdDisplay_low(int temp); extern void print(uchar,uchar,uchar*);//显示数据 //extern void display_float(uchar,uchar,float); extern void display_int(uchar,uchar, int); #endif
DS18B20.c(DS18B20驱动程序)
#include "DS18B20.H"
bit flag;
void Delay_8us(unsigned int us)//@11.0592MHz 粗略延时1ms
{
while(us--);
}
Init_DS18B20(void)
{ unsigned char x=0;
DQ = 1; //复位 先将数据线置高
Delay_8us(10); //延时一下
DQ = 0;
Delay_8us(80);
DQ = 1; //释放总线
Delay_8us(20); //延时120us 等待应答脉冲
x = DQ; //接受应答
Delay_8us(30);
return x;
}
void Write_Byte(unsigned char byte)
{
unsigned char i=0;
for(i=8;i>0;i--)
{
DQ=0;
DQ=byte&0x01;
Delay_8us(5);
DQ=1;
byte>>=1;
}
//return(byte);
}
unsigned char Read_Byte(void)
{
int i;
unsigned char byte = 0;
for(i=0;i<8;i++)
{
DQ = 0; //主机输出低电平
byte>>=1; //先读取低位,每次移一位直至读完一个字节
DQ =1;
if(DQ) //判断读到位是否为1
{
byte|=0x80; //若总线为1,即DQ为1,那就把byte的最高位置1;若为0,则不进行处理,保持为0
}
Delay_8us(4);
}
return byte;
}
DS18B20.h(作用请参考上述.h文件)
#ifndef _DS18B20_h_ #define _DS18B20_h_ #include "reg51.H" #includesbit DQ = P2^0; //DS18B20单总线,作为测温芯片输出口 extern void Delay_8us(unsigned int ms); extern Init_DS18B20(void); extern void Write_Byte(unsigned char byte); extern unsigned char Read_Byte(void); #endif
DS1302.c(时钟芯片的驱动程序)
#include "DS1302.h"
void DS1302_Init()
{
RST = 0; //片选,RST其实质相当于片选CE
SCLK = 0; 在sclk拉低的前提前才能拉高RST
}
void DS1302_Data_Input(unsigned char Address,unsigned char Data)
{
unsigned char i;
RST = 1;//启动DS1302总线
Address = Address & 0xFE;//最低位置0,寄存器0位时写,1时读,这时为写数据
for(i=0;i<8;i++)
{
SCLK = 0;
if(Address&0x01) //数据从低位开始传送
{
DAT = 1;
}
else
{
DAT = 0;
}
SCLK = 1;//上升沿写入指令
Address = Address>>1;
}
for(i=0;i<8;i++)
{
SCLK = 0;
if(Data&0x01)
{
DAT = 1;
}
else
{
DAT = 0;
}
SCLK = 1;//上升沿写入数据
Data = Data>>1;
}
SCLK = 0;
RST = 0;//关闭DS1302总线
}
unsigned char DS1302_Data_Output(unsigned char Address)
{
unsigned char i;
unsigned char temp;
RST = 1;
Address = Address | 0x01;
for(i=0;i<8;i++)
{
SCLK = 0;
if(Address&0x01)
{
DAT = 1;
}
else
{
DAT = 0;
}
SCLK = 1;
Address = Address>>1;
}
for(i=0;i<8;i++)
{
SCLK=0;
temp = temp >> 1;
if (DAT)
{
temp |= 0x80;
}
else
{
temp &= 0x7F;
}
SCLK=1;
}
SCLK=0;
RST = 0;
return temp;
}
void DS1302_Write_Time(void)
{
DS1302_Data_Input(DS1302_CONTROL_ADD,0x00); //关闭写保护,允许数据写入寄存器,数据内容为0(写入允许)
DS1302_Data_Input(DS1302_SEC_ADD,0x80); //CH位置高,振荡器停止,置低,振荡器工作允许
DS1302_Data_Input(DS1302_CHARGER_ADD,0xa9);
DS1302_Data_Input(DS1302_SEC_ADD,Time_buf[1]); //秒
DS1302_Data_Input(DS1302_MIN_ADD,Time_buf[2]); //分
DS1302_Data_Input(DS1302_HR_ADD,Time_buf[3]); //时
DS1302_Data_Input(DS1302_DAY_ADD,Time_buf[4]); //日
DS1302_Data_Input(DS1302_MONTH_ADD,Time_buf[5]); //月
DS1302_Data_Input(DS1302_YEAR_ADD,Time_buf[6]); //年
DS1302_Data_Input(DS1302_CONTROL_ADD,0x80); //打开写保护
}
void DS1302_Read_Time(void)
{
Time_buf[1]=(DS1302_Data_Output(DS1302_SEC_ADD))&0x7f; //秒,屏蔽秒的第7位,避免超出59
Time_buf[2]=DS1302_Data_Output(DS1302_MIN_ADD); //分
Time_buf[3]=DS1302_Data_Output(DS1302_HR_ADD)&0x3F; //时 3f -- 0011 1111
Time_buf[4]=DS1302_Data_Output(DS1302_DAY_ADD); //日
Time_buf[5]=DS1302_Data_Output(DS1302_MONTH_ADD); //月
Time_buf[6]=DS1302_Data_Output(DS1302_YEAR_ADD); //年
}
unsigned char ChangeTime(unsigned char tim)
{
unsigned char Time;
if((tim&0x0F)>9) //如果个位超过9,则十位加一后,个位清零
{
tim=((tim+0x10)&0xF0); //0x4A+0x10=0x5A&0xF0=0x50
} //如果没有则跳过该部分
Time = (tim>>4)*10+(tim&0x0f); //把8421码转变成10进制的数
//0x50>>4=0x05*10=50,0x50&0x0f=0x00=0 ;50
return Time; //返回这个数的值
}
DS1302.h
#ifndef __DS1302_H__ #define __DS1302_H__ #include#include #include #include #include "lcd12864.h" sbit SCLK = P3^6;//串行时钟输入端口 sbit DAT = P3^4;//数据IO端口 sbit RST = P3^5;//复位输入端 #define DS1302_SEC_ADD 0x80//秒(写秒寄存器) #define DS1302_MIN_ADD 0x82//分(写分寄存器) #define DS1302_HR_ADD 0x84//时(写时寄存器) #define DS1302_DAY_ADD 0x86//日(同理) #define DS1302_MONTH_ADD 0x88//月 #define DS1302_YEAR_ADD 0x8C//年 #define DS1302_CONTROL_ADD 0x8E//控制 #define DS1302_CHARGER_ADD 0x90//涓流充电 #define DS1302_CLKBURST_ADD 0xBE//时钟多字节 extern unsigned char Time_buf[7];//={0x20,0x20,0x13,0x15,0x17,0x05,0x20};//秒,分,时,日,月,年;//时间缓冲数组 extern void DS1302_Init(); //初始化DS1302 extern void DS1302_Data_Input(unsigned char Address,unsigned char Data); //数据输入 extern unsigned char DS1302_Data_Output(unsigned char Address); //数据输出 extern void DS1302_Write_Time(void); //写入初始化时间 extern void DS1302_Read_Time(void); //读取时间 extern unsigned char ChangeTime(unsigned char tim); #endif
1.5成果展示
需要工程文件可以留言



