通过几天的调试用,单片机AT89C52,LCD液晶屏LC1602,温度采集模块DS18B20,时钟模块DS1302,和三个按键,以及一些电子元件完成了数字时钟系统的设计。在一块液晶显示屏上实现动态,时、分、秒、年、月、日、星期、温度数据的显示, 通过三个按键,S1调时键、S2时间加键、S3时间减键、实现时钟的年、月、日、星期、时、分、秒、的调整。本系统设计大部分功能有软件来实现,使电路简单明了。由于以前从没有过单片机相关产品的制作经验,所以这个设计共用了约30多个钟。在做这设计之前软件思路非常模糊代码都是想到哪写到哪,结果写到最后调试时出现了大把错误,在调试程序中花了大把的时间,调程序真的是个考验耐心的工作搞的我砸电脑的想法都有了。因为是自己一个人制作的出现了错误只好在网上找资料,阅读别人写过的代码,经过长时间的修改终于完成了这次制作。设计因为是业余学习的单片机加上这断时间工作比较忙,所以为了完成这个产品的制作可没少睡过觉,几乎每天都没在2点钟前睡过。附原代码
- #include"reg52.h"
- #include"12864.h"
- #include"ds1302.h"
- #include"ds18b20.h"
- #include"intrins.h"//_nop_声明
-
- #define uchar unsigned char
- #define uint unsigned int
- sbit DQ=P3^7;
- sbit rs=P3^0;
- sbit rw=P3^1;
- sbit en=P3^2;
- uchar tou;
-
- sbit io=P3^6;
- sbit rst=P3^4; //定义各芯片跟单片机连接的定义
- sbit sck=P3^5;
- sbit s1=P1^0; //定义各芯片跟单片机连接的定义
- sbit s2=P1^4;
- sbit s3=P1^7; //定义各芯片跟单片机的连接的定义
- uchar tung=0; //定义
- uchar tab4[]={0x82,0x85,0x88,0x8f,0xc0,0xc3,0xc6};//用来存放年月日星期时分秒的地址
- char tab[]={13,5,4,5,00,59,33};//年 月 日 周 时 分 秒的初值
- uchar tab1[]; //
- uchar write_add[7]={0x8c,0x88,0x86,0x8a,0x84,0x82,0x80}; //写寄存器地址
- uchar read_add[7]={0x8d,0x89,0x87,0x8b,0x85,0x83,0x81};//存放从1302中读数寄存器地址
- void write_byte(uchar); //写数据函数
- uchar read_byte(void); //读数据函数
- uchar readtemperature(void);//一次性读出温度
-
- void write_ds1302_byte(uchar); //单字节写函数
- void write_ds1302(uchar add,uchar dat);//写 /写地址数据+写数据函数
- uchar read_ds1302(uchar);// 单字节读函数
- void flx(uchar j); //
- void key(void); //按键函数
- void write_ds1302_byte(uchar dat)
- {
- uchar i;
- for(i=0;i<8;i++) //单字节写
- {
- sck=0;
- io=dat&0x01;
- dat=dat>>1;
- sck=1;
- }
-
- }
- void write_ds1302(uchar add,uchar dat)
- {
- rst=0;
- _nop_();
- sck=0;
- _nop_();
- rst=1; //写地址数据+写数据函数
- _nop_();
- write_ds1302_byte(add); //写地址
- write_ds1302_byte(dat); //写数据
- rst=0;
- _nop_();
- io=1;
- sck=1;
-
- }
- uchar read_ds1302(uchar add)
- { uchar i,value;
- rst=0; //
- _nop_();
- sck=0;
- _nop_(); //单字节读数据函数
- rst=1;
- _nop_();
- write_ds1302_byte(add );
- for(i=0;i<8;i++)
- {
- value=value>>1;
- sck=0;
- if(io) //因为是串口所以要分8次读数据
- value=value|0x80;
- sck=1;
- }
- rst=0;
- _nop_();
- sck=0;
- _nop_();
- sck=1;
- io=1;
- return value;
- }
- void read_ds1302_1(void)//一次读出年月日 时分秒函数
- {
- uchar i,shi,ge,tun;
- write_ds1302(0x8e,0x00);//去除写保护
- for(i=0;i<7;i++)
- {
- tun=read_ds1302(read_add[i]); //一次将年月日从DS1302中读出送到数组中
- ge=tun%16;
- shi=tun/16; //将BCD码分成十位和个位
- tab[i]=shi*10+ge; //再换成十进制存到数组tab中
-
- }
- write_ds1302(0x8e,0x00);//加写保护
- }
- void write_ds1302_1(void)
- { //将10进制数送到1602;
- write_com(0x80+0x40+6);//秒的位置
- write_dat(tab[6]/10+0x30);
- write_dat(tab[6]%10+0x30);
-
- write_com(0x80+0x40+3);//
- write_dat(tab[5]/10+0x30); //分的位置
- write_dat(tab[5]%10+0x30);
- //将十进制的数据换成1602的显示码送到显示屏
- write_com(0x80+0x40+0);// //时的地址
- write_dat(tab[4]/10+0x30); //显示屏的第二排要加0x80+40
- write_dat(tab[4]%10+0x30);
-
- write_com(0x80+2);//
- write_dat(tab[0]/10+0x30);//年的地址
- write_dat(tab[0]%10+0x30);
-
- write_com(0x80+5);// 显示屏 的第一排地址要加0x80;
- write_dat(tab[1]/10+0x30);
- write_dat(tab[1]%10+0x30); //月的地址
-
- write_com(0x80+8);// 日的地址
- write_dat(tab[2]/10+0x30);
- write_dat(tab[2]%10+0x30);
-
- write_com(0x80+15); //星期的地址
- write_dat(tab[3]%10+0x30);
-
-
-
-
- }
- void set_rtc(void) //一次设置初始时间年月日函数
- {
- uchar i,j;
-
- for(i=0;i<7;i++)
- {
- j=tab[i]/10; //将设置的年月日时间2013—4-25 00-14-00中分解出十位放入J中
- tab1[i]=tab[i]%10; //将设置的年月日时间2013—4-25 00-14-00中分解出个位再放入time_data[]数组中
- tab1[i]=tab1[i]+j*16;//将十进制的个位和十位转换为BCD码
-
- }
- write_ds1302(0x8e,0x00);//去除写保护
- for(i=0;i<7;i++)
- {
- write_ds1302(write_add[i],tab1[i]); //写要设置初始时间日期的操作寄存器的地址
- }
- write_ds1302(0x8e,0x00);//加写保护
- }
-
- void ds1302_init(void)
- {
-
-
-
- key(); //按键函数
- read_ds1302_1();//一次读出年月日 时分秒函数
- write_ds1302_1(); //时间的显示函数
-
- }
- void key(void)
- {
- //按键函数
- if(s1==0) //如果S1按下
- {
- delay(3); //延时去抖
- if(s1==0)
- {
- while(!s1); //
- tung=0; //tung用来确定光标的位置
-
- while(tung<=6) //循环7次 时分秒年月日星期
- {
-
-
- if(s2==0)
- {
- delay(3);
- if(s2==0)//去抖动延时
- {
- while(!s2); // 检测按键是否放开
- tab[tung]++; //如果+键按下加1
- }
- }
- if(s3==0)
- {
- delay(3); // 检测按键是否放开
- if(s3==0)
- {
- while(!s3); //按键是否松手
- tab[tung]--; //如果—键按下减1
- }
- }
-
- switch(tung)
- { //通过tung的值确定光标的位置
- case 0:
- if(tab[tung]<3) //如果tung=0;则表示正在调年让年在2004到2098这个范围内
- tab[tung]=98;
- if(tab[tung]>98)
- tab[tung]=4;
- break;
-
- case 1:
- if(tab[tung]<1)
- tab[tung]=12;
- if(tab[tung]>12) //如果tung=1;则表示正在调月让月在1到12这个范围内
- tab[tung]=1;
- break;
- case 2:
- if(tab[tung]==0)
- tab[tung]=31;
- if(tab[tung]==32) //如果tung=2;则表示正在调日让在1到31这个范围内
- tab[tung]=1;
- break;
- case 3:
- if(tab[tung]<1)
- tab[tung]=7;
- if(tab[tung]>7) //如果tung =3则表示下在调星期让在1到7这个范围内
- tab[tung]=1;
- break;
- case 4:
- if(tab[tung]<0)
- tab[tung]=23;
- if(tab[tung]>23) //如果tung=4则表示在调时让时在1到23这个范围内
- tab[tung]=0;
- break;
- case 5:
- if(tab[tung]<0)
- tab[tung]=59;
- if(tab[tung]>59) //如果tung=5;则表示正在调分让分在1到59这个范围内
- tab[tung]=0;
- break;
- default:
- if(tab[tung]<0)
- tab[tung]=59;
- if(tab[tung]>59) //如果tung=6;则表示正在调秒让秒在1到59这个范围内
- tab[tung]=0;
- break;
- }
- write_com(tab4[tung]); //显示正在调的位置
- flx(tab[tung]); //将数据分离成1602的显示码并送到1602显示屏
-
-
- if(s1==0)
- {
- while(!s1); //判断是否调好了时间如果好了则跳出key
- tung++; //每按一下S1调式模式键则加一次如果到了7次跳出KEY函数
- }
-
- }
-
- }
- write_com(0x0c);//调完了时间关闭光标
- set_rtc(); //把调好的时间数据送到DS1302芯片中
- }
- }
- void flx(uchar j)
- {
- uchar k,a;
- write_com(0x0f); //开光标显示指令
- k=j/10; //分离十位跟个位
- a=j%10;
- if(tung!=3) //年月日时分秒分离
- {
-
- write_dat(k+0x30); //将分离的十位跟个位换成LC1602能显示的码
- write_com(0x0c);//关光标指令
- write_dat(a+0x30);
-
- }
- else
-
- write_dat(a+0x30); //星期分离
-
- }
-
- void delay1(uchar i)
- {
- while(i--);
- }
- void init_ds18b20(void)
- {
- uchar n;
- DQ=1;
- delay1(8);
- DQ=0;
- delay1(80); //初始化函数
- DQ=1;
- delay1(8);
- n=DQ;
- delay(25);
- }
- void write_byte(uchar dat)
- {
- uchar i;
- for(i=0;i<8;i++)
- {
- DQ=0; //写函数
- DQ=dat&0x01;
- delay(4);
- DQ=1;
- dat>>=1; //移一位
-
- }
- delay(4);
- }
- uchar read_byte(void)
- {
- uchar i,value;
- for(i=0;i<8;i++)
- {
- DQ=0; //读数据函数
- value>>=1; //移1位
- DQ=1;
- if(DQ)
- value|=0x80;
- delay(4);
- }
- return value;
- }
- uchar readtemperature(void)//一次性读出温度
- {
- uchar a,b;
- init_ds18b20();
- write_byte(0xcc);//跳过ROM
- write_byte(0x44);//启动温度测量
- delay1(300);
- init_ds18b20(); //初始化
-
- write_byte(0xcc);
- write_byte(0xbe);//读取温度
- a=read_byte();//高8位
- b=read_byte();//
-
- b<<=4; //因不用小数部分所以移4位
- b+=(a&0xf0)>>4; //将存放低8位的个位和高8位的十位合到一块
- return b;
- }
- void xianshi(void)
- {
- uchar temp,k,f;
- temp=readtemperature();//将温度保存到temp中
- f=temp/10; //分离出十位跟个位
- k=temp%10;
- write_com(0x80+0x40+10);//温度显示的位置
- write_dat(f+0x30); //
- write_dat(k+0x30);
-
-
- }
- uchar tab1[]="20 - - week "; //屏上要固定显示的字符
- uchar tab2[]=" : : ";
- void delay(uint x)
- {
- uint a,b;
- for(a=x;a--;a>0) //延时函数
- for(b=110;b>0;b--);
- }
- void write_com(uchar com)
- {
- rs=0;
- rw=0; //写数据指令函数
- P2=com;
- delay(4);
- en=1;
- delay(4);
- en=0;
- }
- void write_dat(uchar dat)
- {
- rs=1;
- rw=0;
- P2=dat;
- delay(4); //写数据函数
- en=1;
- delay(4);
- en=0;
-
- }
- void init(void)
- {
- write_com(0x38);
- write_com(0x0c);// //1602初始化函数
- write_com(0x06);
- write_com(0x01);
- write_com(0x80);
- write_com(0x80); //第一行显示的位置地址
- for(tou=0;tou<16;tou++)
- {
- write_dat(tab1[tou]); //显示固定字符
- }
- write_com(0x80+0x40); //第二行显示的位置地址
- for(tou=0;tou<8;tou++)
- {
- write_dat(tab2[tou]);
-
- }
- write_com(0x80+0x40+12);
- write_dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码
- write_dat(0x43); //显示"C"符号,0x43是液晶屏字符库里大写C的地址码
- }
- main()
- {
- init();//初始化显示屏
- set_rtc(); //设置ds1302时钟芯片时间
- while(1)
- {
- ds1302_init();//初始化ds1302时钟芯片
- xianshi(); //温度显示函数
-
- }
-
-
- }
- 通过这次的制作让我全面的了解单片机相关产品的开发流程,及一些常用芯片驱动程序的编写。始我在单片机上的学习更上一层楼!
复制代码
|