前言
单片机实现时钟项目实战!
目录
一、矩阵键盘电路设计
1. 键盘接口电路设计
当无按键闭合时,P1.0 ~ P1.3 与 P1.4 ~ P3.7之间开路。当有键闭合时,与闭合键相连的两条 I0 口线之间短路。判断有无按键按下的方法是:第一步,置列线 P1.4 ~ P1.7 为输入状态,从行线 P1.0 ~ P1.3 输出低电平,读入列线数据,若某一列线为低电平,则该列线上有键闭合。
第二步,行线轮流输出低电平,从列线 P1.4 ~ P1.7 读入数据,若有某一列为低电平,则对应行线上有键按下,综合一二两步的结果,可确定按键编号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。

图1 矩阵键盘设计局部电路图

图2 矩阵键盘对应的功能
2. 定时器中断过程的实现原理
定时器工作前先装入初值,利用送数指令将初值装入 TH0 和 TL0 或 TH1 和 TL1,高位数装入 TH0和 TH1,低位数装入 TL0 和 TL1。当发出启动命令后,
装初值寄存器开始计数,连续加 1,每一个机器周期加 1 一次,加到满值(各位全 1)。若再加 1,则溢出,同时将初值寄存器清零。如果继续计数定时,则需要重新赋初值,如下图3所示。

图3 矩阵键盘设计局部电路图
① 定时器初始化的主要步骤
第一步 选择工作方式,即对TMOD 赋初值。第二步 给定时器赋初值,即把初始常数装入THO TLO 或TH1 TL1。第三步根据需要设置中断控制字直接对中断允许寄存器E和优先级寄存器IP设置。第四步启动定时计数数器,若已规定用软件启动(即GATE=0),则可把TRO或TR1置1。若已规定出外中断端子电平启动(即GATE=1),则需给外端子加启动电平。
② 定时器初值设定方法
根据定时长短,选择工作方式,设用M表示最大计数值,则各种方式计数最大值如下。方式0:M = 2^13 = 8192,方式1 :M = 2^16 = 65536,方式2:M = 2^8 = 256,方式3:M = 2^8 = 256
③ 定时器中断服务函数的实现原理
中断服务程序:就是当计满 THO 、TLO 时溢出申请中断,然后单片机允许中断时,所要发生的事情.允许后就自动跳转到中断服务程序,并执行在服务程序中,如果不装入初值,那定时器中断服务完成后,就会从 0 开始重新计时,所以要在中断程序中重新计算并装入初值.然后给一个变量(变量的意义为中断次数),变量 +1 ,当中断次数达到 20 次的时候(50ms*20次 = 1000ms = 1s),次数清零,并且让产生指令,实现时钟定时功能。
二、整体电路原理图

图4 整体电路设计
三、程序设计
通过 STC89C52 单片机,控制矩阵键盘能够实现开始、暂停、归零、计数、定时的功能,并在四个数码管上显示出来。
具体功能为:对前 14 个数码管进行定义,能够实现按键 4 为计时暂停,按键 8 为计时开始,按键 12 为暂停时钟,按键 16 为数值加 1,按键 13 为移动修改数值键,按键 15 为定时功能,其余按键为 0-9 数值键。

