|
1、说明图
2、代码
3、小结(ATC4C02无法连续读取的问题)
1、说明图

在本制作中关于DS1302的详细资料可参考:http://www.51hei.com/bbs/dpj-22465-1.html ,还有红外和DS18B20资料:http://www.51hei.com/bbs/dpj-22807-1.html
2、代码
#include <reg51.h>
#include<intrins.h>
/*时钟芯片*/
sbit st = P2^1; //使能(RST)
sbit da = P2^2; //i/o管脚(数据管脚)(i/o)
sbit cl = P2^3; //时钟管脚(CLK)
/*1602屏*/
sbit RS = P2^4; //定义端口
sbit RW = P2^5;
sbit EN = P2^6;
/*EEPROM--AT24C02*/
sbit scl = P1^0; //时钟管脚
sbit sda = P1^1; //数据管脚
/*其他*/
sbit spk = P2^7; //蜂鸣器
sbit DQ = P2^0; //ds18b20温度传感器
//下面的常量是一些寄存器的地址
#define addr_m 0x80 //秒
#define addr_f 0x82 //分
#define addr_x 0x84 //小时
#define addr_r 0x86 //日期
#define addr_y 0x88 //月
#define addr_xq 0x8a //星期
#define addr_n 0x8c //年
#define addr_xbh 0x8e //写保护
#define addr_ram 0xc0 //时钟芯片RAM 开始地址(这款芯片有31个8位的RAM起始地址是C0)
#define addr_ram2 0xc2 //时钟芯片RAM2
#define addr_atx 0xae //AT24C02 设备地址+写命令
#define addr_atd 0xaf //AT24C02 设备地址+读命令
unsigned char sj[33]; //接收脉冲时间数组
unsigned char jmsj[4]; //客户码,客户码,数据,数据反码
unsigned char zt[8]; //秒,分,时,日,月,周,年,世纪
//unsigned char nz[3]; //秒,分,时
unsigned char ac; //l602AC值,(显示缓存地址)
unsigned char i; //脉冲个数记录
unsigned char mcsj; //脉冲时间(大于0.56ms小于1.125ms为0,大于1.125ms小于2.25ms)
bit ack;
bit MC=0; //接收红外脉冲开始标志(0:脉冲已经结束,1:脉冲刚开始)
bit JS=0; //脉冲接收结束标志位(1标志接收结束)
bit JM=0; //解码完成标志位(1:解码完成)
void zf_1602(unsigned char x,unsigned char y,unsigned dat);
unsigned char rn(int n); //判断闰年(参数:年)返回0:平年,返回1:闰年
void szjc(unsigned char *p); //设置数据检查
void csh_wnl(); //初始化万年历
void fmq(); //按键声音
void dnz(unsigned char *p); //读取闹钟数据
void dsq_0() interrupt 1 using 1 //定时器T0中断服务函数
{
mcsj++; //256
}
void wbzd_0() interrupt 0 //外部中断服务函数
{
if(MC)
{
if(mcsj>32) //判断是不是引导码。(如果是i=0)
i=0;
sj[ i]=mcsj; //把脉冲时间存入sj这个数组里
mcsj=0; //清空脉冲时间准备接收下一个脉冲时间
i++;
if(i==33) //判断是否接收完脉冲时间
{
i=0;
JS = 1; //接收完成标志位置1
MC=0; //红外脉冲结束
}
}
else
{
MC=1; //红外脉冲开始
mcsj=0; //清空脉冲时间
}
}
void csh_dsq_0() //初始化定时器0
{
TMOD = 0x02;
TH0=0x00; //定时器0的重装数据
TL0=0x00; //初始化
ET0=1; //打开定时器0中断
TR0=1; //启用定时器0
}
void csh_wbzd_0() //初始化外部中断0
{
IT0=1; //外部中断0下降沿触发
EX0=1; //启用外部中断0
EA=1; //打开总中断
}
void hwjm() //红外解码函数
{
unsigned char i,j,k=1;
for(i=0;i<4;i++) //4组数据的计数
{
for(j=0;j<8;j++) //每组数据中的8位数据计算
{
jmsj[ i] >>= 1; //数据右移一位
if(sj[k]>7) //脉冲时间大于7的就是1
jmsj[ i] |= 0x80;
k++;
}
}
JS = 0; //分析完成清零JS
JM = 1; //解码完成JM置1
}
void DelayUs2x(unsigned char t) // us级别延时
{
while(--t);
}
void DelayMs(unsigned char t)// ms级别延时
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*DS18b20温度传感器*/
bit d18b20_qs() //18b20 起始
{
bit dat;
DQ = 1; //DQ复位
DQ = 0; //拉低总线
DelayUs2x(175);
DQ = 1; //拉高总线
DelayUs2x(45); //这里延时大概 45us
dat = DQ; //读取返回值(0:有18b20存在 1:是没有)
DelayUs2x(20); //这里延时大概 20us
return dat; //返回数值
}
void d18b20_x(unsigned char dat) //写 8 位 数 据
{
unsigned char i;
for(i=0;i<8;i++) //8位计数器
{
DQ = 0; //拉低总线
DQ = dat & 0x01; //取最低位赋值给总线
DelayUs2x(45); //延时45us
DQ = 1; //拉过总线准备写下一个数据(或者总线复位)
dat >>= 1; //数据右移一位
}
}
unsigned char d18b20_d() //读 8 位 数 据
{
unsigned char i,dat=0;
for(i=0;i<8;i++) //8位计数器
{
DQ = 0; //拉低总线
dat >>= 1; //数据右移一位
DQ = 1; //拉过总线(准备读取数据)
if(DQ) //判断是否是 1 如果是就把数据赋值给变量的高位
dat |= 0x80;
DelayUs2x(25); //这里延时大概 25us
}
return dat; //返回读取到数据数据
}
unsigned int wd() //读取温度函数
{
unsigned char i = 0; //低8位数据
unsigned char j = 0; //高8位数据
unsigned int k = 0; //无符号16整形用来存储读回来的 16位温度数据(j和i组合后的数据)
d18b20_qs(); //初始化
d18b20_x(0xCC); //跳过序列号的操作(因为18b20在总线上可以挂很多个,这个序列号和网卡MAC地址类似)
d18b20_x(0x44); //开启温度转换
//Delay(200); //开启温度转换需要时间这里延时一下
d18b20_qs(); //初始化
d18b20_x(0xCC); //跳过序列号的操作(因为18b20在总线上可以挂很多个,这个序列号和网卡MAC地址类似)
d18b20_x(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
i = d18b20_d(); //读取低8位
j = d18b20_d(); //读取高8位
k = j;
k <<= 8;
k = k + i;
return k; //返回读取到的16位数据
}
void zh(unsigned int i) //1602显示缓存写入函数
{
unsigned char x,z;
x = i & 0x0f; //取出小数
i >>=4;
switch(x) //小数位转换
{
case 0: z=0;break;
case 1: z=1;break;
case 2: z=1;break;
case 3: z=2;break;
case 4: z=3;break;
case 5: z=3;break;
case 6: z=4;break;
case 7: z=4;break;
case 8: z=5;break;
case 9: z=6;break;
case 10: z=6;break;
case 11: z=7;break;
case 12: z=8;break;
case 13: z=8;break;
case 14: z=9;break;
case 15: z=9;break;
}
z = z +48; //转换成ascii码
zf_1602(13,1,z); //写入1602缓存
z = i & 0xff; //取出整数
x = z/10; //取出十位
x= x+48; //转换成ascii码
zf_1602(10,1,x); //写入1602缓存
x = z%10; //取出个位
x= x+48; //转换成ascii码
zf_1602(11,1,x); //写入1602缓存
}
/*DS18b20温度传感器*/
/*EEPOM---AT24C02*/
void i2_qs() //起始信号
{
sda = 1; //拉高数据
scl = 1; //拉高时钟
_nop_(); //空闲一条指令
_nop_();
_nop_();
_nop_();
sda = 0; //拉低数据产生起始信号(下降沿)
_nop_(); //空闲一条指令
scl = 0; //拉低时钟
_nop_(); //空闲一条指令
}
void i2_tz() //停止信号
{
sda = 0; //拉低数据
scl = 1; //拉高时钟
_nop_(); //空闲一条指令
sda = 1; //拉高时钟产生结束信号(上升沿)
_nop_(); //空闲一条指令
}
void i2_ack(bit _ack) //入口产生 0 ack 1 nak
{
sda = _ack; //ack或者nak
scl = 1; //拉高时钟
_nop_(); //空闲一条指令
scl = 0; //拉低时钟
_nop_(); //空闲一条指令
}
void i2_fs(unsigned char Data) //发送8位数据
{
unsigned char i;
for(i=0;i<8;i++) //8位计数
{
Data <<= 1; //把最高位移送到进制标志位中(CY)
sda = CY; //把进制位中的数据赋值给数据线
scl = 1; //拉高时钟
_nop_(); //空闲一条指令
scl = 0; //拉低时钟
//这里
}
//下面代码是接收ACK的代码
_nop_();//空闲一条指令
sda = 1; //拉高数据准备接收ACK
scl = 1; //拉高时钟产生稳定的有效的数据(相对的)
if(sda==1) //确认接收的是ACK还是NAK
ack = 0;//ack
else
ack = 1;//nak
scl = 0; //拉低时钟
_nop_(); //延时大于 4us
}
unsigned char i2_js() //接收8位数据
{
unsigned char i,Data = 0;
sda = 1; //使能内部上拉,准备读取数据
for(i=0;i<8;i++) //8位计数器
{
Data <<= 1; //移出数据的最高位
scl = 1; //拉高时钟
_nop_(); //延时大于 4us
Data |= sda;//接收数据
scl = 0; //拉低时钟
_nop_(); //延时大于 4us
}
return Data;
}
void i2_sj_x(unsigned char addr,unsigned char Data) //往设备内写入数据(参数 1、寄存器地址 2、写入的数据)
{
i2_qs(); //起始信号
i2_fs(addr_atx); //设备地址+写信号
i2_fs(addr); //寄存器内部地址
i2_fs(Data); //写入设备的数据
i2_tz(); //停止信号
}
unsigned char i2_sj_d(unsigned char addr) //读取数据(参数 寄存器地址)
{
unsigned char Data; //用于接收读取回来的数据
i2_qs(); //起始信号
i2_fs(addr_atx); //设备地址+写信号
i2_fs(addr); //寄存器内部地址
i2_qs(); //起始信号
i2_fs(addr_atd); //设备地址+读信号
Data = i2_js(); //读取数据
i2_ack(1); //NAK应答(无应答)
i2_tz(); //停止信号
return Data; //返回读取的数据
}
/*EEPROM---AT24c02*/
/*DS1302时钟芯片*/
/*下面这个函数是ds1302的最低层函数也是最重要的*/
void x_1302(unsigned char addr,unsigned char q,unsigned char l) //写入8个bit(参数1:地址,参数2:数据,参数3:启用BCD处理)
{
unsigned char i;
if(l) //BCD处理(处理成BCD格式)
{
i=q/10;
q=q%10;
q=q+i*16;
}
addr = addr & 0xFE; //最低位置零
cl = 0; //拉低时钟
da = 0; //复位数据
st = 1; //使能芯片。写入开始
for(i=0;i<8;i++) //写入地址
{
addr >>= 1;
da = CY;
cl = 1;
cl = 0;
}
for(i=0;i<8;i++) //写入数据
{
q >>= 1;
da = CY;
cl = 1;
cl = 0;
}
st = 0; //写入结束
}
/*下面这个函数是ds1302的最低层函数也是最重要的*/
unsigned char d_1302(unsigned char addr,unsigned char l) //读取8个bit(参数1:地址,参数2:启用BCD处理)
{
unsigned char i,dat;
addr = addr | 0x01;//最低位置高
cl = 0; //拉低时钟
da = 0; //复位数据
st = 1; //使能芯片,读取开始
for(i=0;i<8;i++) //写入地址
{
addr >>= 1;
da = CY;
cl = 1;
cl = 0;
}
for(i=0;i<8;i++) //读取数据
{
dat >>= 1;
if(da)
dat = dat | 0x80;
cl = 1;
cl = 0;
}
st = 0; //读取结束
if(l) //BCD处理(还原成10进制)
{
i = dat / 16;
dat = dat % 16;
dat = dat +i*10;
}
return dat;
}
/*DS1302时钟芯片*/
/*1602屏*/
bit m_1602(bit i) //判断1602是否忙(参数i=1:读取AC值。i=0:不读AC值)
{
P0 = 0xFF; //准备读取
RS = 0;
RW = 1;
EN = 0;
_nop_();
EN = 1; //产生高电平
if(i)
ac = P0 & 0x7f; //读取AC值
return (bit)(P0 & 0x80);
}
void x_1602(bit i,unsigned char j) //参数一是写(0、写指令 1、写数据),参数二是写入的8位数据
{
while(m_1602(0))
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
RS = i;
RW = 0;
EN = 1;
P0 = j;
_nop_();
EN = 0; //产生下降沿
}
void qp_1602() //清屏函数
{
x_1602(0,0x01); //第一个参数是:写入的类型(0、写指令 1、写数据),第一个参数是:写入的数据
DelayMs(5);
}
//显示字符
void zf_1602(unsigned char x,unsigned char y,unsigned dat) //参数一是显示的列,参数二是显示的行,参数三是显示的数据
{
if(y==0)
{
x_1602(0,(0x80+x)); //第一行
}
else
{
x_1602(0,(0xc0+x)); //第二行
}
x_1602(1,dat); //写入数据
}
void zfc_1602(unsigned char x,unsigned char y,unsigned char *dat)//参数一是显示的列,参数二是显示的行,参数三是显示的数据(注:数据用\0结尾)
{
if(y==0)
{
x_1602(0,(0x80+x)); //第一行
}
else
{
x_1602(0,(0xc0+x)); //第二行
}
while(*dat) //&:取地址 *:取值
{
x_1602(1,*dat);
dat ++;
}
}
/*1602屏*/
void xrsj(unsigned char i,unsigned char *ls) //写入设置数据
{
unsigned char j,k,s;
while(m_1602(0)); //判忙(如果不忙就退出循环)
m_1602(1); //读取光标的位置
switch(ac)
{
case 0x00: j = 7;k = 1;break; //世纪高位
case 0x01: j = 7;k = 0;break; //世纪低位
case 0x02: j = 6;k = 1;break; //年高位
case 0x03: j = 6;k = 0;break; //年低位
case 0x05: j = 4;k = 1;break; //月份高位
case 0x06: j = 4;k = 0;break; //月份低位
case 0x08: j = 3;k = 1;break; //日高位
case 0x09: j = 3;k = 0;break; //日低位
case 0x40: j = 2;k = 1;break; //小时高位
case 0x41: j = 2;k = 0;break; //小时低位
case 0x43: j = 1;k = 1;break; //分钟高位
case 0x44: j = 1;k = 0;break; //分钟低位
case 0x46: j = 0;k = 1;break; //秒高位
case 0x47: j = 0;k = 0;break; //秒低位
case 0x0e: j = 5;k = 0;break; //周
}
if(k) //1:是高位,0:s 低位
{
s = ls[j] % 10; //高位1
ls[j] = i * 10 + s;
}
else
{
s = ls[j] % 10; //低位0
ls[j] = ls[j] - s;
ls[j] += i;
}
}
void gbkz(unsigned char i) //控制光标去它应该去的地方的函数
{
while(m_1602(0)); //判忙
m_1602(1); //读取光标的位置
if( 0x04 == ac || 0x07 == ac || 0x42 == ac || 0x45 == ac )
{
if(i)
x_1602(0,0x16); //光标右移
else
x_1602(0,0x10); //光标左移
}
else if(0x48 == ac)
{
x_1602(0,0x10); //光标左移
}
else if( 0x0a == ac || 0x0d == ac)
{
if(i)
x_1602(0,0x8e); //移动光标
else
x_1602(0,0x89); //移动光标
}
else if(0x0f == ac)
x_1602(0,0x10); //光标左移
}
unsigned char rn(int n) //判断闰年(参数:年)返回0:平年,返回1:闰年
{
//n = p[7]*100 + p[6];
if( n % 400 ==0 ||((n%4) ==0 && (n%100))) //能被400整除与能被4整除且不能被100整除
//四年一闰,百年不闰,四百年再闰
return 1; //闰年
else
return 0; //平年
}
void xhc(unsigned char k) //红外按键设置函数(0:修改闹钟 1:修改时间)
{
unsigned char i=10,j; //临时变量
unsigned char ls[8]; //临时数组(用来存储修改后的数据)
JM = 0;
if(k) //定位光标
{
for(j = 0;j<8;j++) //把数据存入临时数组用来判断是否有修改。
ls[j] = zt[j];
x_1602(0,0x80); //把光标(ac)移动到第一行第一列
}
else
{
/*for(j=0;j<3;j++)
ls[j] = i2_sj_d(j); //秒,分,时*/
dnz(ls);
x_1602(0,0x01); //清屏
zf_1602(0,1,((ls[2]/10)+48));
zf_1602(1,1,((ls[2]%10)+48));
zf_1602(2,1,'-');
zf_1602(3,1,((ls[1]/10)+48));
zf_1602(4,1,((ls[1]%10)+48));
zf_1602(5,1,'-');
zf_1602(6,1,((ls[0]/10)+48));
zf_1602(7,1,((ls[0]%10)+48));
zfc_1602(0,0,"Set the alarm");
x_1602(0,0xc0); //把光标(ac)移动到第二行第一列
}
x_1602(0,0x0f); //显示光标
while(i)
{
if(JS) //脉冲接收结束后调用解码函数解码
{
hwjm();
}
if(JM) //解码函数解码完成后执行代码
{
switch(jmsj[2]) //匹配按键
{
case 0x16: //按键0
{
xrsj(0,ls); //写入数据
x_1602(1,'0'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x0c: //按键1
{
xrsj(1,ls); //写入数据
x_1602(1,'1'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x18: //按键2
{
xrsj(2,ls); //写入数据
x_1602(1,'2'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x5e: //按键3
{
xrsj(3,ls); //写入数据
x_1602(1,'3'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x08: //按键4
{
xrsj(4,ls); //写入数据
x_1602(1,'4'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x1c: //按键5
{
xrsj(5,ls); //写入数据
x_1602(1,'5'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x5a: //按键6
{
xrsj(6,ls); //写入数据
x_1602(1,'6'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x42: //按键7
{
xrsj(7,ls); //写入数据
x_1602(1,'7'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x52: //按键8
{
xrsj(8,ls); //写入数据
x_1602(1,'8'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x4a: //按键9
{
xrsj(9,ls); //写入数据
x_1602(1,'9'); //写入缓存
fmq();
gbkz(1); //光标控制
break;
}
case 0x44: //光标左移
{
m_1602(1); //读取AC值(此函数也是读忙值函数)
if(ac != 0x00 && ac != 0x40) //限制光标左移的边界
{
x_1602(0,0x10); //光标左移 <
gbkz(0); //光标控制
}
fmq();
break;
}
case 0x40: //光标右移
{
m_1602(1); //读取AC值(此函数也是读忙值函数)
x_1602(0,0x16); //光标右移
fmq();
gbkz(1); //光标控制
break;
}
case 0x43: //换行
{
if( k )
{
m_1602(1); //读取ac值
if(0x40 > ac)
x_1602(0,0xc0); //第2行
else
x_1602(0,0x80); //第1行
fmq();
}
break;
}
case 0x09: //退出设置
{
fmq();
if(k)
{
szjc(ls); //检查设置的数据是否合理
j = addr_m; //起始地址(秒)
x_1302(addr_xbh,0,0); //关闭写保护
x_1302(addr_ram,ls[7],1); //把世纪写入ram1
x_1302(addr_ram2,ls[6],1); //把年写入ram2
for(i=0;i<7;i++) //循环写入秒,分,时,日,月,周,年
{
if(ls[ i] != zt[ i]) //判断数据是否有修改,有修改则写入数据否则不写数据
{
x_1302(j,ls[ i],1); //写入数据
}
j += 2; //地址提升一位
}
x_1302(addr_xbh,0x80,0); //打开写保护
csh_wnl(); //初始化万年历(作用是重新显示)
}
else
{
if(ls[0] > 59) //检查秒是否越界
ls[0] = 59;
if(ls[1] > 59) //检查分是否越界
ls[1] = 59;
if(ls[2] > 23) //检查时是否越界
ls[2] = 23;
for(i=0;i<3;i++)
{
i2_sj_x(i,ls[ i]); //把数据写入EEPROM
DelayMs(1);
}
}
i=0; //退出设置大循环
csh_wnl(); //初始化万年历(作用是重新显示)
break;
}
case 0x15: //取消(退出设置)
{
fmq();
i=0;
csh_wnl();
break;
}
}
JM=0; //按键对应的代码执行完毕。
}
}
x_1602(0,0x0c); //关闭光标
}
void szjc(unsigned char *p) //设置数据检查
{
unsigned char i;
if(p[0]>59) //判断秒是否超过上限(超过就强制将秒设置成59)
{
p[0] = 59; //秒的上限
}
if(p[1]>59) //判断分是否超过上限(超过就强制将分设置成59)
{
p[1] = 59; //分的上限
}
if(p[2]>23) //判断小时是否超过上限(超过就强制将小时设置成23)
{
p[2] = 23; //时的上限
}
if(p[7] > 99) //判断世纪是否超过上限(超过就强制将世纪设置成99)
{
p[7] = 99; //世纪的上限
}
if(p[6] > 99) //判断年是否超过上限(超过就强制将年设置成99)
{
p[6] = 99; //年的上限
}
if(p[4]>12) //判断月是否超过上限(超过就强制将月设置成12)
{
p[4] = 12; //月的上限
}
if(p[4]==4||p[4]==6||p[4]==9||p[4]==11) //设置日期上限
{
i = 30; //小月份的日期上限
}
else if(p[4] == 2) //设置日期上限
{
int n;
n = p[7]*100 + p[6];
i=rn(n); //判断闰年 (如果是闰年返回1)
i += 28; //2月份的日期上限
}
else //设置日期上限
{
i = 31; //大月份的如期上限
}
if(p[3] > i) //判断日期是否超过上限(超过就强制将日期设置成i(i的结果有后面4种:31,30,29,28))
{
p[3] = i; //日期的上限
}
if(p[5] > 7) //判断星期是否超过上限(超过就强制将星期设置成59)
{
p[5] = 7; //星期的上限
}
}
void csh_1602() //初始化1602
{
x_1602(0,0x38); //显示模式设置
DelayMs(5);
x_1602(0,0x38);
DelayMs(5);
x_1602(0,0x38);
DelayMs(5);
x_1602(0,0x38);
x_1602(0,0x0c); //显示光标
x_1602(0,0x01); //显示清屏
//x_1602(0,0x06); //显示光标移动设置
}
void zdy_1602() //1602自定义字符
/*
1602自定义的字符是存储在CGRAM中的
1、CGRAM的地址是:0x40~0x7f 共有:64bit位,每个地址对应8bit位,可以存储8个自定义字符
2、怎么使用自定义字符:0~7对应8个自定义字符 zf_1602(10,1,0x00)这条代码就对应着年;
3、注:1602的小点阵是1点亮的(打开)
*/
{
unsigned char zf[5][8]={{0x08,0x0f,0x12,0x0f,0x0a,0x1f,0x02,0x02}, //年
{0x0f,0x09,0x0f,0x09,0x0f,0x09,0x11,0x00}, //月
{0x1f,0x11,0x11,0x1f,0x11,0x11,0x1f,0x00}, //日
{0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00}, //摄氏度的点
{0x00,0x0e,0x15,0x1d,0x11,0x0e,0x00,0x00}}; //小闹钟图标
unsigned char i,j,k=0x40; //k是CGRAM地址的起始点
for(i=0;i<5;i++) //年,月,入,摄氏度"点"
{
for(j=0;j<8;j++) //年,月,入,摄氏度"点"的数据写入
{
x_1602(0,k); //写入CGRAM地址
x_1602(1,zf[ i][j]); //写入数据
k++; //CGRAM地址加1(提高一位)
}
}
}
void csh_wnl() //初始化万年历
{
unsigned char j,i; //临时变量
i = addr_m; //起始读取地址(秒)
x_1602(0,0x01);
for(j=0;j<7;j++) //循环读取秒,分,时,日,月,周,年
{
zt[j] = d_1302(i,1);
i += 2;
}
zt[7] = d_1302(addr_ram,1); //读取世纪
zh(wd()); //显示温度
zf_1602(12,1,'.');
zf_1602(14,1,0x03); //摄氏度的点
zf_1602(15,1,'C');
zf_1602(0,1,((zt[2]/10)+48)); //写小时高位
zf_1602(1,1,((zt[2]%10)+48)); //写小时低位
zf_1602(2,1,'-');
zf_1602(3,1,((zt[1]/10)+48)); //写分钟高位
zf_1602(4,1,((zt[1]%10)+48)); //写分钟低位
zf_1602(5,1,'-');
zf_1602(6,1,((zt[0]/10)+48)); //写秒高位
zf_1602(7,1,((zt[0]%10)+48)); //写秒低位
zf_1602(0,0,((zt[7]/10)+48)); //写世纪高位
zf_1602(1,0,((zt[7]%10)+48)); //写世纪低位
zf_1602(2,0,((zt[6]/10)+48)); //写年高位
zf_1602(3,0,((zt[6]%10)+48)); //写年低位
zf_1602(4,0,0x00); //年
zf_1602(5,0,((zt[4]/10)+48)); //写月高位
zf_1602(6,0,((zt[4]%10)+48)); //写月底位
zf_1602(7,0,0x01); //月
zf_1602(8,0,((zt[3]/10)+48)); //写入日高位
zf_1602(9,0,((zt[3]%10)+48)); //写入日低位
zf_1602(10,0,0x02); //日
zf_1602(13,0,'-');
zf_1602(14,0,((zt[5])+48)); //写入星期
zf_1602(15,0,'-');
}
void ntb() //年同步(作用是用来同步年和世纪的)
{
unsigned char i,j;
i = d_1302(addr_n,1); //读取时钟芯片的年
j = d_1302(addr_ram2,1); //读取ram的年
if(i != j) //判断是否一样。一样说明在单片机关机的时候年没有变化
{
x_1302(addr_xbh,0,0); //关闭写保护
if(j == 99) //判断ram中的年是否是99如果是世纪加1
{
j = d_1302(addr_ram,1); //读取RAM中的世纪
j++; //世纪加1
if(j == 100) //判断世纪是否等于100
{
x_1302(addr_ram,0,1); //等于100世纪清零然后写入ram
}
else
{
x_1302(addr_ram,j,1); //否则把加1后的世纪写入ram
}
}
x_1302(addr_ram2,i,1); //如果j不等于99则把时钟芯片里面的年写入ram
x_1302(addr_xbh,0x80,0); //关闭写保护
}
}
void dnz(unsigned char *p) //读取闹钟数据
{
unsigned char i;
for(i=0;i<4;i++)
{
p[ i] = i2_sj_d(i);
DelayMs(1);
}
p[1] = i2_sj_d(1);
if(p[0] > 59) //检查秒是否越界
p[0] = 59;
if(p[1] > 59) //检查分是否越界
p[1] = 59;
if(p[2] > 23) //检查时是否越界
p[2] = 23;
}
void fmq() //按键声音
{
unsigned char i = 80;
while(i)
{
DelayUs2x(150);
spk = !spk;
i--;
}
}
void main()
{
unsigned char j,i,nz[4]; //临时变量
bit l=0; //l:闹钟响铃标志位
csh_1602(); //初始化1602
qp_1602(); //清屏
zdy_1602(); //自定义字符
csh_wbzd_0(); //初始化外部中断0
csh_dsq_0(); //初始化定时器0
ntb(); //年份同步
csh_wnl(); //初始化万年历所有显示缓存
dnz(nz); //读取闹钟时间与闹钟的状态
if(nz[3])
{
zf_1602(12,0,0x04);
}
while(1)
{
if(JS) //脉冲接收结束后调用解码函数解码
{
hwjm();
}
if(JM) //解码完成后调用按键匹配函数
{
switch(jmsj[2])
{
case 0x45:fmq();xhc(1);break; //修改时间
case 0x46:
{
fmq();xhc(0);dnz(nz); //修改闹钟
break;
}
case 0x47:
{
fmq();
if(!l) //判断是否响铃了。如果响铃了就先关闭响铃
{
if(nz[3]) //判断是否关闭闹钟
{
zf_1602(12,0,' '); //清除闹钟小图标
nz[3] = 0; //关闭闹钟
}
else
{
zf_1602(12,0,0x04); //显示闹钟小图标
nz[3] = 1; //启用闹钟
}
i2_sj_x(3,nz[3]);
}
else
{
l = 0; //关闭响铃
}
break;
}
}
JM = 0;
if(nz[3])
{
zf_1602(12,0,0x04);
}
}
if(l) //判断响铃是否打开
{
fmq();
}
if((j = d_1302(addr_m,1)) != zt[0]) //读秒
{
zt[0] = j;
zf_1602(6,1,((zt[0]/10)+48)); //写秒高位
zf_1602(7,1,((zt[0]%10)+48)); //写秒低位
if(zt[0] == 0 || zt[0] == nz[0])
{
zt[1] = d_1302(addr_f,1); //读分钟
zf_1602(3,1,((zt[1]/10)+48)); //写分钟高位
zf_1602(4,1,((zt[1]%10)+48)); //写分钟低位
if(zt[1] == 0 || zt[1] == nz[1])
{
zt[2] = d_1302(addr_x,1); //读小时
zf_1602(0,1,((zt[2]/10)+48)); //写小时高位
zf_1602(1,1,((zt[2]%10)+48)); //写小时低位
if( zt[2] == 0 ) //判断小时是否等于0
{
i= addr_r;
for(j=3;j<7;j++) //读取日,月,周,年
{
zt[j] = d_1302(i,1);
i += 2;
}
ntb(); //年份同步
zt[7] = d_1302(addr_ram,1); //读取世纪
zf_1602(0,0,((zt[7]/10)+48)); //写世纪高位
zf_1602(1,0,((zt[7]%10)+48)); //写世纪低位
zf_1602(2,0,((zt[6]/10)+48)); //写年高位
zf_1602(3,0,((zt[6]%10)+48)); //写年低位
zf_1602(5,0,((zt[4]/10)+48)); //写月高位
zf_1602(6,0,((zt[4]%10)+48)); //写月底位
zf_1602(8,0,((zt[3]/10)+48)); //写入日高位
zf_1602(9,0,((zt[3]%10)+48)); //写入日低位
zf_1602(14,0,((zt[5])+48)); //写入星期
}
if(zt[2] == nz[2]) //判断小时是否等于闹钟小时
{
if(nz[3])
l = 1; //打开响铃
}
}
}
else
{
zh(wd()); //显示温度
}
}
}
}
3小结
1、1602出现数据显示或光标移动出现有时显示或者操作正确有时错误
{ 原因:1、程序出现流程或者算法问题
2、1602在忙的时候是写入不了指令或命令的
出现第二种情况可以用判忙
}
2、RRPROM 读取单字节时应答ack,在下一次读取单字节时会出错。
所以 在读单字节时应答应该是NAK。
3、写代码时一定要了解芯片的资料,原理。
本例完整代码下载地址:http://www.51hei.com/f/shiz1602.rar
|
评分
-
查看全部评分
|