关于设计单片机万年历的收获
1.在改程序中的错误的过程中,知道了许多错误类型,增加了经验,而且我深刻的体会到了做任何事都要有耐心~
2.在结合模块的过程中,我知道了扎实基础很重要,不然这个过程会很混乱…
3.不能半途而废,程序中有些错误改不出来时,有些内容不理解时,我想过换一些模块联动,但是我相信坚持就是胜利~
4.虚心求教的重要性,问过CSDN上的其他博主,B站上的老师等等,他们都给我提供了有力的帮助~
!!万年历具有读取方便,显示直观,功能多样,电路简洁等优点
功能:
1.显示年,月,日,时,分,秒
2.显示温度
3.显示星期
4.按键调节各参数,分别为设置键,加键,减键设置闹钟键
一. 电路组成
1.单片机最小系统
2.LCD1602液晶显示屏电路
3.四路独立按键
4.DS1302时钟电路
5.DS18B20温度传感器
6.系统电源
二.lcd1602介绍
1.引脚定义
VL引脚:为液晶显示器对比度调整端
RS引脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器
R/W引脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。
EN引脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
2.控制程序:写数据、写指令、初始化
读状态:输入:RS=L,RW=H,E=H----输出:D0~D7=状态字
写指令:输入:RS=L,RW=L,D0~D7=指令码----输出:无
读数据:输入:RS=H,RW=H,E=H----输出:D0~D7=数据
写数据:输入:RS=H,RW=L,D0~D7=数据,E=高脉冲----输出:无
(1)写数据函数
(2)写指令函数
(3)初始化函数
a.设置写操作:R/W设为低电平;
b.屏幕设置模式:写指令38H;
c.打开显示,关闭光标:写指令0CH;
d.设置显示光标移动位置:写指令06H;
e.设置字符显示位置:写指令(字符显示的起始位置);
由于对LCD1602进行控制时不需要实现读操作,所以R/W在初始化后恒为低电平;对控制器每次进行读写操作前,都必须进行读写检测,确保STA7为0
三.DS18B20介绍
1.初始化
在操作时序的时候,为了防止中断干扰总线时序,先关闭总中断。拉低 DS18B20 这个引脚,持续 500us;延时 60us;读取存在脉冲,并且等待存在脉冲结束。
2.ROM 操作指令
3.RAM 存储器操作指令
4.DS18B20的位读写时序
四.ds1302介绍
本系统的时间读取主要来源于单片机对ds1302的操作
引脚功能
内部结构
寄存器
操作方法:启动传送,发送命令字节,传送数据
ds18b20
uchar ds18b20_read()
{ //读一个字节
uchar i=0;
uchar dat = 0;
for (i=8;i>0;i--)
{
dq = 0; // 给脉冲信号
dat>>=1;
dq = 1; // 给脉冲信号
if(dq)
dat|=0x80;
delay1(4);
}
return(dat);
}
void ds18b20_write(char dat)
{ //写一个字节
uchar i=0;
for (i=8; i>0; i--)
{
dq = 0;
dq = dat&0x01;
delay1(5);
dq = 1;
dat>>=1;
}
}
int ds18b20_read_temp()
{ //读取温度
uchar low;
uchar high;
unsigned long tmp;
float value;
int t;//温度
ds18b20_init();
ds18b20_write(0xCC); //跳过读序列号的操作
ds18b20_write(0x44); //启动温度转换
ds18b20_init();
ds18b20_write(0xCC); //跳过读序列号的操作
ds18b20_write(0xBE); //读取温度寄存器
low=ds18b20_read();//低八位数据
high=ds18b20_read();//高八位数据
tmp=high;
tmp<<=8;
tmp=tmp|low;//此处有正负之分
if(tmp>=63488)
{ //ffff f000 0000 0000-->(f800)
temp_flag=0;//8位全为1时,加1才进位
if((~low)==0xff)
{ //判断low取反加1之后是否进位
high=(~high)+1;
low=0;
}
else
{
high=~high;
low=(~low)+1;
}
tmp=high*256+low;
}
else
{
temp_flag=1;
}
value=tmp*0.0625;
t=value*10+((temp_flag==1)?+0.5:-0.5);//放大十倍输出并四舍五入
return t;
}
LCD1602
void write_1602com(uchar com)
{ //1602写指令
rs=0;
lcden=0;
P2=com;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void write_1602data(uchar dat)
{ //1602写数据
rs=1;
lcden=0;
P2=dat;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init_1602()
{ //初始化1602液晶
rw=0;
lcden=0;
write_1602com(0x38);//设置显示模式
write_1602com(0x0c);//显示开关及光标是否显示和闪动
write_1602com(0x06);//光标移动方向
write_1602com(0x01);//清屏
}
ds1302
void write_ds1302_byte(uchar temp)
{ //ds1302写一个字节数据
uchar i;
for(i=0;i<8;i++)
{
io=temp&0x01;//将数据放到IO口上
scl=0;//scl为低时准备数据
scl=1;//上升沿写入
temp>>=1;
}
}
void write_ds1302(uchar add,uchar dat)
{ //向地址add写入数据dat
rst=0;
scl=0;
rst=1;
write_ds1302_byte(add);
write_ds1302_byte(dat);
scl=1;
rst=0;
}
uchar read_ds1302(uchar add)
{ //ds1302读数据
uchar i,dat;
rst=0;
scl=0;
rst=1;
write_ds1302_byte(add);//首先写入要读的数据处的地址
for(i=0;i<8;i++)
{
if(io==1)
{
dat|=0x80;
}
scl=1;
scl=0;//下降沿读取数据
dat>>=1;
}
scl=1;
rst=0;
return dat;
}
void read_time(uchar curr_time[])
{
uchar i;
uchar ucAddr = 0x81;
for (i=0;i<7;i++)
{
curr_time[i] = read_ds1302(ucAddr);//格式为: 秒 分 时 日 月 星期 年
ucAddr += 2;
}
}
void set_time(uchar *pSecDa)
{ //设定时间
uchar i;
uchar ucAddr = 0x80;
write_ds1302(0x8e,0x00);
for(i =7;i>0;i--)
{
write_ds1302(ucAddr,*pSecDa); //秒 分 时 日 月 星期 年
pSecDa++;
ucAddr+=2;
}
write_ds1302(0x8e,0x80);
}
void init_ds1302()
{ //ds1302初始化
isInit_1302=read_ds1302(0x81);//读出时钟状态
if(isInit_1302&0x80)
{ //说明没有初始化
write_ds1302(0x8e,0x00);//关闭写保护
write_ds1302(0x90,0xa5); //辅助电源充电命令 一个二极管一个2K电阻
write_ds1302(0x80,0x00);//秒 CH置0 开启时钟
write_ds1302(0x82,0x59);//分
write_ds1302(0x84,0x10);//时
write_ds1302(0x86,0x07);//日
write_ds1302(0x88,0x05);//月
write_ds1302(0x8a,0x04);//星期
write_ds1302(0x8c,0x14);//年
write_ds1302(0x8e,0x80);
}
}
闹钟设置
void init_alarm()
{ //闹钟设置界面 只有首次进入才执行
uchar code x[]="SET ALARM";
uchar i;
if(alarm==0)
{
write_1602com(0x01);//清屏
write_1602com(0x80+3);//设置数据指针
for(i=0;i<9;i++)
{
write_1602data(x[i]);
}
display_alarm(0x80+0x40+5,shi);//载入闹钟的时分
write_1602com(0x80+0x40+7);
write_1602data(':');
display_alarm(0x80+0x40+8,fen);
if(isOpen)
{ //初始化的时候如果已经设定闹钟则显示ON
write_1602com(0x80+0x40+13);
write_1602data(' ');
write_1602data('O');
write_1602data('N');
}
else
{
write_1602com(0x80+0x40+13);
write_1602data('O');
write_1602data('F');
write_1602data('F');
}
}
}