`基于电子钟的控制器,其实相当于是闹钟的功能。学习来源是根据宋雪松老师的《手把手教你学单片机》。通过stc89c52+LCD1602+DS1302+矩阵按键+LED小灯实现的。具体现象,到固定时间小灯会自主亮灭。程序分为5个部分。程序所实现的现象为在10:00,10:02,10:05开始亮,在10:01,10:04,10:06开始灭。
protues的仿真图在下方
一:LCD1602液晶模块
- #include
- //控制LCD1602的位
- #define LCD1602_DB P0 //数据位
- ***it LCD1602_RS = P2^6; //指令/数据选择信号
- ***it LCD1602_RW = P2^5; //读写选择信号
- ***it LCD1602_E = P2^7; //使能信号
- /*LCD1602状态检测函数DB7为忙标志位,为1时禁止,为0是工作*/
- void LcdWaitReady()
- {
- unsigned char sta; //用来缓存LCD1602_DB的值
- LCD1602_DB = 0xff; //赋初值
- LCD1602_RS = 0;
- LCD1602_RW = 1;
- do {
- LCD1602_E = 1;
- sta = LCD1602_DB;
- LCD1602_E = 0;
- }while (sta&0x80); //当DB7为0时结束循环
- }
- /*LCD1602写指令函数,形参cmd-要输入的指令*/
- void LcdWriteCmd(unsigned char cmd)
- {
- LcdWaitReady(); //检测状态函数
- LCD1602_RS = 0; //根据LCD1602时序图所写
- LCD1602_RW = 0;
- LCD1602_DB = cmd;
- LCD1602_E = 1;
- LCD1602_E = 0;
- }
- /*LCD1602设置坐标函数,形参(x,y)-书写坐标*/
- void LcdSetCursor(unsigned char x, unsigned char y)
- {
- unsigned char addr; //书写地址
- if (y == 0)
- addr = x + 0x00;
- else
- addr = x + 0x40;
- LcdWriteCmd(addr | 0x80); //写入书写的地址
- }
- /*LCD1602写入数据函数,形参dat-要写入的数据*/
- void LcdWriteDat(unsigned char dat)
- {
- LcdWaitReady(); //状态检测函数
- LCD1602_RS = 1;
- LCD1602_RW = 0;
- LCD1602_DB = dat;
- LCD1602_E = 1;
- LCD1602_E = 0;
- }
- /*LCD1602开光标函数*/
- void LcdOpenCursor()
- {
- LcdWriteCmd(0x0f);
- }
- /*LCD1602关闭光标函数*/
- void LcdCloseCursor()
- {
- LcdWriteCmd(0x0c);
- }
- /*LCD1602显示字符函数,形参(x,y)-代表书写坐标,str-所写字符串的指针*/
- void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
- {
- LcdSetCursor(x, y); //设置坐标函数
- while (*str != '\0')
- {
- LcdWriteDat(*str++); //写数据函数
- }
- }
- /*LCD1602液晶初始化函数*/
- void InitLcd1602()
- {
- LcdWriteCmd(0x38); //写指令函数。功能设置,8位数据总线,两行显示,5*7点阵
- LcdWriteCmd(0x0c); //显示开关控制,开显示,游标不显示
- LcdWriteCmd(0x06); //完成一个字符码传送后,光标右移,地址自动加1
- LcdWriteCmd(0x01); //清显示
- }
复制代码
二:矩阵按键模块
- #include
- //控制按键的位
- ***it keyout1 = P1^7;
- ***it keyout2 = P1^6;
- ***it keyout3 = P1^5;
- ***it keyout4 = P1^4;
- ***it keyin1 = P1^3;
- ***it keyin2 = P1^2;
- ***it keyin3 = P1^1;
- ***it keyin4 = P1^0;
- unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的印射表
- {'1', '2', '3', 0x26}, //数字键1,数字键2,数字键3,向上键
- {'4', '5', '6', 0x25}, //数字键4,数字键5,数字键6,向左键
- {'7', '8', '9', 0x28}, //数字键7,数字键8,数字键9,向下键
- {'0', 0x1b, 0x0d, 0x27} //数字键0,ESC键,回车键,向右键
- };
- //用来保存按键的状态
- unsigned char keysta[4][4] = {
- {1, 1, 1, 1},{1, 1, 1, 1},{1, 1, 1, 1},{1, 1, 1, 1}
- };
- extern void KeyAction(unsigned char keycode);
- /*按键驱动函数,用来调用按键*/
- void KeyDriver()
- { //用来保存上一次按键的状态
- static unsigned char backup[4][4] = {
- {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
- };
- unsigned char i;
- unsigned char j;
- for (i = 0; i < 4; i++) //构建两层循环来完成对矩阵按键的扫描
- {
- for (j = 0; j < 4; j++)
- {
- if (keysta[i][j] != backup[i][j])
- {
- if (backup[i][j] != 0)
- {
- KeyAction(KeyCodeMap[i][j]); //通过按键位置来执行相应的程序
- }
- backup[i][j] = keysta[i][j];
- }
- }
- }
- }
- /*按键扫描函数,通过定时器0中断来完成1ms扫描一次*/
- void KeyScan()
- {
- static unsigned char keyout = 0;
- static unsigned char keybuff[4][4] = { //用来消抖
- {0xff, 0xff, 0xff, 0xff},{0xff, 0xff, 0xff, 0xff},
- {0xff, 0xff, 0xff, 0xff},{0xff, 0xff, 0xff, 0xff}
- };
- unsigned char i;
- //将一行的4个按键值移入缓冲区
- keybuff[keyout][0] = (keybuff[keyout][0] << 1)|keyin1;
- keybuff[keyout][1] = (keybuff[keyout][1] << 1)|keyin2;
- keybuff[keyout][2] = (keybuff[keyout][2] << 1)|keyin3;
- keybuff[keyout][3] = (keybuff[keyout][3] << 1)|keyin4;
- //消抖后更新按键状态
- for (i = 0; i < 4; i++)
- {
- if ((keybuff[keyout][i]&0x0f) == 0x0f)
- {//连续4次扫描值为1,即4*4ms内都是弹起状态时,可以认为按键已稳定弹起
- keysta[keyout][i] = 1;
- }
- else if ((keybuff[keyout][i]&0x0f) == 0x00)
- {//连续4次扫描值为0,即4*4ms内都是按下状态时,可以认为按键已稳定按下
- keysta[keyout][i] = 0;
- }
- }
- keyout++;
- if (keyout >= 4)
- keyout = 0;
- switch (keyout)
- { //每次拉低一个按键的值
- case 0:keyout1=0;keyout2=1;keyout3=1;keyout4=1;break;
- case 1:keyout1=1;keyout2=0;keyout3=1;keyout4=1;break;
- case 2:keyout1=1;keyout2=1;keyout3=0;keyout4=1;break;
- case 3:keyout1=1;keyout2=1;keyout3=1;keyout4=0;break;
- default:break;
- }
- }
复制代码
三:DS1302时钟芯片模块(单字节读出函数未使用)
- #include
- #include
- //控制DS1302时钟的位
- ***it DS1302_SK = P3^6; //时钟信号端,通过控制其高低电平来输入/输出数据
- ***it DS1302_IO = P3^4; //输入输出数据端
- ***it DS1302_CE = P3^5; //使能端,高电平时有效
- struct sTime { //定义时间结构体
- unsigned char year; //年
- unsigned char mon; //月
- unsigned char day; //日
- unsigned char hour; //时
- unsigned char min; //分
- unsigned char sec; //秒
- unsigned char week; //星期
- };
- /*DS1302读出函数,根据时序图编写,返回值为读出的数据*/
- unsigned char DS1302ByteRead()
- {
- unsigned char dat = 0;
- unsigned char mask;
- for (mask=0x01; mask!=0; mask<<=1)
- {
- if (DS1302_IO != 0)
- dat |= mask;
- DS1302_SK = 1; //先准备好数据,然后拉高SK
- _nop_();
- DS1302_SK = 0; //拉低SK
- _nop_();
- }
- return dat;
- }
- /*DS1302写入函数,根据时序图编写,dat-写入的数据*/
- void DS1302ByteWrite(unsigned char dat)
- {
- unsigned char mask;
- for (mask=0x01; mask!=0; mask<<=1)
- {
- if ((mask&dat) != 0) //将输入数据的位从最低位依次输入
- DS1302_IO = 1;
- else
- DS1302_IO = 0;
- DS1302_SK = 1; //先准备好数据,拉高SK,数据采样
- _nop_();
- DS1302_SK = 0; //拉低SK,数据输出
- _nop_();
- }
- DS1302_IO = 1; //释放IO引脚
- }
- /*DS1302单字节读出函数,addr-写入要读字节的寄存器地址,返回值为读出的数据*/
- unsigned char DS1302SingleRead(unsigned char addr)
- {
- unsigned char dat;
- DS1302_CE = 1;
- _nop_();
- DS1302ByteWrite(addr);
- dat = DS1302ByteRead();
- DS1302_CE = 0;
- _nop_();
- return dat;
- }
- /*DS1302单字节写入函数,addr-写入字节的寄存器地址,dat-写入数据*/
- void DS1302SingleWrite(unsigned addr, unsigned dat)
- {
- DS1302_CE = 1;
- _nop_();
- DS1302ByteWrite(addr);
- DS1302ByteWrite(dat);
- DS1302_CE = 0;
- _nop_();
- }
- /*DS1302Burst写入模式,一次写入8个字节,dat-写入数据的指针*/
- void DS1302BurstWrite(unsigned char *dat)
- {
- unsigned char i;
- DS1302_CE = 1;
- _nop_();
- DS1302ByteWrite(0xbe);
- for (i = 0; i < 8; i++)
- {
- DS1302ByteWrite(*dat++);
- }
- DS1302_CE = 0;
- _nop_();
- }
- /*DS1302Burst读出模式,一次读出8个字节,dat-存储读出数据的指针*/
- void DS1302BurstRead(unsigned char * dat)
- {
- unsigned char i;
- DS1302_CE = 1;
- _nop_();
- DS1302ByteWrite(0xbf);
- for (i = 0; i < 8; i++)
- {
- *dat++ = DS1302ByteRead();
- }
- DS1302_CE = 0;
- _nop_();
- }
- /*获取实时时间,time-存储时间的结构体*/
- void GetRealTime(struct sTime * time)
- {
- unsigned char buf[8];
- DS1302BurstRead(buf);
- time->year = buf[6] + 0x2000;
- time->week = buf[5];
- time->mon = buf[4];
- time->day = buf[3];
- time->hour = buf[2];
- time->min = buf[1];
- time->sec = buf[0];
- }
- /*设置实时时间,dat-要写入的数据的结构体*/
- void SetRealTime(struct sTime * dat)
- {
- unsigned char buf[8];
- buf[7] = 0x00;
- buf[6] = dat->year;
- buf[5] = dat->week;
- buf[4] = dat->mon;
- buf[3] = dat->day;
- buf[2] = dat->hour;
- buf[1] = dat->min;
- buf[0] = dat->sec;
- DS1302BurstWrite(buf);
- }
- /*初始化DS1302函数*/
- void InitDS1302()
- {
- struct sTime code InitTime[] = {0x2019, 0x10, 0x11, 0x09, 0x59, 0x50, 0x05};
- DS1302_CE = 0;
- _nop_(); //延时一个机器周期
- DS1302_SK = 0;
- _nop_();
- DS1302SingleWrite(0x8e, 0x00); //撤销写保护,0x8e-写保护位的地址,0x00-撤销写保护
- SetRealTime(InitTime); //设置初始时间
- DS1302SingleWrite(0x8e, 0x80); //添加写保护
- }
复制代码
四:设置时间函数
- #include
- struct sTime { //定义时间结构体
- unsigned char year; //年
- unsigned char mon; //月
- unsigned char day; //日
- unsigned char hour; //时
- unsigned char min; //分
- unsigned char sec; //秒
- unsigned char week; //星期
- };
- struct sTime bufTime; //时间缓冲区
- unsigned char setIndex = 0; //设置标志位
- /*函数声明*/
- extern void RefreshTime();
- extern void LcdSetCursor(unsigned char x, unsigned char y);
- extern void SetRealTime(struct sTime * dat);
- extern void LcdCloseCursor();
- extern void LcdOpenCursor();
- /*刷新当前设置位的光标指示*/
- void RefreshSetShow()
- {
- switch(setIndex)
- {
- case 1:LcdSetCursor(2, 0);break;
- case 2:LcdSetCursor(3, 0);break;
- case 3:LcdSetCursor(5, 0);break;
- case 4:LcdSetCursor(6, 0);break;
- case 5:LcdSetCursor(8, 0);break;
- case 6:LcdSetCursor(9, 0);break;
- case 7:LcdSetCursor(15, 0);break;
- case 8:LcdSetCursor(4, 1);break;
- case 9:LcdSetCursor(5, 1);break;
- case 10:LcdSetCursor(7, 1);break;
- case 11:LcdSetCursor(8, 1);break;
- case 12:LcdSetCursor(10, 1);break;
- case 13:LcdSetCursor(11, 1);break;
- default:break;
- }
- }
- /***********************************************************************/
- //递增或递减分,秒的BCD码
- /*递增一个BCD码的高位*/
- unsigned char IncTimeBcdHigh(unsigned char bcd)
- {
- if ((bcd&0xf0) < 0x50)
- bcd += 0x10;
- else
- bcd &= 0x0f;
- return bcd;
- }
- /*递减一个BCD码的高位*/
- unsigned char DecTimeBcdHigh(unsigned char bcd)
- {
- if ((bcd&0xf0) > 0x00)
- bcd -= 0x10;
- else
- bcd |= 0x50;
- return bcd;
- }
- //递增或递减时,分,秒的BCD码
- /********************************************************************/
- /*递增一个BCD码的高位*/
- unsigned char IncBcdHigh(unsigned char bcd)
- {
- if ((bcd&0xf0) < 0x90)
- bcd += 0x10;
- else
- bcd &= 0x0f;
- return bcd;
- }
- /*递增一个BCD码的低位*/
- unsigned char IncBcdLow(unsigned char bcd)
- {
- if ((bcd&0x0f) < 0x09)
- bcd += 0x01;
- else
- bcd &= 0xf0;
- return bcd;
- }
- /*递减一个BCD码的高位*/
- unsigned char DecBcdHigh(unsigned char bcd)
- {
- if ((bcd&0xf0) > 0x00)
- bcd -= 0x10;
- else
- bcd |= 0x90;
- return bcd;
- }
- /*递减一个BCD码的低位*/
- unsigned char DecBcdLow(unsigned char bcd)
- {
- if ((bcd&0x0f) > 0x00)
- bcd -= 0x01;
- else
- bcd |= 0x09;
- return bcd;
- }
- /*递增周的BCD码*/
- unsigned char IncWeekBcd(unsigned char bcd)
- {
- if ((bcd&0x0f) < 0x07)
- bcd += 0x01;
- else
- bcd = 0x00;
- return bcd;
- }
- /*递减周的BCD码*/
- unsigned char DecWeekBcd(unsigned char bcd)
- {
- if ((bcd&0x0f) > 0x00)
- bcd -= 0x01;
- else
- bcd = 0x07;
- return bcd;
- }
- /*递增时间当前设置位的值*/
- void IncSetTime()
- {
- switch(setIndex)
- {
- case 1:bufTime.year = IncBcdHigh(bufTime.year);break;
- case 2:bufTime.year = IncBcdLow(bufTime.year);break;
- case 3:bufTime.mon = IncBcdHigh(bufTime.mon);break;
- case 4:bufTime.mon = IncBcdLow(bufTime.mon);break;
- case 5:bufTime.day = IncBcdHigh(bufTime.day);break;
- case 6:bufTime.day = IncBcdLow(bufTime.day);break;
- case 7:bufTime.week = IncWeekBcd(bufTime.week);break;
- case 8:bufTime.hour = IncBcdHigh(bufTime.hour);break;
- case 9:bufTime.hour = IncBcdLow(bufTime.hour);break;
- case 10:bufTime.min = IncTimeBcdHigh(bufTime.min);break;
- case 11:bufTime.min = IncBcdLow(bufTime.min);break;
- case 12:bufTime.sec = IncTimeBcdHigh(bufTime.sec);break;
- case 13:bufTime.sec = IncBcdLow(bufTime.sec);break;
- default:break;
- }
- RefreshTime();
- RefreshSetShow();
- }
- /*递减时间当前设置位的值*/
- void DecSetTime()
- {
- switch(setIndex)
- {
- case 1:bufTime.year = DecBcdHigh(bufTime.year);break;
- case 2:bufTime.year = DecBcdLow(bufTime.year);break;
- case 3:bufTime.mon = DecBcdHigh(bufTime.mon);break;
- case 4:bufTime.mon = DecBcdLow(bufTime.mon);break;
- case 5:bufTime.day = DecBcdHigh(bufTime.day);break;
- case 6:bufTime.day = DecBcdLow(bufTime.day);break;
- case 7:bufTime.week = DecWeekBcd(bufTime.week);break;
- case 8:bufTime.hour = DecBcdHigh(bufTime.hour);break;
- case 9:bufTime.hour = DecBcdLow(bufTime.hour);break;
- case 10:bufTime.min = DecTimeBcdHigh(bufTime.min);break;
- case 11:bufTime.min = DecBcdLow(bufTime.min);break;
- case 12:bufTime.sec = DecTimeBcdHigh(bufTime.sec);break;
- case 13:bufTime.sec = DecBcdLow(bufTime.sec);break;
- default:break;
- }
- RefreshTime();
- RefreshSetShow();
- }
- /*光标左移函数*/
- void LeftShift()
- {
- if (setIndex != 0)
- {
- if (setIndex > 1)
- {
- setIndex--;
- }
- else
- {
- setIndex = 13;
- }
- RefreshSetShow();
- }
- }
- /*光标右移函数*/
- void RightShift()
- {
- if (setIndex != 0)
- {
- if (setIndex < 13)
- {
- setIndex++;
- }
- else
- {
- setIndex = 1;
- }
- RefreshSetShow();
- }
- }
- /*退出时间设置函数,save-是否保存标志*/
- void ExitSetTime(bit save)
- {
- setIndex = 0;
- if (save)
- {
- SetRealTime(&bufTime);
- }
- LcdCloseCursor();
- }
- /*进入时间设置函数*/
- void EnterSetTime()
- {
- setIndex = 2; //先把设置标志位设为2,再向左移
- LeftShift();
- LcdOpenCursor();
- }
- /*按键执行函数,keycode-所传递的哪一个按键被按下*/
- void KeyAction(unsigned char keycode)
- {
- if (keycode >= '0' && keycode <= '9')
- {
- //数字按键不执行任何动作
- }
- else if (keycode == 0x26) //数字键加
- {
- IncSetTime();
- }
- else if (keycode == 0x28) //数字键减
- {
- DecSetTime();
- }
- else if (keycode == 0x25) //光标向左移动
- {
- LeftShift();
- }
- else if (keycode == 0x27) //光标向右移动
- {
- RightShift();
- }
- else if (keycode == 0x0d) //进入/退出时间设置
- {
- if (setIndex == 0)
- {
- EnterSetTime();
- }
- else
- {
- ExitSetTime(1); //保存数据并退出
- }
- }
- else if (keycode == 0x1b) //取消键
- {
- ExitSetTime(0); //不保存数据
- }
- }
复制代码
五:主程序
- #include
- //控制小灯的位
- ***it led1 = P2^0;
- ***it led2 = P2^1;
- ***it led3 = P2^2;
- //小灯开启时间表
- ledstarthour[3] = {0x10, 0x10, 0x10};
- ledstartmin[3] = {0x00, 0x02, 0x05};
- //小灯关闭时间表
- ledclosehour[3] = {0x10, 0x10, 0x10};
- ledclosemin[3] = {0x01, 0x04, 0x06};
- struct sTime { //定义时间结构体
- unsigned char year; //年
- unsigned char mon; //月
- unsigned char day; //日
- unsigned char hour; //时
- unsigned char min; //分
- unsigned char sec; //秒
- unsigned char week; //星期
- };
- bit flag200ms = 0; //定时标志
- extern struct sTime bufTime; //时间缓冲区
- extern unsigned char setIndex ; //设置标志位
- unsigned char T0RH = 0; //定时器的高8位
- unsigned char T0RL = 0; //定时器的低8位
- /*函数声明*/
- extern void KeyScan();
- void configTimer0(unsigned char ms);
- extern void InitLcd1602();
- extern void InitDS1302();
- extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
- extern void KeyDriver();
- extern void GetRealTime(struct sTime * time);
- void RefreshTime();
- void LedStartMonitor();
- void LedCloseMonitor();
- void main()
- {
- unsigned char psec = 0xaa; //秒备份
- EA = 1; //开总中断
- configTimer0(1); //为定时器0配置时间
- InitLcd1602(); //初始化液晶显示屏
- InitDS1302(); //初始化时钟
- //显示液晶上固定的位
- LcdShowStr(0, 0, "20 - - week ");
- LcdShowStr(4, 1, " : : ");
- while (1)
- {
- KeyDriver(); //按键驱动
- if (flag200ms && (setIndex == 0))
- {
- flag200ms = 0;
- GetRealTime(&bufTime); //获取实时时间
- LedStartMonitor();
- LedCloseMonitor();
- if (psec != bufTime.sec) //如果秒发生变化,就刷新时间
- {
- RefreshTime();
- psec = bufTime.sec;
- }
- }
- }
- }
- /*监控时间开启小灯函数,当到达指定时间,小灯亮*/
- void LedStartMonitor()
- {
- static unsigned char i = 0;
- if ((bufTime.hour == ledstarthour[i])&&(bufTime.min == ledstartmin[i]))
- {
- led1 = 0;
- led2 = 0;
- led3 = 0;
- i++;
- if (i >= 3)
- i = 0;
- }
- }
-
- /*监控时间关闭小灯函数,当到达指定时间,小灯灭*/
- void LedCloseMonitor()
- {
- static unsigned char i = 0;
- if ((bufTime.hour == ledclosehour[i])&&(bufTime.min == ledclosemin[i]))
- {
- led1 = 1;
- led2 = 1;
- led3 = 1;
- i++;
- if (i >= 3)
- i = 0;
- }
- }
- /*将BCD码转换为字符串*/
- void ShowBcdByte(unsigned char x, unsigned char y, unsigned char dat)
- {
- unsigned char buf[3];
- if (x == 15) //对“周”的特殊照顾
- {
- buf[0] = (dat&0x0f)+'0';
- buf[1] = '\0';
- }
- else
- {
- buf[0] = (dat>>4)+'0';
- buf[1] = (dat&0x0f)+'0';
- buf[2] = '\0';
- }
- LcdShowStr(x, y, buf);
- }
- /*刷新时间函数*/
- void RefreshTime()
- {
- ShowBcdByte(2, 0, bufTime.year);
- ShowBcdByte(5, 0, bufTime.mon);
- ShowBcdByte(8, 0, bufTime.day);
- ShowBcdByte(15, 0, bufTime.week);
- ShowBcdByte(4, 1, bufTime.hour);
- ShowBcdByte(7, 1, bufTime.min);
- ShowBcdByte(10, 1, bufTime.sec);
- }
- /*配置定时器0*/
- void configTimer0(unsigned char ms)
- {
- unsigned long temp;
- temp = 11059200 / 12;
- temp = (temp*ms)/1000;
- temp = 65536 - temp;
- T0RH = (unsigned char)(temp >> 8);
- T0RL = (unsigned char)temp;
- TMOD &= 0xf0;
- TMOD |= 0x01;
- TH0 = T0RH;
- TL0 = T0RL;
- TR0 = 1;
- ET0 = 1; //打开定时器0中断
- }
- /*中断函数*/
- void InterruptTimer0() interrupt 1
- {
- static unsigned char tmr200ms = 0;
- TH0 = T0RH;
- TL0 = T0RL;
- KeyScan();
- tmr200ms++;
- if (tmr200ms >= 200)
- {
- tmr200ms = 0;
- flag200ms = 1;
- }
- }
复制代码
`
|