找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 14239|回复: 27
打印 上一主题 下一主题
收起左侧

51单片机+红外遥控+24c02存储+1602屏+1302时钟+18b20温度+蜂鸣器 程序

  [复制链接]
跳转到指定楼层
楼主
ID:72008 发表于 2015-1-11 18:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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

评分

参与人数 1黑币 +8 收起 理由
xiou + 8

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏12 分享淘帖 顶2 踩
回复

使用道具 举报

沙发
ID:71996 发表于 2015-1-13 13:59 | 只看该作者
感谢楼主!   51hei论坛有你更精彩!!!!!!!!
回复

使用道具 举报

板凳
ID:65707 发表于 2015-1-15 17:22 | 只看该作者
比较清晰,不错哟.感谢分享
回复

使用道具 举报

地板
ID:74303 发表于 2015-4-1 20:05 | 只看该作者
51黑有你更精彩
回复

使用道具 举报

5#
ID:77205 发表于 2015-4-16 22:21 | 只看该作者
不懂,还是攒一个************************
回复

使用道具 举报

6#
ID:73762 发表于 2015-4-17 22:20 | 只看该作者
不错哟.感谢分享
回复

使用道具 举报

7#
ID:77776 发表于 2015-4-23 21:40 | 只看该作者
怎么没有个电路图
回复

使用道具 举报

8#
ID:79943 发表于 2015-5-15 10:21 | 只看该作者
1602出现数据显示或光标移动出现有时显示或者操作正确有时错误
        {        原因:1、程序出现流程或者算法问题
                         2、1602在忙的时候是写入不了指令或命令的
                                出现第二种情况可以用判忙
        }
         2、RRPROM 读取单字节时应答ack,在下一次读取单字节时会出错。
                所以 在读单字节时应答应该是NAK。
        3、写代码时一定要了解芯片的资料,原理。
回复

使用道具 举报

9#
ID:45225 发表于 2015-11-22 14:43 | 只看该作者
写得很好,很详细!
回复

使用道具 举报

10#
ID:102957 发表于 2016-2-25 18:20 | 只看该作者
谢谢楼主了。能结识你就更好了。
回复

使用道具 举报

11#
ID:106341 发表于 2016-6-2 08:23 | 只看该作者
点个赞
回复

使用道具 举报

12#
ID:116662 发表于 2016-6-5 21:43 | 只看该作者
热心楼主辛苦了,学习了,谢谢分享
回复

使用道具 举报

13#
ID:79544 发表于 2016-6-9 11:14 | 只看该作者
谢谢楼主无私分享大伙跟着学习啦,赞!!!!!!
回复

使用道具 举报

14#
ID:101443 发表于 2016-7-7 22:47 | 只看该作者
求助。那个精度在哪段程序可以修改?我的12m晶振半天有快1分多
回复

使用道具 举报

15#
ID:101443 发表于 2016-7-7 22:48 | 只看该作者
好好好,好资料,51黑有你更精彩
回复

使用道具 举报

16#
ID:135781 发表于 2017-4-8 18:21 | 只看该作者
楼主的程序挺好的,写的很详细
回复

使用道具 举报

17#
ID:194871 发表于 2017-4-30 09:22 | 只看该作者
点个赞,51黑有你更精彩
回复

使用道具 举报

18#
ID:163950 发表于 2017-5-3 08:21 | 只看该作者
注释清晰,就喜欢这种代码!加油!
回复

使用道具 举报

19#
ID:98122 发表于 2017-12-26 16:35 | 只看该作者
程序注释非常清晰,谢谢楼主分享!
回复

使用道具 举报

20#
ID:253022 发表于 2017-12-28 10:51 | 只看该作者
可以用一下1602的突发模式,可以避免以上问题
回复

使用道具 举报

21#
ID:166341 发表于 2017-12-30 18:23 | 只看该作者
谢谢分享,收藏了。51黑有你更精彩
回复

使用道具 举报

22#
ID:269143 发表于 2018-1-1 17:04 | 只看该作者
努力学习中!
回复

使用道具 举报

23#
ID:185866 发表于 2018-1-4 13:09 | 只看该作者
额    有电路图麽0.0
回复

使用道具 举报

24#
ID:274043 发表于 2018-1-15 02:03 | 只看该作者
aa3314xxx 发表于 2016-7-7 22:47
求助。那个精度在哪段程序可以修改?我的12m晶振半天有快1分多

在定时器初始化那里可以改精度
回复

使用道具 举报

25#
ID:272625 发表于 2018-1-25 03:32 | 只看该作者
感谢资料共享,51黑有你更精彩
回复

使用道具 举报

26#
ID:354282 发表于 2018-10-17 09:26 | 只看该作者
楼主厉害
回复

使用道具 举报

27#
ID:326450 发表于 2018-10-18 23:50 | 只看该作者
嘻嘻,楼主厉害了
回复

使用道具 举报

28#
ID:552827 发表于 2019-7-1 16:42 | 只看该作者
试试看看怎么样
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表