標(biāo)題: 51單片機+紅外遙控+24c02存儲+1602屏+1302時鐘+18b20溫度+蜂鳴器 程序 [打印本頁]

作者: liuyy    時間: 2015-1-11 18:28
標(biāo)題: 51單片機+紅外遙控+24c02存儲+1602屏+1302時鐘+18b20溫度+蜂鳴器 程序
1、說明圖
2、代碼
3、小結(jié)(ATC4C02無法連續(xù)讀取的問題)


        1、說明圖

  在本制作中關(guān)于DS1302的詳細資料可參考:http://www.torrancerestoration.com/bbs/dpj-22465-1.html   ,還有紅外和DS18B20資料:http://www.torrancerestoration.com/bbs/dpj-22807-1.html

2、代碼

#include <reg51.h>
#include<intrins.h>

/*時鐘芯片*/
sbit st = P2^1;        //使能(RST)
sbit da = P2^2;        //i/o管腳(數(shù)據(jù)管腳)(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;        //數(shù)據(jù)管腳

/*其他*/
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 設(shè)備地址+寫命令
#define addr_atd 0xaf //AT24C02 設(shè)備地址+讀命令

unsigned char sj[33];        //接收脈沖時間數(shù)組
unsigned char jmsj[4];        //客戶碼,客戶碼,數(shù)據(jù),數(shù)據(jù)反碼
unsigned char zt[8];        //秒,分,時,日,月,周,年,世紀(jì)
//unsigned char nz[3];        //秒,分,時
unsigned char ac;        //l602AC值,(顯示緩存地址)
unsigned char i;        //脈沖個數(shù)記錄
unsigned char mcsj;        //脈沖時間(大于0.56ms小于1.125ms為0,大于1.125ms小于2.25ms)
bit ack;
bit MC=0;        //接收紅外脈沖開始標(biāo)志(0:脈沖已經(jīng)結(jié)束,1:脈沖剛開始)        
bit JS=0;        //脈沖接收結(jié)束標(biāo)志位(1標(biāo)志接收結(jié)束)
bit JM=0;        //解碼完成標(biāo)志位(1:解碼完成)


void zf_1602(unsigned char x,unsigned char y,unsigned dat);
unsigned char rn(int n);        //判斷閏年(參數(shù):年)返回0:平年,返回1:閏年
void szjc(unsigned char *p);        //設(shè)置數(shù)據(jù)檢查
void csh_wnl();        //初始化萬年歷
void fmq();        //按鍵聲音
void dnz(unsigned char *p);        //讀取鬧鐘數(shù)據(jù)

void dsq_0() interrupt 1 using 1        //定時器T0中斷服務(wù)函數(shù)
{
        mcsj++;        //256        
}
void wbzd_0() interrupt 0         //外部中斷服務(wù)函數(shù)
{

        if(MC)
        {
                if(mcsj>32)        //判斷是不是引導(dǎo)碼。(如果是i=0)
                                i=0;
                        sj[ i]=mcsj;        //把脈沖時間存入sj這個數(shù)組里
                        mcsj=0;        //清空脈沖時間準(zhǔn)備接收下一個脈沖時間
                        i++;        
                        if(i==33)        //判斷是否接收完脈沖時間
                        {
                                
                                i=0;
                                JS = 1;        //接收完成標(biāo)志位置1
                                MC=0;                //紅外脈沖結(jié)束
                        }
                        
        }
        else
        {
                MC=1;        //紅外脈沖開始
                mcsj=0;        //清空脈沖時間
        }
        
}
void csh_dsq_0()        //初始化定時器0
{
        TMOD = 0x02;
        TH0=0x00;        //定時器0的重裝數(shù)據(jù)
        TL0=0x00;        //初始化
        ET0=1;        //打開定時器0中斷
        TR0=1;        //啟用定時器0
        
}