图5 代码实现流程图
四、程序实现
#include<reg52.h>
//--定义使用的IO口--//
#define GPIO_KEY P1
#define GPIO_DIG P0 //段位选
sbit D1=P3^6;//段选
sbit D2=P3^7;//位选
unsigned char code DIG_PLACE[8] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char codeseg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x40};
unsigned char xianshi[8];//保存时钟的数据
char second,minit,hours,s,m;
char Time,state,set,T;
//state=0显示时间,state=1校对时间,state=2定时
//set校对或者定时时,设置哪一位
unsigned char KeyValue,KeyValue2;//keyValue2(0-15),keyValue(0-9加功能)
unsigned char KeyState; //记录按键的状态,0没有,1有
void Delay10ms(unsigned int c) //延时函数
{
unsigned char a,b;
for(; c>0; c--)
for(b=38; b>0; b--)
for(a=130; a>0; a--);
}
void KeyDown(void)
{
GPIO_KEY=0x0f; //测试列
if(GPIO_KEY!=0x0f)
{
Delay10ms(1);//延时消抖
if(GPIO_KEY!=0x0f)
{
KeyState=1;//有按键按下
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07):
KeyValue2=0;
break;
case(0X0b):
KeyValue2=1;
break;
case(0X0d):
KeyValue2=2;
break;
case(0X0e):
KeyValue2=3;
break;
}
//测试行
GPIO_KEY=0XF0;
Delay10ms(1);
switch(GPIO_KEY)
{
case(0X70):
KeyValue2=KeyValue2;
break;
case(0Xb0):
KeyValue2=KeyValue2+4;
break;
case(0Xd0):
KeyValue2=KeyValue2+8;
break;
case(0Xe0):
KeyValue2=KeyValue2+12;
break;
}
while(GPIO_KEY!=0xf0); //等待按键松开
if(KeyValue2==0)KeyValue=1;
if(KeyValue2==1)KeyValue=2;
if(KeyValue2==2)KeyValue=3;
if(KeyValue2==4)KeyValue=4;
if(KeyValue2==5)KeyValue=5;
if(KeyValue2==6)KeyValue=6;
if(KeyValue2==8)KeyValue=7;
if(KeyValue2==9)KeyValue=8;
if(KeyValue2==10)KeyValue=9;
if(KeyValue2==13)KeyValue=0;
if(KeyValue2==3)KeyValue=10; //暂停
if(KeyValue2==7)KeyValue=11; //开始
if(KeyValue2==11)KeyValue=12; //归零
if(KeyValue2==12)KeyValue=13; //左移
if(KeyValue2==14)KeyValue=14; //右移
if(KeyValue2==15)KeyValue=15; //小时+1
}
}
}
void DigDisplay()
{
unsigned char i;
unsigned int j;
for(i=0; i<4; i++)
{
GPIO_DIG = DIG_PLACE[i]; //发送位选
D2=1;//573输出位选
D2=0;//锁存
GPIO_DIG = xianshi[i]; //发送段码
D1=1;//输出段选
D1=0;//锁存
j = 100; //扫描间隔时间设定
while(j--);
GPIO_DIG = 0x00;//消隐
D1=1;
D1=0;
}
}
void Smgshow()
{
if(hours/8%2==1)
xianshi[0]=seg[minit/10]|0x80;
else
xianshi[0]=seg[minit/10];
if(hours/4%2==1)
xianshi[1]=seg[minit%10]|0x80;
else
xianshi[1]=seg[minit%10];
if(hours/2%2==1)
xianshi[2]=seg[second/10]|0x80;
else
xianshi[2]=seg[second/10];
if(hours%2==1)
xianshi[3]=seg[second%10]|0x80;
else
xianshi[3]=seg[second%10];
DigDisplay();
}
void Flashshow()//设置时闪烁显示
{
if(Time<50)
{
if(hours/8%2==1)
xianshi[0]=seg[minit/10]|0x80;
else
xianshi[0]=seg[minit/10];
if(hours/4%2==1)
xianshi[1]=seg[minit%10]|0x80;
else
xianshi[1]=seg[minit%10];
if(hours/2%2==1)
xianshi[2]=seg[second/10]|0x80;
else
xianshi[2]=seg[second/10];
if(hours%2==1)
xianshi[3]=seg[second%10]|0x80;
else
xianshi[3]=seg[second%10];
}
else
{
if(set==3)
{
xianshi[0]=0x00;
}
if(set==2)
{
xianshi[1]=0x00;
}
if(set==1)
{
xianshi[2]=0x00;
}
if(set==0)
{
xianshi[3]=0x00;
}
}
DigDisplay();
}
void Flashshow2()//定时结束时闪烁显示
{
int i=0;
while(i<1000) {
T++;
if(T>100)
T=0;
if(T<50)
{
if(hours/8%2==1)
xianshi[0]=seg[minit/10]|0x80;
else
xianshi[0]=seg[minit/10];
if(hours/4%2==1)
xianshi[1]=seg[minit%10]|0x80;
else
xianshi[1]=seg[minit%10];
if(hours/2%2==1)
xianshi[2]=seg[second/10]|0x80;
else
xianshi[2]=seg[second/10];
if(hours%2==1)
xianshi[3]=seg[second%10]|0x80;
else
xianshi[3]=seg[second%10];
}
else
{
xianshi[0]=0x00;
xianshi[1]=0x00;
xianshi[2]=0x00;
xianshi[3]=0x00;
}
DigDisplay();
}
}
void main()
{
second=55;
minit=59;
hours=0;
TMOD=0X01;
TH0 = 0xDC;
TL0 = 0x00; //10ms
TR0=1;//启动定时功能
ET0=1;//开定时器允许中断
EA=1;//打开总中断
state=0;
m=59;
s=59;
while(1)
{
while(state==0)
{
KeyDown(); //扫描键盘
if((KeyState==1)) //如果有按键按下
{
if(KeyValue==10) //如果是第4个按键按下
{
TR0=0;
ET0=0; //暂停,关闭定时功能,关闭定时器允许中断
}
if(KeyValue==11) //如果是第8个按键按下
{
TR0=1;
ET0=1; //开始
}
if(KeyValue==12) //如果是第12个按键按下
{
second=0;
minit=0;
hours=0;
TR0=1;
ET0=1; //归零
}
if(KeyValue==15) //如果是第16个按键按下
{
hours++; //加1小时
if(hours>23)
hours=0;
}
if(KeyValue==13) //如果是第13个按键按下
{
state=1; //设置
set=0;
}
if(KeyValue==14) //如果是第15个按键按下
{
state=2; //定时
set=0;
}
KeyState=0;
}
Smgshow();
}
while(state==1)
{
KeyDown(); //扫描键盘
if((KeyState==1)) //如果有按键按下
{
if(KeyValue==15) //如果是第16个按键按下
{
hours++; //加1小时
if(hours>23)
hours=0;
}
if(KeyValue==13) //如果是第13个按键按下
{
set++;//左移
if(set>=4)
{
set=0;
state=0;
}
}
if(KeyValue==14) //如果是第15个按键按下
{
set--;//右移
if(set<0)
{
set=0;
state=0;
}
}
if(KeyValue==11) //如果是第8个按键按下
{
TR0=1;
ET0=1; //开始
}
if(KeyValue<10)
{
if(set==0)
{
second=second/10%10*10+KeyValue;
}
if(set==1)
{
if(KeyValue<6)
second=second%10+KeyValue*10;
}
if(set==2)
{
minit=minit/10%10*10+KeyValue;
}
if(set==3)
{
if(KeyValue<6)
minit=minit%10+KeyValue*10;
}
}
KeyState=0;
}
Flashshow();
}
while(state==2)
{
KeyDown(); //扫描键盘
if((KeyState==1)) //如果有按键按下
{
if(KeyValue==15) //如果是第16个按键按下
{
hours++; //加1小时
if(hours>23)
hours=0;
}
if(KeyValue==13) //如果是第13个按键按下
{
set++;//左移
}
if(KeyValue==10) //如果是第4个按键按下
{
TR0=0;
ET0=0; //暂停,关闭定时功能,关闭定时器允许中断
}
if(KeyValue==11) //如果是第8个按键按下
{
TR0=1;
ET0=1; //开始
}
if(KeyValue<10)
{
if(set==0)
{
second=second/10%10*10+KeyValue;
s=second;
}
if(set==1)
{
if(KeyValue<6)
second=second%10+KeyValue*10;
s=second;
}
if(set==2)
{
minit=minit/10%10*10+KeyValue;
m=minit;
}
if(set==3)
{
if(KeyValue<6)
minit=minit%10+KeyValue*10;
m=minit;
second=0;
minit=0;
}
}
KeyState=0;
}
Flashshow();
}
}
}
void time0() interrupt 1
{
static unsigned int j;
TH0 = 0xDC;
TL0 = 0x00; //重新装载10ms
if(state==1)
{
Time++;
if(Time>100)
Time=0;
}
if(state==0)
{
j++;
if(j==100) //1s
{
j=0;
second++;
if(second==60) //秒满60清零
{
second=0;
minit++;
if(minit==60) //分满60清零
{
minit=0;
hours++;
if(hours>15)hours=0; //时满16清零
}
}
}
}
if(state==2)
{
Time++;
if(Time>100)
Time=0;
if((minit>=m)&(second>=s)) //如果到时间
{
TR0=0;
ET0=0; //暂停,关闭定时功能,关闭定时器允许中断
Flashshow2();
}
else {
j++;
if(j==100) //1s
{
j=0;
second++;
if(second==60) //秒满60清零
{
second=0;
minit++;
if(minit==60) //分满60清零
{
minit=0;
hours++;
if(hours>15)hours=0; //时满16清零
}
}
}
}
}
}
本文详细介绍了使用51单片机通过矩阵键盘实现时钟的完整流程,包括矩阵键盘的接口电路设计、定时器中断的原理及实现、整体电路设计和程序设计。在矩阵键盘中断服务函数中,计数达到特定次数后,实现时钟功能。
10万+

被折叠的 条评论
为什么被折叠?



