基于51单片机的可掉电走表万年历设计
前言
之前跟随郭天祥老师学习51单片机的时候就很想实现掉电走表这个功能,奈何那时候跟随视频学的还是非常浅显,到了这个这期刚好老师需要做一个万年历所以接触到了DS1302时钟模块以至于用了差不多10天时间每天捉摸一点点模块拼凑,终于完成这个功能,虽还有瑕疵,恳请各位指出我的不足和漏洞
下面的是效果演示,采用锂电池供电
使用到的模块
- LCD12864液晶
- 独立按钮
- DS18B20温度模块
- DS1302时钟模块
- LM7805降压模块和18650锂电池
本项目声明的变量
本文以模块化的方式阐述如何一步步丰富功能,担心下面有些模块里面的变量名读者看不懂现在先将其全部写出来,读者可以看了下面的再回来看上面的变量声明
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
/*12864端口定义*/
#define LCD_data P0 //数据口
sbit LCD_RS = P3^5; //寄存器选择输入
sbit LCD_RW = P3^6; //液晶读/写控制
sbit LCD_EN = P3^4; //液晶使能控制
sbit LCD_PSB = P3^7; //串/并方式控制
sbit beep=P2^3; //蜂鸣器
sbit LED=P1^0; //灯泡
sbit s1=P3^2; //设置按钮
sbit s2=P2^1; //+1按钮
sbit s3=P2^0; //-1按钮
//ds1302端口
sbit TSCLK = P1^1;//串行时钟输入端
sbit TIO = P1^2;//双向输入线(数据线)
sbit TRST = P1^3;//使能端
//温度的
sbit ds=P2^2; //温度传感器信号线
uint temp;
float f_temp;
void init_T1(void);//温度用的定时器
uchar code dis1[] = "20 -- -- 周 ";
uchar dis3[] = "室内温度:";
uchar code dis4[] = "开发作者:Ramsey ";
//测试 开始
uchar code tab1[]={"时 分 秒"};
uchar num=0,i,h=23,m=00,s=00,hl,hr,ml,mr,sl,sr,s1num,n=1;
uint run,year=2021,month=11,day=13,y1,y2,y3,y4,mol,mor,dl,dr;
uchar testnum,Flag=0;
uchar week=0;
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
uchar IRDIS[2];
uchar IRCOM[4];
//变量定义
uchar count,s1num=0;
uchar testcount;
char miao,shi,fen;
void lcd_pos(uchar X,uchar Y); //确定显示位置
void delay0(uchar x); //x*0.14MS
void beepon();
void display();
void dataconv();
void lcd_wcmd(uchar cmd);
void lcd_wdat(uchar dat);
//温度函数声明
void dsreset(void);
bit tmpreadbit(void); //read a bit
bit tempreadbit(void);
uchar tempread(void);
void tempwritebyte(uchar dat);
void tempchange(void);
uint get_temp();
void tempdisplay();
程序用到的初始化函数
/*********************************************************/
/* */
/* 定时器尝试使用初始化 */
/* 这里用了2个定时器,一个走表一个刷新温度 */ 、
/*********************************************************/
void init_T1(void)
{
TMOD=0x11;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
EA=1;//开总中断
ET0=1;//开定时器0中断
ET1=1;
TR0=1;//启动定时器0
TR1=1;//启动定时器1
//外部中断
EX0=1;
IT0=1;
}
LCD12864液晶代码
/*12864端口定义*/
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
#define LCD_data P0 //数据口
sbit LCD_RS = P3^5; //寄存器选择输入
sbit LCD_RW = P3^6; //液晶读/写控制
sbit LCD_EN = P3^4; //液晶使能控制
sbit LCD_PSB = P3^7; //串/并方式控制
uchar IRDIS[2];
uchar IRCOM[4];
uchar code dis1[] = "20 -- -- 周 ";//预留的空位是留来显示的
uchar dis3[] = "室内温度:";
uchar code dis4[] = "开发作者:Ramsey ";
uchar code tab1[]={"时 分 秒"};
/*******************************************************************/
/* */
/*检查LCD忙状态 */
/*lcd_busy为1时,忙,等待。lcd-busy为0时,闲,可写指令与数据。 */
/* */
/*******************************************************************/
bit lcd_busy()
{
bit result;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
delayNOP();
result = (bit)(P0&0x80);
LCD_EN = 0;
return(result);
}
/*******************************************************************/
/* */
/*写指令数据到LCD */
/*RS=L,RW=L,E=高脉冲,D0-D7=指令码。 */
/* */
/*******************************************************************/
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
_nop_();
_nop_();
P0 = cmd;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}
/*******************************************************************/
/* */
/*写显示数据到LCD */
/*RS=H,RW=L,E=高脉冲,D0-D7=数据。 */
/* */
/*******************************************************************/
void lcd_wdat(uchar dat)
{
while(lcd_busy());
LCD_RS = 1;
LCD_RW = 0;
LCD_EN = 0;
P0 = dat;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}
/*******************************************************************/
/* */
/* LCD初始化设定 */
/* */
/*******************************************************************/
void lcd_init()
{
LCD_PSB = 1; //并口方式
lcd_wcmd(0x34); //扩充指令操作
delay(5);
lcd_wcmd(0x30); //基本指令操作
delay(5);
lcd_wcmd(0x0C); //显示开,关光标
delay(5);
lcd_wcmd(0x01); //清除LCD的显示内容
delay(5);
//
lcd_wcmd(0x06); //清除LCD的显示内容
delay(5);
}
/*********************************************************/
/* */
/* 设定显示位置 */
/* */
/*********************************************************/
void lcd_pos(uchar X,uchar Y)
{
uchar pos;
if (X==0)
{X=0x80;}
else if (X==1)
{X=0x90;}
else if (X==2)
{X=0x88;}
else if (X==3)
{X=0x98;}
pos = X+Y ;
lcd_wcmd(pos); //显示地址
}
/*******************************************************************/
/* */
/* LCD显示函数 */
/* */
/*******************************************************************/
void display(){
uchar i;
delay(10);
lcd_pos(0,0); //设置显示位置为第一行的第1个字符
i = 0;
while(dis1[i] != '\0')
{ //显示字符
lcd_wdat(dis1[i]);
i++;
}
//暂时把第二行的隔开来\
//第二行的汉字
lcd_wcmd(0x92);
for(i=0;i<10;i++)
{
lcd_wdat(tab1[i]);
delay(10);
}
/* lcd_pos(1,0); //设置显示位置为第二行的第1个字符
i = 0;
while(dis2[i] != '\0')
{
lcd_wdat(dis2[i]); //显示字符
i++;
}
*/
//把第三行业隐藏起来
/*
lcd_pos(2,0); //设置显示位置为第三行的第1个字符
i = 0;
while(dis3[i] != '\0')
{
lcd_wdat(dis3[i]); //显示字符
i++;
}
*/
lcd_pos(3,0); //设置显示位置为第四行的第1个字符
i = 0;
while(dis4[i] != '\0')
{
lcd_wdat(dis4[i]); //显示字符
i++;
}
}
上述是LCD12864的基础显示内容下面需要根据具体时间写数据到对应的位置,具体位置代码详情看LCD12864说明文档,下面不在赘述
/*******************************************************************/
/* */
/* 计算时间变化,每次改变就要引用一次这个方法 */
/* */
/*******************************************************************/
void change() //时间变化
{
//时分秒的
hl=shi/10;
hr=shi%10;
ml=fen/10;
mr=fen%10;
sl=miao/10;
sr=miao%10;
//年月日的
y1=year/1000;
y2=year/100%10;
y3=year/10%10;
y4=year%10;
mol=month/10;
mor=month%10;
dl=day/10;
dr=day%10;
}
/*******************************************************************/
/* */
/* 时间显示函数 */
/* */
/*******************************************************************/
void display1() //时间显示函数
{
change();
//测试开始
//年
lcd_wcmd(0x80); //数字显示位置
lcd_wdat(y1+48);
delay1(10);
lcd_wdat(y2+48);
delay1(10);
lcd_wdat(y3+48);
delay1(10);
lcd_wdat(y4+48);
//月
lcd_wcmd(0x83);
lcd_wdat(mol+48);
delay1(10);
lcd_wdat(mor+48);
delay1(10);
//日
lcd_wcmd(0x85);
lcd_wdat(dl+48);
delay1(10);
lcd_wdat(dr+48);
delay1(10);
//测试结束
//时
delay1(10);
lcd_wcmd(0x91);
lcd_wdat(hl+48);
delay1(10);
lcd_wdat(hr+48);
//分
delay1(10);
lcd_wcmd(0x93);
lcd_wdat(ml+48);
delay1(10);
lcd_wdat(mr+48);
//秒
delay1(10);
lcd_wcmd(0x95);
lcd_wdat(sl+48);
delay1(10);
lcd_wdat(sr+48);
delay1(10);
//搞完复位
lcd_wcmd(0x97);
lcd_wdat(0x20);
lcd_wdat(0x20);
delay1(10);
}
上述代码就已经将年月日和时分秒的数据填入,现在需要根据年月日来计算今天是星期几
算法如下:
基姆拉尔森计算公式
W= (d+2m+3(m+1)/5+y+y/4-y/100+y/400) mod 7在公式中d表示日期中的日数,m表示月份数,y表示年数。
注意:在公式中有个与其他公式不同的地方:
把一月和二月看成是上一年的十三月和十四月,例:如果是
2004-1-10则换算成:2003-13-10来代入公式计算。
以公元元年为参考,公元元年1月1日为星期一
基姆拉尔森计算公式引用地址
/*******************************************************************/
/* */
/* 基姆拉尔森计算星期几 */
/* */
/*******************************************************************/
int CaculateWeek(int y, int m, int d) {
int tempweek;
if (m == 1 || m == 2) {
m += 12;
--y;
}
tempweek = (d + 2 * m + 3 * (m + 1) / 5 + y + (y >> 2) - y / 100 + y / 400) % 7 + 1;//加一为了后面方便计算
return tempweek;
}
/*******************************************************************/
/* */
/* 写入星期几到屏幕里面 */
/* */
/*******************************************************************/
void displayweek()
{
week=CaculateWeek(year,month,day);
switch(week)
{
case 1:
lcd_wcmd(0x87);
lcd_wdat(0XD2);
lcd_wdat(0XBB);
delay1(10);
break;
case 2:
lcd_wcmd(0x87);
lcd_wdat(0XB6);
lcd_wdat(0XFE);
delay1(10);
break;
case 3:
lcd_wcmd(0x87);
lcd_wdat(0XC8);
lcd_wdat(0XFD);
delay1(10);
break;
case 4:
lcd_wcmd(0x87);
lcd_wdat(0XCB);
lcd_wdat(0XC4);
delay1(10);
break;
case 5:
lcd_wcmd(0x87);
lcd_wdat(0XCE);
lcd_wdat(0XE5);
delay1(10);
break;
case 6:
lcd_wcmd(0x87);
lcd_wdat(0XC1);
lcd_wdat(0XF9);
delay1(10);
break;
case 7:
lcd_wcmd(0x87);
lcd_wdat(0XC8);
lcd_wdat(0XD5);
delay1(10);
break;
}
}
独立按钮代码
独立按钮的需要实现的功能是对时间进行调整,我用了3个按钮分别是1个设置按钮和2个用来加减时间的按钮,设置按钮将其优先级设置最高,只有设置按钮按下的时候,加减按钮才会有效,因此作者将设置按钮的代码放在外部中断,具体请看下面的代码
sbit s1=P3^2; //设置按钮
sbit s2=P2^1; //+1按钮
sbit s3=P2^0; //-1按钮
/*******************************************************************/
/* */
/* 键盘检测函数 */
/* */
/*******************************************************************/
void keyScan(){
LCD_RW=0;
if(testnum!=0)
{
if(s2==0)
{
delay1(5);
if(s2==0)
{
beepon();
//testnum++;
while(!s2);
if(testnum==1)
{
miao++;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x80,dat_to_BCD(miao));//秒
write_DS1302(0x8e,0x80);//开写保护
//
if(miao==60)
miao=0;
change();
//秒
delay1(10);
lcd_wcmd(0x95);
lcd_wdat(sl+48);
delay1(10);
lcd_wdat(sr+48);
lcd_wcmd(0x95);
}
if(testnum==2)
{
fen++;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x82,dat_to_BCD(fen));//分
write_DS1302(0x8e,0x80);//开写保护
delay1(100);
//
if(fen==60)
fen=0;
change();
//分
delay1(10);
lcd_wcmd(0x93);
lcd_wdat(ml+48);
delay1(10);
lcd_wdat(mr+48);
lcd_wcmd(0x93);
}
if(testnum==3)
{
shi++;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x84,dat_to_BCD(shi));//时
write_DS1302(0x8e,0x80);//开写保护
//
if(shi==24)
shi=0;
change();
//时
delay1(10);
lcd_wcmd(0x91);
lcd_wdat(hl+48);
delay1(10);
lcd_wdat(hr+48);
delay1(10);
lcd_wcmd(0x91);
}
//年月日的加减
if(testnum==4)
{
day++;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x86,dat_to_BCD(day));//时
write_DS1302(0x8e,0x80);//开写保护
//
if(day==31)
day=1;
change();
//日
delay1(10);
lcd_wcmd(0x85);
lcd_wdat(dl+48);
delay1(10);
lcd_wdat(dr+48);
delay1(10);
lcd_wcmd(0x85);
//写星期
displayweek();
lcd_wcmd(0x85);
}
if(testnum==5)
{
month++;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x88,dat_to_BCD(month));//时
write_DS1302(0x8e,0x80);//开写保护
//
if(month==13)
month=1;
change();
//月
delay1(10);
lcd_wcmd(0x83);
lcd_wdat(mol+48);
delay1(10);
lcd_wdat(mor+48);
delay1(10);
lcd_wcmd(0x83);
//写星期
displayweek();
lcd_wcmd(0x83);
}
if(testnum==6)
{
year++;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x8c,dat_to_BCD(year%100));//年
write_DS1302(0x8e,0x80);//开写保护
//
if(year==2040)
year=2021;
change();
//年
delay1(10);
lcd_wcmd(0x81); //数字显示位置
lcd_wdat(y3+48);
delay1(10);
lcd_wdat(y4+48);
lcd_wcmd(0x81);
//写星期
displayweek();
lcd_wcmd(0x81);
}
//测试
//displayweek();
}
}
//后面减法的代码
if(s3==0)
{
delay1(5);
if(s3==0)
{
beepon();
while(!s3);
if(testnum==1)
{
miao--;
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x80,dat_to_BCD(miao));//秒
write_DS1302(0x8e,0x80);//开写保护
if(miao==-1)
miao=59;
change();
//秒
delay1(10);
lcd_wcmd(0x95);
lcd_wdat(sl+48);
delay1(10);
lcd_wdat(sr+48);
lcd_wcmd(0x95);
}
if(testnum==2)
{
fen--;
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x82,dat_to_BCD(fen));//分
write_DS1302(0x8e,0x80);//开写保护
if(fen==-1)
fen=59;
change();
//分
delay1(10);
lcd_wcmd(0x93);
lcd_wdat(ml+48);
delay1(10);
lcd_wdat(mr+48);
lcd_wcmd(0x93);
}
if(testnum==3)
{
shi--;
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x84,dat_to_BCD(shi));//时
write_DS1302(0x8e,0x80);//开写保护
if(shi==-1)
shi=23;
change();
//时
delay1(10);
lcd_wcmd(0x91);
lcd_wdat(hl+48);
delay1(10);
lcd_wdat(hr+48);
delay1(10);
lcd_wcmd(0x91);
}
//测试开始
if(testnum==4)
{
day--;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x86,dat_to_BCD(day));//时
write_DS1302(0x8e,0x80);//开写保护
if(day==-1)
day=30;
change();
//日
delay1(10);
lcd_wcmd(0x85);
lcd_wdat(dl+48);
delay1(10);
lcd_wdat(dr+48);
delay1(10);
lcd_wcmd(0x85);
//写星期
displayweek();
lcd_wcmd(0x85);
}
if(testnum==5)
{
month--;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x88,dat_to_BCD(month));//时
write_DS1302(0x8e,0x80);//开写保护
if(month==-1)
month=12;
change();
//月
delay1(10);
lcd_wcmd(0x83);
lcd_wdat(mol+48);
delay1(10);
lcd_wdat(mor+48);
delay1(10);
lcd_wcmd(0x83);
//写星期
displayweek();
lcd_wcmd(0x83);
}
if(testnum==6)
{
year--;
//
write_DS1302(0x8e,0);//清除写保护
write_DS1302(0x8c,dat_to_BCD(year%100));//年
write_DS1302(0x8e,0x80);//开写保护
if(year==2009)
year=2040;
change();
//年
delay1(10);
lcd_wcmd(0x81); //数字显示位置
lcd_wdat(y3+48);
delay1(10);
lcd_wdat(y4+48);
lcd_wcmd(0x81);
//写星期
displayweek();
lcd_wcmd(0x81);
}
//测试结束
}
}
}
}
/*********************************************************/
/* */
/* 外部中断函数 */
/* */
/*********************************************************/
void timer1() interrupt 0
{
LCD_RW=0;
if(s1==0)
{
delay1(5);
if(s1==0)
{
beepon();
testnum++;
while(!s1);
if(testnum==1)
{
Flag=1;
display1();
TR0=0;
TR1=0;
lcd_wcmd(0x95);
lcd_wcmd(0x0f);
}
}
if(testnum==2)
{
display1();
lcd_wcmd(0x93);
}
if(testnum==3)
{
display1();
lcd_wcmd(0x91);
}
/*
if(testnum==4)
{
Flag=0;
display1();
testnum=0;
lcd_wcmd(0x0c);
TR0=1;
TR1=1;
delay1(10);
}
*/
//继续测试
if(testnum==4)
{
display1();
lcd_wcmd(0x85);
}
if(testnum==5)
{
display1();
lcd_wcmd(0x83);
}
if(testnum==6)
{
display1();
lcd_wcmd(0x81);
}
if(testnum==7)
{
//退出的时候写时间函数
//结束
Flag=0;
display1();
testnum=0;
lcd_wcmd(0x0c);
TR0=1;
TR1=1;
delay1(10);
}
//继续结束
}
DS18B20温度模块
DS18B20是常用的数字温度传感器,其输出的是数字信号,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。 [1] DS18B20数字温度传感器接线方便,封装成后可应用于多种场合,如管道式,螺纹式,磁铁吸附式,不锈钢封装式,型号多种多样,有LTM8877,LTM8874等等。
DS18B20温度模块资料来源于百度百科
本次项目需求: 需要在一定的频率内将实时温度的数值呈现到显示屏上,所以我将温度的变化封装在第二个定时器 上具体代码如下
/*******************************************************************/
/* */
/* 18B20复位,初始化函数函数 */
/* */
/*******************************************************************/
void dsreset(void)
{
uint i;
ds=0;
i=103;
while(i>0)i--;//提供480-950us延时
ds=1;
i=4;
while(i>0)i--;//提供60-240us的延时;单片机检测到低电平复位成功
}
/*******************************************************************/
/* */
/* 18B20读1位数据函数 */
/* */
/*******************************************************************/
bit tempreadbit(void)
{
uint i;
bit dat;
ds=0;i++; //i++ 起延时作用
ds=1;i++;i++;
dat=ds; //ds为低电平则读0反之则为1
i=8;while(i>0)i--; //提供延时
return (dat);
}
/*******************************************************************/
/* */
/* 18B20读1字节数据函数 */
/* */
/*******************************************************************/
uchar tempread(void)
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tempreadbit();
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里
}
return(dat);
}
/*******************************************************************/
/* */
/* 18B20写1字节数据函数 */
/* */
/*******************************************************************/
void tempwritebyte(uchar dat)
{
uint i;
uchar j;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) //写 1
{
ds=0;
i++;i++;
ds=1;
i=8;
while(i>0)i--;
}
else
{
ds=0; //写 0
i=8;while(i>0)i--;
ds=1;
i++;i++;
}
}
}
/*******************************************************************/
/* */
/* 18B20开始获取温度并转换 */
/* */
/*******************************************************************/
void tempchange(void) {
dsreset();
delay(1);
tempwritebyte(0xcc); // 写跳过读ROM指令
tempwritebyte(0x44); // 写温度转换指令
}
/*******************************************************************/
/* */
/* 18B20读取寄存器中存储的温度数据 */
/* */
/*******************************************************************/
uint get_temp()
{
uchar a,b;
dsreset();
delay(1);
tempwritebyte(0xcc);
tempwritebyte(0xbe); //读取RAM
a=tempread(); //读低8位
b=tempread(); //读高8位
temp=b;
temp<<=8; //两个字节组合为1个字
temp=temp|a;
f_temp=temp*0.0625; //温度在寄存器中为12位 分辨率位0.0625°
temp=f_temp*10+0.5; //乘以10表示小数点后面只取1位,加0.5是四舍五入
f_temp=f_temp+0.05;
return temp; //temp是整型
}
/*******************************************************************/
/* */
/* 18B20温度显示 */
/* */
/*******************************************************************/
void tempdisplay() //这里非常重要,位置是已经选好的,各位读者可仔细分析
{
uint mm;
tempchange();
mm=get_temp();
dis3[9]=mm%1000/100+'0';
dis3[10]=mm%100/10+'0';
dis3[11]='.';
dis3[12]=mm%10+'0';
lcd_wcmd(0x88);
for(i=0;i<14;i++)
{
lcd_wdat(dis3[i]);
delay(10);
}
//搞完复位
//lcd_wcmd(0x01);
//delay1(10);
}
DS1302时钟模块
DS1302时钟芯片是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。DS1302芯片包含一个用于存储实时时钟/日历的 31 字节的静态 RAM,可通过简单的串行接口与微处理器通讯,将当前的是时钟存于RAM。DS1302芯片对于少于 31 天的月份月末会自动调整,并会自动对闰年进行校正。由于有一个 AM/PM 指示器,时钟可以工作在 12 小时制或者 24小时制。
————————————————
版权声明:本文为CSDN博主「_会飞_的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文出处链接
DS1302时钟模块需求分析: 需要将模块内的时间显示在显示屏上,同时在用按钮调整时间的时候每调整一次加或减都要将数据写入到模块里面(上述按键检测有对应的代码),作者在设计的时候一开始犯得错误是调整完才进行写入导致不是想要的结果
/*******************************************************************/
/* */
/* ds1302写一个字节 */
/* */
/*******************************************************************/
void write_DS1302(uchar cmd,uchar dat)//写一个字节
{
uchar i ;
TRST = 0;//拉低使能端
TSCLK = 0;//拉低数据总线
TRST = 1;//使能端产生上升沿
for(i = 0; i < 8; i ++)//写命令
{
TSCLK = 0;
TIO = cmd & 0x01;//最低位先传
TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走
cmd >>= 1;//右移1位
}
for(i = 0; i < 8;i ++)//写数据
{
TSCLK = 0;
TIO = dat & 0x01;//最低位先传
TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走
dat >>= 1;//右移1位
}
TRST = 0;//拉低使能端,关闭读写功能
}
/*******************************************************************/
/* */
/* ds1302读取数据 */
/* */
/*******************************************************************/
uchar read_DS1302(uchar cmd)//读取数据
{
uchar i,dat;
TRST = 0;//拉低使能端
TSCLK = 0;//拉低数据总线
TRST = 1;//使能端产生上升沿
for(i = 0; i < 8; i ++)//传命令
{
TSCLK = 0;
TIO = cmd & 0x01;//最低位先传
TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走
cmd >>= 1;//右移1位
}
for(i = 0;i < 8; i ++)//读数据
{
TSCLK = 0;//拉低数据总线
dat >>= 1;
if(TIO)
{
dat |= 0x80;
}
TSCLK = 1;//数据总线产生上升沿,数据被DS1302读走
}
TRST = 0;//拉低使能端,关闭读写功能
return dat;
}
/*******************************************************************/
/* */
/* 数据转BCD码 */
/* */
/*******************************************************************/
uchar dat_to_BCD(uchar dat)//数据转BCD码
{
uchar dat1,dat2;
dat1 = dat / 10;
dat2 = dat % 10;
dat2 = dat2 + dat1 * 16;
return dat2;
}
/*******************************************************************/
/* */
/* BCD码转数据 */
/* */
/*******************************************************************/
uchar BDD_to_dat(uchar dat)//BCD码转数据
{
uchar dat1,dat2;
dat1 = dat /16;
dat2 = dat % 16;
dat2 = dat2 + dat1 * 10;
return dat2;
}
/*******************************************************************/
/* */
/* 讲数据从ds1302读取 */
/* */
/*******************************************************************/
void GetDate_ds1302()
{
shi = BDD_to_dat(read_DS1302(0x85));//读秒寄存器(进行BCD码转换)
fen = BDD_to_dat(read_DS1302(0x83));//读分寄存器
miao = BDD_to_dat(read_DS1302(0x81));//读时寄存器
day = BDD_to_dat(read_DS1302(0x87));//读日寄存器
month = BDD_to_dat(read_DS1302(0x89));//读月寄存器
//年比较特殊
year=BDD_to_dat(read_DS1302(0x8d))+2000;//读年寄存器
write_DS1302(0x8e,0x80); //开写保护
}
main函数(包括中断服务函数)
主函数只负责初始化在while大循环中只检测按键再根据按键调整时间
注: 上面在模块分析的时候已经给出对应的中断函数,此处给出只是为了实验的完整性和作者本人编程的风格(中断函数写在主函数的下面,功能函数放在主函数的上面)
/*********************************************************/
/* */
/* 主程序 */
/* */
/*********************************************************/
main()
{
// day1118 start
// write_DS1302(0x8e,0);//清除写保护
// write_DS1302(0x86,dat_to_BCD(18));//日
// write_DS1302(0x88,dat_to_BCD(11));//月
// write_DS1302(0x8c,dat_to_BCD(21));//年
// write_DS1302(0x8e,0x80);//开写保护
// day1118 end
delay(10); //延时
init_T1(); // 初始化定时器
lcd_init(); //初始化LCD
display();//除了第二行的都显示
display1();
//试试写摄氏度符号
lcd_wcmd(0x8F);
lcd_wdat(0xA1);
lcd_wdat(0xE6);
//写星期
displayweek();
delay1(10);
while(1){
keyScan();
delay(10);
GetDate_ds1302();
//测试
// display1();
// delay(10);
// tempdisplay();
// delay(10);
//测试
// tempdisplay();
// delay1(10);
//test
//display1();
// delay(100);
// display();
// keyscan();
// delay(100);
//测试
//lcd_pos(1,4);
// lcd_wcmd(0x0f); //分钟
// delay1(1000);
// lcd_pos(1,5);
// delay1(1000);
}
}
/*********************************************************/
/* */
/* 定时器0 */
/* */
/*********************************************************/
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==14)
{
//测试
display1();
LED=!LED;
//tempdisplay();
//测试
count=0;
miao++;
// beepon();
if(miao==60)
{
miao=0;
fen++;
if(fen==60)
{
fen=0;
shi++;
if(shi==24)
{
shi=0;
}
//write_sfm(4,shi);
// dis2[4]=table0[shi/10];//小时的十位
// dis2[5]=table0[shi%10];//小时的个位
}
//write_sfm(7,fen);
// dis2[7]=table0[fen/10];//分钟的十位
// dis2[8]=table0[fen%10];//分钟的个位
}
//write_sfm(10,miao);
//dis2[10]=table0[miao/10];//秒钟的十位
//dis2[11]=table0[miao%10];//秒钟的个位
}
}
/*********************************************************/
/* */
/* 定时器1刷新温度 */
/* */
/*********************************************************/
void T1_ISR(void) interrupt 3
{
TH1=(65536-50000)/256; //给TH1和TL1重新赋值
TL1=(65536-50000)%256;
testcount++;
if(testcount==21)
{
testcount=0;
tempdisplay();
}
}
/*********************************************************/
/* */
/* 外部中断函数 */
/* */
/*********************************************************/
void timer1() interrupt 0
{
LCD_RW=0;
if(s1==0)
{
delay1(5);
if(s1==0)
{
beepon();
testnum++;
while(!s1);
if(testnum==1)
{
Flag=1;
display1();
TR0=0;
TR1=0;
lcd_wcmd(0x95);
lcd_wcmd(0x0f);
}
}
if(testnum==2)
{
display1();
lcd_wcmd(0x93);
}
if(testnum==3)
{
display1();
lcd_wcmd(0x91);
}
/*
if(testnum==4)
{
Flag=0;
display1();
testnum=0;
lcd_wcmd(0x0c);
TR0=1;
TR1=1;
delay1(10);
}
*/
//继续测试
if(testnum==4)
{
display1();
lcd_wcmd(0x85);
}
if(testnum==5)
{
display1();
lcd_wcmd(0x83);
}
if(testnum==6)
{
display1();
lcd_wcmd(0x81);
}
if(testnum==7)
{
//退出的时候写时间函数
//结束
Flag=0;
display1();
testnum=0;
lcd_wcmd(0x0c);
TR0=1;
TR1=1;
delay1(10);
}
//继续结束
}
致谢
感谢CSDN的许多文章对我的帮助
参考文献
[1] 郭天祥.新概念51单片机C语言教程[M]. 北京:电子工业出版社, 2018. 120-140.
[2] 魏二有.单片机应用系统设计与实现教程[M]. 北京:清华大学出版社, 2019. 100-120.