| [51單片機] 初學者自制的時鐘:每行代碼都有備注,進來不虧 郭天祥老師實驗板做的時鐘,代碼每一行都有備注;非常適合初學者!因為我也是初學者,可以一起交流啊。 所用技術:定時器,矩陣鍵盤,1602液晶,51單片機,中斷,I2C保存時間,串口通信,串口緩沖區(qū)(核心),串口協(xié)議頭(核心),時間緩沖區(qū)(核心),定時器復用技術(核心),狀態(tài)機編程思想(核心),多文件編程技術,預處理技術等等,非常適合初學者。先看大概的,然后你決定要不要下載整個代碼。 /********************************************************* 文件名稱:shizhong.C  主控芯片:STC89C52RC     晶振11.0592MHz 功能描述:時鐘(1602,IIC,按鍵,串行接口) 作者日期:田衛(wèi)衛(wèi) / 2013年2月22日 版    本:V1.1 *********************************************************/ #include"define.H"        //公共頭文件,可替代reg52.h #include"dulikey.h"              //按鍵頭文件:按鍵掃描、按鍵服務 #include"LCD1602.h"           //1602液晶頭文件 #include"clock.h"          //時鐘頭文件:初始化時鐘、及時鐘走時分秒,每秒寫I2C保存時間 #include"time.h"           //定時器、延時函數等 #include"sio.h"             //串口初始化、接收、串口服務函數等。 void main() {             sio_init();       //串口初始化函數        InitTimer0();  //定時器0初始化函數        ClockInit();    //時鐘顯示界面初始化        while(1)        {               KeyService(clock);        //調用鍵盤服務函數               sio_service();         //調用串口服務函數               WRclock ();    //調用時鐘計時函數        }             } #include"define.H" #include"clock.h" #include"time.h" #include"LCD1602.h" #include"I2C.h" #include"dulikey.h" uchar code clock1[]="I Like Clock";    //液晶第一排 uchar clock[]={23,59,50};    //時間緩沖區(qū) void ClockInit() { //    Write(clock,1,3);    //////////////////////////調試初始化用的,把“時:分:秒”寫入I2C。              Delayms(1000);            //上電后等電壓穩(wěn)定        Read(1,3,clock);     /*從I2C器件第1個存儲區(qū)開始,連續(xù)讀取3個字節(jié),放入時間緩沖區(qū)中*/        Lcd_init();                           /*LCD1602液晶初始化函數*/        WRLCD();     //把時間緩沖區(qū)“時:分:秒”,寫在液晶屏上。        TaskCount[1] = 500;             // TO進中斷的次數:500次        TaskMark[1]  = 0;        // 啟動此任務的定時器 } void WRclock ()    //時鐘計時程序 {        if (TaskMark[1] == 1)  //1秒標記,定時器中斷2ms*500次;        {               clock[2]++;           //秒+1               TaskMark[1] = 0;          //1秒標記 清零               if (clock[2]==60)   //秒 = 60?               {                      clock[2]=0;            //秒=60!秒=0;                      clock[1]++;           //分+1                      if (clock[1]==60)   //分 = 60?                      {                             clock[1]=0;            //分=60!分=0;                             clock[0]++;           //時+1                             if (clock[0]==24)   //時= 24?                                    clock[0]=0;            //時=24!時=0;                                       }               }               Write(&clock[0],1,3);    //I2C寫               WRLCD();     //把時間緩沖區(qū)“時:分:秒”,寫在液晶屏上。        } } /********************************************************* 文件名稱:sio.c           主控芯片:STC89C52RC            晶振11.0592MHz 功能描述:串口程序 函數名稱:含串口中斷函數!其余函數詳見頭文件sio.h 作者日期:田衛(wèi)衛(wèi) / 2013年2月21日 版    本:V1.0 *********************************************************/ #include"define.h" #include"sio.h" #include"time.h" #include"lcd1602.h" #define T1MS_1200bps   0xe8;     /* (e8,-24,SMOD=0) @1200bps  pcon&=0x7f @11.0592MHz*/ #define T1MS_2400bps   0xf4;     /* (f4,-12,SMOD=0) @2400bps  pcon&=0x7f */ #define T1MS_4800bps   0xfa;     /* (fa, -6,SMOD=0) @4800bps  pcon&=0x7f */ #define T1MS_9600bps   0xfd;     /* (fd, -3,SMOD=0) @9600bps  pcon&=0x7f */ #define T1MS_19k2bps   0xfd;     /* (fd, -3,SMOD=1) @19.2kbps pcon|=0x80 */ #define SIOADDNUM  3     //協(xié)議頭數;設置為0,可以關閉協(xié)議頭。 uchar code SIOADD[SIOADDNUM]={0xeb,0x00,0xaa};  //定義串口協(xié)議頭 uchar inbuf[BUFS];              //串口接收緩沖區(qū) uchar buf = 0;               //統(tǒng)計串口接收到本次數據包的總數量。 /********************************************************* 函數名稱:sio_service(); 功能描述:串口服務程序,處理串口接收緩沖區(qū)inbuf[]的數據。 輸入參數:全局變量 buf,TaskMark[0] 包含函數:send(uchar dat); 作者日期:田衛(wèi)衛(wèi) / 2013年2月21日 *********************************************************/ void sio_service() {        if(TaskMark[0] != 0)     //如果為真,說明收到了數據,而且本次數據包接收已經結束。        {               uchar i=0,buf_temp=0;         //串口緩沖區(qū)服務程序當前指向位置。               while (buf_temp < SIOADDNUM)      //協(xié)議頭處理核心:循環(huán)SIOADDNUM次,判斷協(xié)議頭。               {                      if (inbuf[buf_temp] == SIOADD)           //協(xié)議頭處理核心:協(xié)議頭相同                      {                             i++;               //協(xié)議頭處理核心:指向下一個要判斷的協(xié)議頭                             buf_temp++;   //協(xié)議頭處理核心:指向下一個要判斷的協(xié)議頭                      }                      else                       //協(xié)議頭處理核心:協(xié)議頭錯誤                      {                             buf_temp = 0; //協(xié)議頭處理核心:清零                             buf = 0;          //協(xié)議頭處理核心:清零                             break;            //協(xié)議頭處理核心:退出本次循環(huán)                      }                                 }               if (buf_temp < buf)               {                      if (inbuf[buf_temp]==0 | inbuf[buf_temp]==1)          //判斷第4字節(jié)是0,還是1;用戶可以自定義。                      {                             lcdflag = inbuf[buf_temp];    //把串口接收到得第4個字節(jié)給LCD顯示標記                             buf_temp++;   //指向下一個串口接收到得元素                             WRLCD();     //寫液晶屏。                      }               }               ES = 0;   //先關中斷                    while (buf_temp < buf)  //協(xié)議頭,如果相等,才會跑到這兒               {                      send(inbuf[buf_temp]);  //調用串口發(fā)送函數,把協(xié)議頭以后所接收到的數據原封不動發(fā)給上位機                      buf_temp++;          //指向下一個要發(fā)送的數據               }               ES = 1;          //剛才關了中斷,現在要打開,以備下次接收。               TaskMark[0]  = 0;        // 此任務的定時標志清零               buf = 0;                 //關鍵:統(tǒng)計串口接收到本次數據包的總數量--清零。        } } /********************************* 函數名:sio_int(sio interrupt) 功能:中斷+緩沖區(qū)方式接收串口數據,把收到的一個字節(jié)放在inbuf[bufnum]中. *********************************/ void  sio_int() interrupt 4 {        ES=0;        if(RI)        /* RI==1 */        {                 if (buf < BUFS)            //小于緩沖區(qū)溢出上限,為真可以接收               {                      inbuf[buf] = SBUF;       //讀串口數據,放入緩沖區(qū)中                      buf++;           //緩沖區(qū)指向下一個存放位置。                      TaskCount[0] = 5;         // 啟動一個定時器中斷延時                      TaskMark[0]  = 0;        // 啟動此任務的定時器               }               else                       //大于或等于緩沖區(qū)上限,說明本次接收的數據可能被截掉了:錯誤!               {                      beep = 0;        // 蜂鳴器響;切勿刪除:可作為串口穩(wěn)定性的參考。               }                           RI=0;        }        ES=1; } /********************************* 函數名:send(uchar dat) 功能:向串口發(fā)送數據 *********************************/ void send(uchar dat) {        SBUF=dat;        while(!TI);        TI=0; } /********************************* 函數名:sel_bps(select bps) 功能:設置串口波特率 sel為選擇通訊速率:   0=1200,1=2400,2=4800,3=9600,4=19.2k *********************************/ void sel_bps(uchar sel) {      switch(sel)      {         case 0:PCON&=0x7f;                TH1=T1MS_1200bps;   /* T1 use sio */                TL1=T1MS_1200bps;                break;         case 1:PCON&=0x7f;                TH1=T1MS_2400bps;   /* T1 use sio */                TL1=T1MS_2400bps;                break;         case 2:PCON&=0x7f;                TH1=T1MS_4800bps;   /* T1 use sio */                TL1=T1MS_4800bps;                break;         case 3:PCON&=0x7f;                TH1=T1MS_9600bps;   /* T1 use sio */                TL1=T1MS_9600bps;                break;         case 4:PCON|=0x80;                TH1=T1MS_19k2bps;   /* T1 use sio */                TL1=T1MS_19k2bps;                break;      } } /********************************* 函數名:tran_init()(tranmit initialize) 功能:串口初始化      串口通訊參數初始化 包含子函數:sel_bps *********************************/ void sio_init()        /* 通訊有關參數初始化 */ { /* 定時器初始化 */        TMOD=0x21;  /* T1=MODE2,sio; T0=MODE1,16bit,use time */        sel_bps(3); /* 選擇通訊速率:0=1200,1=2400,2=4800,3=9600,4=19.2k */ /* SCON寄存器設置 */        SM0=0;        SM1=1; /* SM0=0 SM1=1,mode1,10bit          */        SM2=0; /* data int,無校驗(TB8=bit_duble偶) */        REN=1; /* 允許串口接收 */        TI=0;  /* 清空發(fā)送中斷標志位 */        RI=0;  /* 清空接收中斷標志位 */ /* IE、IP寄存器設置 */      PS=1;  /* SIO int high  優(yōu)先級 */        ET1=0; /* 關 定時器1串口中斷 */        ES=1;  /* 開 串口中斷 */        EA=1;  /* 開 全局中斷 */        TR1=1; /* 啟動 定時器1 */       } /********************************* 文件名稱:time.C        主控芯片:STC89C52RC 功能描述:延時函數 作者日期:田衛(wèi)衛(wèi) / 2013年2月15日 版    本:V1.0 *********************************************************/ #include"define.H" #include"time.h" #include"dulikey.h" #include<intrins.h> uint TaskCount[TASK_NUM];             //定義變量:為定時任務存放定時值; uchar  TaskMark[TASK_NUM];         //標志位,0表示時間沒到,1表示定時的時間到。 /*/////////////////////////////////////////////////////////////////////////////////////// void main(void) {        InitTimer0(); } ///////////////////////////////////////////////////////////////////////////////////////// TaskCount[0] = 20;        // TO進中斷的次數:20次 TaskMark[0]  = 0;        // 啟動此任務的定時器 ///////////////////////////////////////////////////////////////////////////////////////*/ void InitTimer0() {        TMOD = 0x21;        TH0 = 0xF8;        TL0 = 0xcc;        EA = 1;        ET0 = 1;        TR0 = 1; } //////////////////////////////////////////////////////////////////////////////////////// void Timer0Int(void) interrupt 1 {        uchar i;        TH0 = 0xF8;   //定義為2ms中斷一次;中斷太頻繁效率低,中斷太久實時性差。        TL0 = 0xcc;        for (i=0; i<TASK_NUM; i++)     //循環(huán)檢查TASK_NUM的任務。任務定義見頭文件。這里是核心知識:定時器復用技術。        {               if (TaskCount)               {                      TaskCount--;                      if (TaskCount == 0)          //只有被定時器減的TaskCount,才置標志位!                      {                             TaskMark = 0x01;                      }               }        }        KeyScan(); } //////////////////////////////////////////////////////////////////////////////////////// void Delayms(uint xms) {        uint i,j;        for(i=0;i<xms;i++)               for(j=0;j<110;j++); } //////////////////////////////////////////////////////////////////////////////////////// /*NOP延時函數,單位 微秒*/ void delay() {        _nop_();        _nop_();        _nop_();        _nop_();        _nop_();        _nop_();        _nop_(); } | 
|  |