郭天祥老师实验板做的时钟,代码每一行都有备注;非常适合初学者!因为我也是初学者,可以一起交流啊。
所用技术:定时器,矩阵键盘,1602液晶,51单片机,中断,I2C保存时间,串口通信,串口缓冲区(核心),串口协议头(核心),时间缓冲区(核心),定时器复用技术(核心),状态机编程思想(核心),多文件编程技术,预处理技术等等,非常适合初学者。先看大概的,然后你决定要不要下载整个代码。
/********************************************************* 文件名称:shizhong.C 主控芯片:STC89C52RC 晶振11.0592MHz 功能描述:时钟(1602,IIC,按键,串行接口) 作者日期:田卫卫 / 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}; //时间缓冲区 void ClockInit() { // Write(clock,1,3); //////////////////////////调试初始化用的,把“时:分:秒”写入I2C。 Delayms(1000); //上电后等电压稳定 Read(1,3,clock); /*从I2C器件第1个存储区开始,连续读取3个字节,放入时间缓冲区中*/ Lcd_init(); /*LCD1602液晶初始化函数*/ WRLCD(); //把时间缓冲区“时:分:秒”,写在液晶屏上。 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(); //把时间缓冲区“时:分:秒”,写在液晶屏上。 } } /********************************************************* 文件名称:sio.c 主控芯片:STC89C52RC 晶振11.0592MHz 功能描述:串口程序 函数名称:含串口中断函数!其余函数详见头文件sio.h 作者日期:田卫卫 / 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 //协议头数;设置为0,可以关闭协议头。 uchar code SIOADD[SIOADDNUM]={0xeb,0x00,0xaa}; //定义串口协议头 uchar inbuf[BUFS]; //串口接收缓冲区 uchar buf = 0; //统计串口接收到本次数据包的总数量。 /********************************************************* 函数名称:sio_service(); 功能描述:串口服务程序,处理串口接收缓冲区inbuf[]的数据。 输入参数:全局变量 buf,TaskMark[0] 包含函数:send(uchar dat); 作者日期:田卫卫 / 2013年2月21日 *********************************************************/ void sio_service() { if(TaskMark[0] != 0) //如果为真,说明收到了数据,而且本次数据包接收已经结束。 { uchar i=0,buf_temp=0; //串口缓冲区服务程序当前指向位置。 while (buf_temp < SIOADDNUM) //协议头处理核心:循环SIOADDNUM次,判断协议头。 { if (inbuf[buf_temp] == SIOADD) //协议头处理核心:协议头相同 { i++; //协议头处理核心:指向下一个要判断的协议头 buf_temp++; //协议头处理核心:指向下一个要判断的协议头 } else //协议头处理核心:协议头错误 { buf_temp = 0; //协议头处理核心:清零 buf = 0; //协议头处理核心:清零 break; //协议头处理核心:退出本次循环 } } if (buf_temp < buf) { if (inbuf[buf_temp]==0 | inbuf[buf_temp]==1) //判断第4字节是0,还是1;用户可以自定义。 { lcdflag = inbuf[buf_temp]; //把串口接收到得第4个字节给LCD显示标记 buf_temp++; //指向下一个串口接收到得元素 WRLCD(); //写液晶屏。 } } ES = 0; //先关中断 while (buf_temp < buf) //协议头,如果相等,才会跑到这儿 { send(inbuf[buf_temp]); //调用串口发送函数,把协议头以后所接收到的数据原封不动发给上位机 buf_temp++; //指向下一个要发送的数据 } ES = 1; //刚才关了中断,现在要打开,以备下次接收。 TaskMark[0] = 0; // 此任务的定时标志清零 buf = 0; //关键:统计串口接收到本次数据包的总数量--清零。 } } /********************************* 函数名:sio_int(sio interrupt) 功能:中断+缓冲区方式接收串口数据,把收到的一个字节放在inbuf[bufnum]中. *********************************/ void sio_int() interrupt 4 { ES=0; if(RI) /* RI==1 */ { if (buf < BUFS) //小于缓冲区溢出上限,为真可以接收 { inbuf[buf] = SBUF; //读串口数据,放入缓冲区中 buf++; //缓冲区指向下一个存放位置。 TaskCount[0] = 5; // 启动一个定时器中断延时 TaskMark[0] = 0; // 启动此任务的定时器 } else //大于或等于缓冲区上限,说明本次接收的数据可能被截掉了:错误! { beep = 0; // 蜂鸣器响;切勿删除:可作为串口稳定性的参考。 } RI=0; } ES=1; } /********************************* 函数名:send(uchar dat) 功能:向串口发送数据 *********************************/ 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; /* 清空发送中断标志位 */ RI=0; /* 清空接收中断标志位 */ /* IE、IP寄存器设置 */ PS=1; /* SIO int high 优先级 */ ET1=0; /* 关 定时器1串口中断 */ ES=1; /* 开 串口中断 */ EA=1; /* 开 全局中断 */ TR1=1; /* 启动 定时器1 */ } /********************************* 文件名称:time.C 主控芯片:STC89C52RC 功能描述:延时函数 作者日期:田卫卫 / 2013年2月15日 版 本:V1.0 *********************************************************/ #include"define.H" #include"time.h" #include"dulikey.h" #include 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的任务。任务定义见头文件。这里是核心知识:定时器复用技术。 { if (TaskCount) { TaskCount--; if (TaskCount == 0) //只有被定时器减的TaskCount,才置标志位! { TaskMark = 0x01; } } } KeyScan(); } //////////////////////////////////////////////////////////////////////////////////////// void Delayms(uint xms) { uint i,j; for(i=0;i for(j=0;j<110;j++); } //////////////////////////////////////////////////////////////////////////////////////// /*NOP延时函数,单位 微秒*/ void delay() { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); }
|