超详细注释的C语言单片机电子时钟
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/*
作者:twtyypmb123
转载请注明出处,谢谢
数码管时钟,整点响铃
24小时制,8位数码管显示格式为HH-MM-SS
本程序使用普中HC6800 V3.0开发板,其他板子大同小异
P0口接8为数码管的段选
P1口接3*4矩阵键盘P1^0为空,只是用了前7个引脚
P2口的2、3、4引脚通过138译码器连接数码管的电源端
*/
#include
#include
#define HOUR_DIS 0xC0 //小时显示控制码
#define MINU_DIS 0x18 //分钟显示控制码
#define SECO_DIS 0x03 //秒显示控制码
#define ON_BAR 0x24 //分隔符显示控制码
#define OFF_DIS 0x00 //关闭数码管显示
#define START_KEY 11 //开始键代码
#define RESET_KEY 12 //复位键代码
#define NULL_KEY 13 //空按键代码
#define REVISE_HOUR 0x04 //修正小时代码
#define REVISE_MINU 0X02 //修正分钟代码
#define REVISE_SECO 0X01 //修正秒代码
#define CLEAR 16 //清除数码管段选代码的数组坐标
#define BAR 17 //分隔符代码的数组坐标
#define INTERRUPT_TIME 10 //每次中断时间,单位毫秒
#define BEEP_DURA 200 //蜂鸣器响时长,单位毫秒
sbit beep = P1^5; //定义蜂鸣器控制
//位选码,控制位选
//如若总线直连更为简单,使用译码器节约接口
unsigned char code bit_select[]={0xe3,0xe7,0xeb,0xef,0xf3,0xf7,0xfb,0xff};
//段选码,8位共阴极数码管的段选都连到P0口
//各个数码管为并联关系
unsigned char code segment_select[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40};//0~F,清空,最后一位为分隔符
unsigned char hour_switch, minute_switch, second_switch;//小时、分钟、秒的显示开关
unsigned char hour[2], minute[2], second[2];//小时、分钟、秒的数组
unsigned int time_count;//中断中的计数器
unsigned int beep_time = 0; //定义蜂鸣器时间变量
bit reset, start, beep_flag;//复位、开始、蜂鸣器的标志位
//初始化函数
void Init(void);
//设定新值函数
void SetNewValue(void);
//键盘扫描函数
unsigned char KeyboardScan(void);
//显示函数
void Display(unsigned char control_byte);
//值修正函数,用于修正非法输入
void ReviseValue(unsigned char control_byte);
//进位函数
void CarryValue(void);
//整点报时函数
void ClockBeep(void);
//简易延时函数
void DelayXms(unsigned int x);
void main(void)
{
unsigned char temp_key = 0;//定义临时变量
while(1)
{
Init(); //初始化
SetNewValue(); //设定一个新时间
while(reset == 0) //检查复位标志
{
temp_key = KeyboardScan(); //接收按键
if(temp_key == RESET_KEY) //如果按键是复位
{
reset = 1;
}
CarryValue(); //进位
Display(hour_switch | minute_switch | second_switch | ON_BAR);//显示时分秒和分隔符
ClockBeep(); //整点报时
}
}
}
void Init(void)
{
int i = 0;
TMOD = 0x01; //设定定时器模式为定时器0模式1
TH0 = (65536 - INTERRUPT_TIME * 1000) / 256; //计10k个数,为10ms
TL0 = (65536 - INTERRUPT_TIME * 1000) % 256;
ET0 = 1; //允许定时器0开启
EA = 1; //全局中断开启
P0 = segment_select[CLEAR]; //清空数码管段选
TR0 = 0; //停止定时器0
reset = 0; //初始化各个变量
start = 0;
time_count = 0;
beep = 1; //beep置1为蜂鸣器不响
beep_flag = 0;
hour_switch = minute_switch = second_switch = OFF_DIS;//清空数码管位选
for(i = 0; i < 2; ++i) //初始化时间数组
{
hour[i] = BAR;
minute[i] = BAR;
second[i] = BAR;
}
}
void SetNewValue(void)
{
unsigned char temp, i;
temp = i = 0;
hour_switch = HOUR_DIS;//开启小时显示
minute_switch = MINU_DIS;//开启分钟显示
second_switch = SECO_DIS;//开启秒显示
for(i = 0; (i < 2) && (start == 0);)
{
temp = KeyboardScan();
if(temp == START_KEY)//按下的是开始键
{
start = 1;
}
else if(temp == RESET_KEY)//按下的是复位键
{
reset = 1;
}
else if(temp != NULL_KEY)//按下的是数字键
{
hour[i] = temp;
++i;
}
Display(hour_switch | minute_switch | second_switch);//显示时分秒
}
ReviseValue(REVISE_HOUR); //修正小时
for(i = 0; (i < 2) && (start == 0);) //如果有按开始直接退出按键扫描
{
temp = KeyboardScan();
if(temp == START_KEY)
{
start = 1;
}
else if(temp == RESET_KEY)
{
reset = 1;
}
else if(temp != NULL_KEY)
{
minute[i] = temp;
++i;
}
Display(hour_switch | minute_switch | second_switch);
}
ReviseValue(REVISE_HOUR | REVISE_MINU); //修正小时和分钟
for(i = 0; (i < 2) && (start == 0);)
{
temp = KeyboardScan();
if(temp == START_KEY)
{
start = 1;
}
else if(temp == RESET_KEY)
{
reset = 1;
}
else if(temp != NULL_KEY)
{
second[i] = temp;
++i;
}
Display(hour_switch | minute_switch | second_switch);
}
ReviseValue(REVISE_HOUR | REVISE_MINU | REVISE_SECO); //修正时分秒
start = 1;
TR0 = 1; //开定时中断
}
//扫描矩阵键盘
unsigned char KeyboardScan(void)
{
unsigned char return_val = NULL_KEY;
unsigned char temp;
//如果蜂鸣器响则不接收按键,原因是扫描键盘和蜂鸣器有冲突
//如若蜂鸣器与键盘不是同一IO口,可忽略此句
if(!beep && start)
return return_val;
//
//要检测某排按键,就把该口对应的引脚置0
//检测1~4
P1 = 0xfd;
DelayXms(5); //等待按键
temp = P1; //读回来
if((temp & 0xf0)!=0xf0) //说明第一排有键按下
{
DelayXms(5); //延时去抖
temp = P1;
if((temp & 0xf0)!=0xf0) //再次检验
{
switch(temp) //根据此时temp的值,也就是P1口的值
{ //来判断具体哪一个键被按下
case 0xed:return_val = 1;break;
case 0xdd:return_val = 2;break;
case 0xbd:return_val = 3;break;
case 0x7d:return_val = 4;break;
}
while((temp & 0xf0)!=0xf0) //如果按键没放手,就一直停在这检测
{
temp = P1;
Display(hour_switch | minute_switch | second_switch);
}
DelayXms(5);
return return_val;
}
}
P1 = 0xfb;
DelayXms(5);
temp = P1; //检测5~8
if((temp & 0xf0)!=0xf0)
{
DelayXms(5);
temp = P1;
if((temp & 0xf0)!=0xf0)
{
switch(temp)
{
case 0xeb:return_val = 5;break;
case 0xdb:return_val = 6;break;
case 0xbb:return_val = 7;break;
case 0x7b:return_val = 8;break;
}
while((temp & 0xf0)!=0xf0)
{
temp = P1;
Display(hour_switch | minute_switch | second_switch);
}
DelayXms(5);
return return_val;
}
}
P1 = 0xf7;
DelayXms(5);
temp = P1; //检测9~12
if((temp & 0xf0)!=0xf0)
{
DelayXms(5);
temp = P1;
if((temp & 0xf0)!=0xf0)
{
switch(temp)
{
case 0xe7:return_val = 9;break;
case 0xd7:return_val = 0;break;
case 0xb7:return_val = START_KEY;break;
case 0x77:return_val = RESET_KEY;break;
}
while((temp & 0xf0)!=0xf0)
{
temp = P1;
Display(hour_switch | minute_switch | second_switch);
}
DelayXms(5);
return return_val;
}
}
return return_val;
}
void Display(unsigned char control_byte)
{
unsigned char i = 0;
unsigned char led_out[8];
for(i = 0; i < 2; ++i)
{
led_out[i] = hour[i];
led_out[i+3] = minute[i];
led_out[i+6] = second[i];
}
led_out[2] = led_out[5] = BAR;
P0 = segment_select[CLEAR]; //关闭所有段选
for(i = 0; i < 8; ++i)
{
control_byte = control_byte << 1;
if(CY) //利用控制字的溢出标志来开启或关闭数码管
{
P0 = segment_select[led_out[i]];//送段选
P2 = bit_select[i]; //送位选
}
DelayXms(1);
P0 = segment_select[CLEAR]; //清空段选,防止对下一位的干扰
}
}
//自调整函数,用于设定时间的调整
void ReviseValue(unsigned char control_byte)
{
//如果小时超过23且有调整标志
if(hour[0] * 10 + hour[1] > 23 && (control_byte & REVISE_HOUR) )
{
hour[0] = 2;
hour[1] = 3;
}
//如果分钟超过59且有调整标志
if(minute[0] * 10 + minute[1] > 59 && (control_byte & REVISE_MINU))
{
minute[0] = 5;
minute[1] = 9;
}
//如果秒超过59且有调整标志
if(second[0] * 10 + second[1] > 59 && (control_byte & REVISE_SECO))
{
second[0] = 5;
second[1] = 9;
}
}
//进位函数
void CarryValue(void)
{
if(second[1] == 10)
{
second[1] = 0;
++second[0];
}
if(second[0] == 6)
{
second[0] = 0;
++minute[1];
}
if(minute[1] == 10)
{
minute[1] = 0;
++minute[0];
}
if(minute[0] == 6)
{
minute[0] = 0;
++hour[1];
beep_flag = 1; //蜂鸣器响标志
}
if(!hour[0] || hour[0] == 1)//当小时十位为0或1时
{
if(hour[1] == 10) //如果个位为10
{
hour[1] = 0;
++hour[0];
}
}
if(hour[0] == 2)
{
if(hour[1] > 3) //如果超过23,全部归0
{
hour[1] = 0;
hour[0] = 0;
}
}
}
void ClockBeep(void)
{
if(beep_flag) //如果有蜂鸣器标志且时间为0
{
beep_flag = 0; //蜂鸣器标志归0
beep = 0; //蜂鸣器响
}
if(beep_time * INTERRUPT_TIME > BEEP_DURA)//如果当前时间-蜂鸣器开始响的时间大于设定的时长
{
beep_time = 0; //蜂鸣器时间归0
beep = 1; //蜂鸣器停止响
}
}
void DelayXms(unsigned int x)
{
unsigned int i, j;
for(i = 0; i < x; ++i)
for(j = 0; j < 110; ++j);
}
void Time1Interrupt(
) interrupt 1
{
TH0 = (65536 - INTERRUPT_TIME * 1000) / 256;//重载定时器高八位
TL0 = (65536 - INTERRUPT_TIME * 1000) % 256;//重载定时器低八位
if(!beep)
{
++beep_time;
}
++time_count; //时钟计数累加,+1就说明已过INTERRUPT_TIME毫秒
if(time_count * INTERRUPT_TIME >= 1000)//如果累加时间是超过1s
{
++second[1];
time_count = 0;
}
}