void csh_wbzd_0()        //初始化外部中斷0
{
        IT0=1;        //外部中斷0下降沿觸發(fā)
        EX0=1;        //啟用外部中斷0
        EA=1;        //打開總中斷
}
void hwjm() //紅外解碼函數(shù)
{
        unsigned char i,j,k=1;
        
                for(i=0;i<4;i++)        //4組數(shù)據(jù)的計數(shù)
                {
                        for(j=0;j<8;j++)        //每組數(shù)據(jù)中的8位數(shù)據(jù)計算
                        {
                                jmsj[ i] >>= 1;        //數(shù)據(jù)右移一位
                                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復(fù)位
        DQ = 0;        //拉低總線
        DelayUs2x(175);
        DQ = 1;        //拉高總線
        DelayUs2x(45);         //這里延時大概 45us
        dat = DQ;        //讀取返回值(0:有18b20存在 1:是沒有)
        DelayUs2x(20);        //這里延時大概 20us
        return dat;        //返回數(shù)值
}

void d18b20_x(unsigned char dat) //寫 8 位 數(shù) 據(jù)
{
        unsigned char i;
        for(i=0;i<8;i++)        //8位計數(shù)器
        {
                DQ = 0;        //拉低總線
                DQ = dat & 0x01;        //取最低位賦值給總線
                DelayUs2x(45);        //延時45us
                DQ = 1;        //拉過總線準(zhǔn)備寫下一個數(shù)據(jù)(或者總線復(fù)位)
                dat >>= 1;        //數(shù)據(jù)右移一位
        }
}
unsigned char d18b20_d()        //讀 8 位 數(shù) 據(jù)
{
        unsigned char i,dat=0;
        for(i=0;i<8;i++)        //8位計數(shù)器
        {
                DQ = 0;        //拉低總線
                dat >>= 1;        //數(shù)據(jù)右移一位
                DQ = 1;        //拉過總線(準(zhǔn)備讀取數(shù)據(jù))
                if(DQ)        //判斷是否是 1 如果是就把數(shù)據(jù)賦值給變量的高位
                        dat |= 0x80;
                DelayUs2x(25);        //這里延時大概 25us
        }
        return dat; //返回讀取到數(shù)據(jù)數(shù)據(jù)
}


unsigned int wd()        //讀取溫度函數(shù)
{
        unsigned char i = 0;        //低8位數(shù)據(jù)
        unsigned char j = 0;        //高8位數(shù)據(jù)
        unsigned int k = 0;        //無符號16整形用來存儲讀回來的 16位溫度數(shù)據(jù)(j和i組合后的數(shù)據(jù))

        d18b20_qs();        //初始化
        d18b20_x(0xCC);        //跳過序列號的操作(因為18b20在總線上可以掛很多個,這個序列號和網(wǎng)卡MAC地址類似)
        d18b20_x(0x44);        //開啟溫度轉(zhuǎn)換
        //Delay(200);                        //開啟溫度轉(zhuǎn)換需要時間這里延時一下
        
        d18b20_qs();        //初始化
        d18b20_x(0xCC);        //跳過序列號的操作(因為18b20在總線上可以掛很多個,這個序列號和網(wǎng)卡MAC地址類似)
        d18b20_x(0xBE);        //讀取溫度寄存器等(共可讀9個寄存器) 前兩個就是溫度
        i = d18b20_d();        //讀取低8位
        j = d18b20_d();        //讀取高8位

        k = j;               
        k <<= 8;
        k = k + i;
        return k;        //返回讀取到的16位數(shù)據(jù)
}
void zh(unsigned int i)         //1602顯示緩存寫入函數(shù)
{
        unsigned char x,z;
        x = i & 0x0f;        //取出小數(shù)
        i >>=4;
        switch(x)        //小數(shù)位轉(zhuǎn)換
        {
                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;        //轉(zhuǎn)換成ascii碼
        zf_1602(13,1,z);        //寫入1602緩存
        z = i & 0xff;        //取出整數(shù)
        x = z/10;        //取出十位
        x= x+48;        //轉(zhuǎn)換成ascii碼
        zf_1602(10,1,x);        //寫入1602緩存
        x = z%10;        //取出個位
        x= x+48;        //轉(zhuǎn)換成ascii碼
        zf_1602(11,1,x);        //寫入1602緩存
}
/*DS18b20溫度傳感器*/

/*EEPOM---AT24C02*/
void i2_qs()        //起始信號
{
        sda = 1;        //拉高數(shù)據(jù)
        scl = 1;        //拉高時鐘
        _nop_();        //空閑一條指令
        _nop_();
        _nop_();
        _nop_();
        sda = 0;        //拉低數(shù)據(jù)產(chǎn)生起始信號(下降沿)
        _nop_();        //空閑一條指令
        scl = 0;        //拉低時鐘
        _nop_();        //空閑一條指令
}
void i2_tz()        //停止信號
{
        sda = 0;        //拉低數(shù)據(jù)
        scl = 1;        //拉高時鐘
        _nop_();        //空閑一條指令
        sda = 1;        //拉高時鐘產(chǎn)生結(jié)束信號(上升沿)
        _nop_();        //空閑一條指令
        
}
void i2_ack(bit _ack)        //入口產(chǎn)生 0 ack 1 nak
{
        sda = _ack;        //ack或者nak
        scl = 1;        //拉高時鐘
        _nop_();        //空閑一條指令
        scl = 0;        //拉低時鐘
        _nop_();        //空閑一條指令
}
void i2_fs(unsigned char Data) //發(fā)送8位數(shù)據(jù)
{
        unsigned char i;
        for(i=0;i<8;i++)        //8位計數(shù)
        {
                Data <<= 1;        //把最高位移送到進制標(biāo)志位中(CY)
                sda = CY;        //把進制位中的數(shù)據(jù)賦值給數(shù)據(jù)線
                scl = 1;        //拉高時鐘
                _nop_();        //空閑一條指令
                scl = 0;        //拉低時鐘
                //這里
        }
        //下面代碼是接收ACK的代碼
        _nop_();//空閑一條指令
        sda = 1;        //拉高數(shù)據(jù)準(zhǔn)備接收ACK
        scl = 1;        //拉高時鐘產(chǎn)生穩(wěn)定的有效的數(shù)據(jù)(相對的)
        if(sda==1)        //確認接收的是ACK還是NAK
                ack = 0;//ack
        else
                ack = 1;//nak
        scl = 0;        //拉低時鐘
        _nop_();        //延時大于 4us
        
}

unsigned char i2_js()        //接收8位數(shù)據(jù)
{
        unsigned char i,Data = 0;
        sda = 1;        //使能內(nèi)部上拉,準(zhǔn)備讀取數(shù)據(jù)
        for(i=0;i<8;i++)        //8位計數(shù)器
        {
                Data <<= 1;        //移出數(shù)據(jù)的最高位
                scl = 1;        //拉高時鐘
                _nop_();        //延時大于 4us
                Data |= sda;//接收數(shù)據(jù)
                scl = 0;        //拉低時鐘
                _nop_();        //延時大于 4us
               
        }
        return Data;
}
void i2_sj_x(unsigned char addr,unsigned char Data)        //往設(shè)備內(nèi)寫入數(shù)據(jù)(參數(shù) 1、寄存器地址 2、寫入的數(shù)據(jù))
{
        i2_qs();                //起始信號
        i2_fs(addr_atx);        //設(shè)備地址+寫信號
        i2_fs(addr);        //寄存器內(nèi)部地址
        i2_fs(Data);        //寫入設(shè)備的數(shù)據(jù)
        i2_tz();                        //停止信號
}
unsigned char i2_sj_d(unsigned char addr)        //讀取數(shù)據(jù)(參數(shù) 寄存器地址)
{
        unsigned char Data;        //用于接收讀取回來的數(shù)據(jù)
        i2_qs();                //起始信號
        i2_fs(addr_atx);        //設(shè)備地址+寫信號
        i2_fs(addr);        //寄存器內(nèi)部地址
        i2_qs();                //起始信號
        i2_fs(addr_atd);        //設(shè)備地址+讀信號
        Data = i2_js();        //讀取數(shù)據(jù)
        i2_ack(1);                //NAK應(yīng)答(無應(yīng)答)
        i2_tz();                //停止信號
        return Data;        //返回讀取的數(shù)據(jù)
}
/*EEPROM---AT24c02*/

/*DS1302時鐘芯片*/
/*下面這個函數(shù)是ds1302的最低層函數(shù)也是最重要的*/
void x_1302(unsigned char addr,unsigned char q,unsigned char l)        //寫入8個bit(參數(shù)1:地址,參數(shù)2:數(shù)據(jù),參數(shù)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;        //復(fù)位數(shù)據(jù)
        st = 1;        //使能芯片。寫入開始
        for(i=0;i<8;i++)        //寫入地址
        {
                addr >>= 1;
                da = CY;
                cl = 1;
                cl = 0;        
        }
        for(i=0;i<8;i++)        //寫入數(shù)據(jù)
        {
                q >>= 1;
                da = CY;
                cl = 1;
                cl = 0;
        }
        st = 0;        //寫入結(jié)束
}

/*下面這個函數(shù)是ds1302的最低層函數(shù)也是最重要的*/
unsigned char d_1302(unsigned char addr,unsigned char l)        //讀取8個bit(參數(shù)1:地址,參數(shù)2:啟用BCD處理)
{
        unsigned char i,dat;
        addr = addr | 0x01;//最低位置高
        cl = 0;        //拉低時鐘
        da = 0;        //復(fù)位數(shù)據(jù)
        st = 1;        //使能芯片,讀取開始
        for(i=0;i<8;i++)        //寫入地址
        {
                addr >>= 1;
                da = CY;
                cl = 1;
                cl = 0;
        }
        for(i=0;i<8;i++)        //讀取數(shù)據(jù)
        {
                dat >>= 1;
                if(da)
                        dat = dat | 0x80;
                cl = 1;
                cl = 0;
        }
        st = 0;        //讀取結(jié)束
        if(l)        //BCD處理(還原成10進制)
        {
                i = dat / 16;        
                dat = dat % 16;
                dat = dat +i*10;
        }
        return dat;
}
/*DS1302時鐘芯片*/

/*1602屏*/

bit m_1602(bit i)        //判斷1602是否忙(參數(shù)i=1:讀取AC值。i=0:不讀AC值)
{
        P0 = 0xFF; //準(zhǔn)備讀取
        RS = 0;
        RW = 1;
        EN = 0;
        _nop_();
        EN = 1; //產(chǎn)生高電平
        if(i)
                ac = P0 & 0x7f;        //讀取AC值
        return (bit)(P0 & 0x80);
}
void x_1602(bit i,unsigned char j)        //參數(shù)一是寫(0、寫指令 1、寫數(shù)據(jù)),參數(shù)二是寫入的8位數(shù)據(jù)
{
        while(m_1602(0))
        {
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
        }
        RS = i;
        RW = 0;
        EN = 1;
        P0 = j;
        _nop_();
        EN = 0; //產(chǎn)生下降沿
}
void qp_1602()        //清屏函數(shù)
{
        x_1602(0,0x01); //第一個參數(shù)是:寫入的類型(0、寫指令 1、寫數(shù)據(jù)),第一個參數(shù)是:寫入的數(shù)據(jù)
        DelayMs(5);
}

//顯示字符
void zf_1602(unsigned char x,unsigned char y,unsigned dat)        //參數(shù)一是顯示的列,參數(shù)二是顯示的行,參數(shù)三是顯示的數(shù)據(jù)
{
        if(y==0)
        {
                x_1602(0,(0x80+x));        //第一行
        }
        else
        {
                x_1602(0,(0xc0+x));        //第二行
        }
        x_1602(1,dat);        //寫入數(shù)據(jù)
}

void zfc_1602(unsigned char x,unsigned char y,unsigned char *dat)//參數(shù)一是顯示的列,參數(shù)二是顯示的行,參數(shù)三是顯示的數(shù)據(jù)(注:數(shù)據(jù)用\0結(jié)尾)
{
        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)        //寫入設(shè)置數(shù)據(jù)
{
        unsigned char j,k,s;
        while(m_1602(0));        //判忙(如果不忙就退出循環(huán))
        m_1602(1);        //讀取光標(biāo)的位置
        switch(ac)
        {
                case 0x00: j = 7;k = 1;break;        //世紀(jì)高位
                case 0x01: j = 7;k = 0;break;        //世紀(jì)低位
                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)        //控制光標(biāo)去它應(yīng)該去的地方的函數(shù)
{
        while(m_1602(0));        //判忙
        m_1602(1);        //讀取光標(biāo)的位置
        if( 0x04 == ac || 0x07 == ac || 0x42 == ac || 0x45 == ac )
        {
                        if(i)
                                x_1602(0,0x16);        //光標(biāo)右移
                        else
                                x_1602(0,0x10);        //光標(biāo)左移
        }
        else if(0x48 == ac)
        {
                x_1602(0,0x10);        //光標(biāo)左移
        }
        else if( 0x0a == ac || 0x0d == ac)
        {
                if(i)
                        x_1602(0,0x8e);        //移動光標(biāo)
                else
                        x_1602(0,0x89);        //移動光標(biāo)
        }
        else if(0x0f == ac)
                x_1602(0,0x10);        //光標(biāo)左移
}

unsigned char rn(int n)        //判斷閏年(參數(shù):年)返回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)                //紅外按鍵設(shè)置函數(shù)(0:修改鬧鐘 1:修改時間)
{
        unsigned char i=10,j;        //臨時變量
        unsigned char ls[8];        //臨時數(shù)組(用來存儲修改后的數(shù)據(jù))
        JM = 0;
        if(k)        //定位光標(biāo)
        {
                for(j = 0;j<8;j++)        //把數(shù)據(jù)存入臨時數(shù)組用來判斷是否有修改。
                        ls[j] = zt[j];
                x_1602(0,0x80);        //把光標(biāo)(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);        //把光標(biāo)(ac)移動到第二行第一列
               
        }
        x_1602(0,0x0f);        //顯示光標(biāo)
        while(i)
        {
                if(JS)        //脈沖接收結(jié)束后調(diào)用解碼函數(shù)解碼
                {
                        hwjm();
                }
                if(JM)        //解碼函數(shù)解碼完成后執(zhí)行代碼
                {
                        switch(jmsj[2])        //匹配按鍵
                        {
                                case 0x16:        //按鍵0
                                {
                                        xrsj(0,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'0');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x0c:        //按鍵1
                                {
                                        xrsj(1,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'1');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x18:        //按鍵2
                                {
                                        xrsj(2,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'2');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x5e:        //按鍵3
                                {
                                        xrsj(3,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'3');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x08:        //按鍵4
                                {
                                        xrsj(4,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'4');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x1c:        //按鍵5
                                {
                                        xrsj(5,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'5');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x5a:        //按鍵6
                                {
                                        xrsj(6,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'6');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x42:        //按鍵7
                                {
                                        xrsj(7,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'7');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x52:        //按鍵8
                                {
                                        xrsj(8,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'8');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x4a:        //按鍵9
                                {
                                        xrsj(9,ls);        //寫入數(shù)據(jù)
                                        x_1602(1,'9');        //寫入緩存
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        break;
                                }
                                case 0x44:        //光標(biāo)左移
                                {
                                        m_1602(1);        //讀取AC值(此函數(shù)也是讀忙值函數(shù))
                                        if(ac != 0x00 && ac != 0x40)        //限制光標(biāo)左移的邊界
                                        {
                                                x_1602(0,0x10);        //光標(biāo)左移 <
                                                gbkz(0);        //光標(biāo)控制
                                        }
                                        fmq();
                                        break;
                                }
                                case 0x40:        //光標(biāo)右移
                                {
                                        m_1602(1);        //讀取AC值(此函數(shù)也是讀忙值函數(shù))
                                        x_1602(0,0x16);        //光標(biāo)右移
                                        fmq();
                                        gbkz(1);        //光標(biāo)控制
                                        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:        //退出設(shè)置
                                {
                                        fmq();
                                        if(k)
                                        {
                                                szjc(ls);        //檢查設(shè)置的數(shù)據(jù)是否合理
                                                j = addr_m;        //起始地址(秒)
                                                x_1302(addr_xbh,0,0);        //關(guān)閉寫保護
                                                x_1302(addr_ram,ls[7],1);        //把世紀(jì)寫入ram1
                                                x_1302(addr_ram2,ls[6],1);        //把年寫入ram2
                                                for(i=0;i<7;i++)        //循環(huán)寫入秒,分,時,日,月,周,年
                                                {
                                                        if(ls[ i] != zt[ i])        //判斷數(shù)據(jù)是否有修改,有修改則寫入數(shù)據(jù)否則不寫數(shù)據(jù)
                                                        {
                                                                x_1302(j,ls[ i],1);        //寫入數(shù)據(jù)
                                                        }
                                                        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]);        //把數(shù)據(jù)寫入EEPROM
                                                        DelayMs(1);
                                                }
                                        }
                                        i=0;        //退出設(shè)置大循環(huán)
                                        csh_wnl();        //初始化萬年歷(作用是重新顯示)
                                        break;
                                }
                                case 0x15:        //取消(退出設(shè)置)
                                {
                                        fmq();
                                        i=0;
                                        csh_wnl();
                                        break;
                                }
                        }
                        JM=0;        //按鍵對應(yīng)的代碼執(zhí)行完畢。
                }
        }
        x_1602(0,0x0c);        //關(guān)閉光標(biāo)
}

void szjc(unsigned char *p)        //設(shè)置數(shù)據(jù)檢查
{
        unsigned char i;
        if(p[0]>59)        //判斷秒是否超過上限(超過就強制將秒設(shè)置成59)
        {
                p[0] = 59;        //秒的上限
        }
        if(p[1]>59)        //判斷分是否超過上限(超過就強制將分設(shè)置成59)
        {
                p[1] = 59;        //分的上限
        }
        if(p[2]>23)        //判斷小時是否超過上限(超過就強制將小時設(shè)置成23)
        {
                p[2] = 23;        //時的上限
        }
        if(p[7] > 99)        //判斷世紀(jì)是否超過上限(超過就強制將世紀(jì)設(shè)置成99)
        {
                p[7] = 99;        //世紀(jì)的上限
        }
        if(p[6] > 99)        //判斷年是否超過上限(超過就強制將年設(shè)置成99)
        {
                p[6] = 99;        //年的上限
        }
        if(p[4]>12)        //判斷月是否超過上限(超過就強制將月設(shè)置成12)
        {
                p[4] = 12;        //月的上限
        }
        if(p[4]==4||p[4]==6||p[4]==9||p[4]==11)        //設(shè)置日期上限
        {
                i = 30;        //小月份的日期上限
        }
        else if(p[4] == 2)        //設(shè)置日期上限
        {
                int n;
                n = p[7]*100 + p[6];
                i=rn(n);        //判斷閏年 (如果是閏年返回1)
                i += 28;        //2月份的日期上限
        }
        else        //設(shè)置日期上限
        {
                i = 31;        //大月份的如期上限
        }
        if(p[3] > i)        //判斷日期是否超過上限(超過就強制將日期設(shè)置成i(i的結(jié)果有后面4種:31,30,29,28))
        {
                p[3] = i;        //日期的上限
        }
        if(p[5] > 7)        //判斷星期是否超過上限(超過就強制將星期設(shè)置成59)
        {
                p[5] = 7;        //星期的上限
        }
}

void csh_1602()         //初始化1602
{
        x_1602(0,0x38);        //顯示模式設(shè)置
        DelayMs(5);
        x_1602(0,0x38);
        DelayMs(5);
        x_1602(0,0x38);
        DelayMs(5);
        x_1602(0,0x38);
        x_1602(0,0x0c);        //顯示光標(biāo)
        x_1602(0,0x01);        //顯示清屏
        //x_1602(0,0x06);        //顯示光標(biāo)移動設(shè)置
}

void zdy_1602()        //1602自定義字符
/*
1602自定義的字符是存儲在CGRAM中的
1、CGRAM的地址是:0x40~0x7f 共有:64bit位,每個地址對應(yīng)8bit位,可以存儲8個自定義字符
2、怎么使用自定義字符:0~7對應(yīng)8個自定義字符 zf_1602(10,1,0x00)這條代碼就對應(yīng)著年;
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}};        //小鬧鐘圖標(biāo)
        unsigned char i,j,k=0x40;        //k是CGRAM地址的起始點
        for(i=0;i<5;i++)        //年,月,入,攝氏度"點"
        {
                for(j=0;j<8;j++)        //年,月,入,攝氏度"點"的數(shù)據(jù)寫入
                {
                        x_1602(0,k);        //寫入CGRAM地址
                        x_1602(1,zf[ i][j]);        //寫入數(shù)據(jù)
                        k++;        //CGRAM地址加1(提高一位)
                }
        }
        
}

void csh_wnl()        //初始化萬年歷
{
        unsigned char j,i;        //臨時變量
        i = addr_m;        //起始讀取地址(秒)
        x_1602(0,0x01);
        for(j=0;j<7;j++)        //循環(huán)讀取秒,分,時,日,月,周,年
        {
                zt[j] = d_1302(i,1);
                i += 2;
        }
        zt[7] = d_1302(addr_ram,1);        //讀取世紀(jì)
        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));        //寫世紀(jì)高位
        zf_1602(1,0,((zt[7]%10)+48));        //寫世紀(jì)低位
        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()        //年同步(作用是用來同步年和世紀(jì)的)
{
        unsigned char i,j;
        i = d_1302(addr_n,1);        //讀取時鐘芯片的年
        j = d_1302(addr_ram2,1);        //讀取ram的年
        if(i != j)        //判斷是否一樣。一樣說明在單片機關(guān)機的時候年沒有變化
        {
                x_1302(addr_xbh,0,0);        //關(guān)閉寫保護
                if(j == 99)        //判斷ram中的年是否是99如果是世紀(jì)加1
                {
                        j = d_1302(addr_ram,1);        //讀取RAM中的世紀(jì)
                        j++;        //世紀(jì)加1
                        if(j == 100)        //判斷世紀(jì)是否等于100
                        {
                                x_1302(addr_ram,0,1);        //等于100世紀(jì)清零然后寫入ram
                        }
                        else
                        {
                                x_1302(addr_ram,j,1);        //否則把加1后的世紀(jì)寫入ram
                        }
                }
                x_1302(addr_ram2,i,1);        //如果j不等于99則把時鐘芯片里面的年寫入ram
                x_1302(addr_xbh,0x80,0);        //關(guān)閉寫保護
               
        }
}

void dnz(unsigned char *p)        //讀取鬧鐘數(shù)據(jù)
{
        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:鬧鐘響鈴標(biāo)志位
        csh_1602();        //初始化1602
        qp_1602();        //清屏
        zdy_1602();        //自定義字符
        csh_wbzd_0();        //初始化外部中斷0
        csh_dsq_0();        //初始化定時器0
        ntb();        //年份同步
        csh_wnl();        //初始化萬年歷所有顯示緩存
        dnz(nz);        //讀取鬧鐘時間與鬧鐘的狀態(tài)
        if(nz[3])
        {
                zf_1602(12,0,0x04);
        }
        while(1)
        {
                        
                if(JS)        //脈沖接收結(jié)束后調(diào)用解碼函數(shù)解碼
                {
                        hwjm();
                }
                if(JM)        //解碼完成后調(diào)用按鍵匹配函數(shù)
                {
                        switch(jmsj[2])
                        {
                                case 0x45:fmq();xhc(1);break;        //修改時間
                                case 0x46:
                                {
                                        fmq();xhc(0);dnz(nz);        //修改鬧鐘
                                        break;
                                }
                                case 0x47:
                                {
                                        fmq();
                                        if(!l)        //判斷是否響鈴了。如果響鈴了就先關(guān)閉響鈴
                                        {
                                                if(nz[3])        //判斷是否關(guān)閉鬧鐘
                                                {
                                                        zf_1602(12,0,' ');        //清除鬧鐘小圖標(biāo)
                                                        nz[3] = 0;        //關(guān)閉鬧鐘
                                                }
                                                else
                                                {
                                                        zf_1602(12,0,0x04);        //顯示鬧鐘小圖標(biāo)
                                                        nz[3] = 1;        //啟用鬧鐘
                                                }
                                                i2_sj_x(3,nz[3]);
                                        }
                                        else
                                        {
                                                l = 0;        //關(guān)閉響鈴
                                        }
                                                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);        //讀取世紀(jì)
                                                        
                                                        zf_1602(0,0,((zt[7]/10)+48));        //寫世紀(jì)高位
                                                        zf_1602(1,0,((zt[7]%10)+48));        //寫世紀(jì)低位
                                                        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小結(jié)
        1、1602出現(xiàn)數(shù)據(jù)顯示或光標(biāo)移動出現(xiàn)有時顯示或者操作正確有時錯誤
        {        原因:1、程序出現(xiàn)流程或者算法問題
                         2、1602在忙的時候是寫入不了指令或命令的
                                出現(xiàn)第二種情況可以用判忙
        }
         2、RRPROM 讀取單字節(jié)時應(yīng)答ack,在下一次讀取單字節(jié)時會出錯。
                所以 在讀單字節(jié)時應(yīng)答應(yīng)該是NAK。
        3、寫代碼時一定要了解芯片的資料,原理。


本例完整代碼下載地址:http://www.torrancerestoration.com/f/shiz1602.rar

作者: felixno1    時間: 2015-1-13 13:59
感謝樓主!   51hei論壇有你更精彩!。。。。。。
作者: boss654321    時間: 2015-1-15 17:22
比較清晰,不錯喲.感謝分享
作者: looksave    時間: 2015-4-1 20:05
51黑有你更精彩
作者: lesson18    時間: 2015-4-16 22:21
不懂,還是攢一個************************
作者: mirage    時間: 2015-4-17 22:20
不錯喲.感謝分享
作者: kico    時間: 2015-4-23 21:40
怎么沒有個電路圖
作者: 眾生作廢    時間: 2015-5-15 10:21
1602出現(xiàn)數(shù)據(jù)顯示或光標(biāo)移動出現(xiàn)有時顯示或者操作正確有時錯誤
        {        原因:1、程序出現(xiàn)流程或者算法問題
                         2、1602在忙的時候是寫入不了指令或命令的
                                出現(xiàn)第二種情況可以用判忙
        }
         2、RRPROM 讀取單字節(jié)時應(yīng)答ack,在下一次讀取單字節(jié)時會出錯。
                所以 在讀單字節(jié)時應(yīng)答應(yīng)該是NAK。
        3、寫代碼時一定要了解芯片的資料,原理。
作者: fbzsn    時間: 2015-11-22 14:43
寫得很好,很詳細!
作者: bg4icu    時間: 2016-2-25 18:20
謝謝樓主了。能結(jié)識你就更好了。
作者: kingbobo    時間: 2016-6-2 08:23
點個贊
作者: 雙贏電子    時間: 2016-6-5 21:43
熱心樓主辛苦了,學(xué)習(xí)了,謝謝分享
作者: 騰飛的龍    時間: 2016-6-9 11:14
謝謝樓主無私分享大伙跟著學(xué)習(xí)啦,贊。。。。。
作者: aa3314xxx    時間: 2016-7-7 22:47
求助。那個精度在哪段程序可以修改?我的12m晶振半天有快1分多
作者: aa3314xxx    時間: 2016-7-7 22:48
好好好,好資料,51黑有你更精彩
作者: Jodelin    時間: 2017-4-8 18:21
樓主的程序挺好的,寫的很詳細
作者: _少年放肆的夢    時間: 2017-4-30 09:22
點個贊,51黑有你更精彩
作者: c03131401    時間: 2017-5-3 08:21
注釋清晰,就喜歡這種代碼!加油!
作者: 天使之淚雨揚    時間: 2017-12-26 16:35
程序注釋非常清晰,謝謝樓主分享!
作者: 周鑫鑫鑫666    時間: 2017-12-28 10:51
可以用一下1602的突發(fā)模式,可以避免以上問題
作者: Jerry0925    時間: 2017-12-30 18:23
謝謝分享,收藏了。51黑有你更精彩
作者: panfujie    時間: 2018-1-1 17:04
努力學(xué)習(xí)中!
作者: 359901492    時間: 2018-1-4 13:09
額    有電路圖麼0.0
作者: zsnsony    時間: 2018-1-15 02:03
aa3314xxx 發(fā)表于 2016-7-7 22:47
求助。那個精度在哪段程序可以修改?我的12m晶振半天有快1分多

在定時器初始化那里可以改精度

作者: 都市郎    時間: 2018-1-25 03:32
感謝資料共享,51黑有你更精彩
作者: jefelee    時間: 2018-10-17 09:26
樓主厲害
作者: 鈴戀仙    時間: 2018-10-18 23:50
嘻嘻,樓主厲害了

作者: 13673066100    時間: 2019-7-1 16:42
試試看看怎么樣




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1