使用两个按键,一个复位一个停止,代码将每个部分都拆解开,很详细,而且对定时器进行了补偿,进一步减小了误差,而且数值是在小数点后两位,具
#include <reg52.h>
#include<intrins.h>
sbit DU = P2^6;
sbit WE = P2^7;
sbit keys1=P3^4;//声明矩阵按键最后一行的列
sbit keys2=P3^5;
sbit keys3=P3^6;
sbit keys4=P3^7;
unsigned char code LedChar[]= {
//0 1 2 3 4 5 6 7 8 9
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
//a b c d e f
0x77,0x7c,0x39,0x5e,0x79,0x71}; //共阴极数码管0~9
unsigned char LedBuff[6] =
{0x00,0x00,0x00,0x00,0x00,0x00//数码管显示缓冲区
};
unsigned char KeySta[4] = {1,1,1,1};//按键当前状态
bit StopwatchRunning = 0;//秒表运行标志
bit StopwatchRefresh = 1;//秒表刷新标志
unsigned char DecimalPart = 0;//小数部分
unsigned int IntegerPart = 0;//整数部分
unsigned char T0RH = 0;//T0重载值的高字节
unsigned char T0RL = 0;//T0重载值的低字节
void ConfigTimer0(unsigned int ms);//配置定时器0
void StopwatchDisplay();
void KeyDriver();
void main()
{
EA = 1;//总使能
P3 = 0xfe;//打开矩阵第四行
ConfigTimer0(2);//配置T0定时2ms
while(1)
{
if(StopwatchRefresh)
{
StopwatchRefresh = 0;
StopwatchDisplay();//秒表显示
}
KeyDriver();
}
}
void ConfigTimer0(unsigned int ms)//配置定时器0
{
unsigned long tmp;//临时变量
tmp = 11059200/12;//定时器计数频率
tmp = (tmp * ms)/1000;//计算所需的计数值
tmp = 65536 - tmp;//计算所需的重载值
tmp = tmp + 18;// 补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8);//把高八位存起来
T0RL = (unsigned char)tmp;//把低八位存起来
TMOD &= 0xf0;//高4位保持不变
TMOD |= 0x01;//低4位最低位变1
TH0 = T0RH;//加载T0的重载值
TL0 = T0RL;
ET0 = 1;//定时器0的中断使能
TR0 = 1;//启动T0
}
void StopwatchDisplay()//存储数值到缓存区的函数,并点亮小数点,虽然叫秒表显示函数,但实际在这个函数里并没有显示到数码管的指令
{
signed char i;
unsigned char buf[4];//数据转换缓存区
LedBuff[0] = LedChar[DecimalPart%10];//小数部分存到数码管缓存区
LedBuff[1] = LedChar[DecimalPart/10];
buf[0] = IntegerPart%10;//将整数部分转化成4位十进制值
buf[1] = (IntegerPart/10)%10;
buf[2] = (IntegerPart/100)%10;
buf[3] = (IntegerPart/1000)%10;
for (i=3;i>=1;i--)//从最高位起,为0不显示,直到非零
{
if(buf[i]==0)
LedBuff[i+2]=0x00;
else
break;
}
for(;i>=0;i--) //剩余的转化成数码管显示的值
{
LedBuff[i+2]=LedChar[buf[i]];
}
LedBuff[2]|=0x80;//点亮小数点
}
void StopwatchAction()//秒表器启动停止函数
{
if(StopwatchRunning)
StopwatchRunning = 0;//已启动则停止
else
StopwatchRunning = 1;//已停止则启动
}
void StopwatchReset()//秒表复位函数
{
StopwatchRunning = 0;//停止秒表
DecimalPart = 0;//清0计数值
IntegerPart = 0;
StopwatchRefresh = 1;//重置刷新标志
}
void KeyDriver()//检测按键状态并执行操作,主函数调用
{
unsigned char i;
static unsigned char backup[4] = {1,1,1,1};//按键值备份
for (i=0;i<4;i++)//循环检测4*4矩阵按键
{
if(backup[i]!=KeySta[i])//判断按键是否按下
{
if(backup[i]!=0)//按键按下就执行
{
if(i == 1)
StopwatchReset();
else if(i == 2)
StopwatchAction();
}
backup[i] = KeySta[i];//刷新前一次的备份值
}
}
}
void KeyScan() //按键扫描函数,在中断中调用
{
unsigned char i;
static unsigned char keybuf[4]={0xff,0xff,0xff,0xff};//按键扫描缓冲区
keybuf[0] = (keybuf[0]<<1) | keys1; //将按键状态移入缓冲区
keybuf[1] = (keybuf[1]<<1) | keys2;
keybuf[2] = (keybuf[2]<<1) | keys3;
keybuf[3] = (keybuf[3]<<1) | keys4;
for(i=0;i<4;i++)//消抖更新按键状态
{
if(keybuf[i]==0x00) //连续扫描4次值为0,则认为按键已经按下
KeySta[i]=0;
else if(keybuf[i]==0xff)
KeySta[i]=1;
}
}
void LedScan()//数码管动态刷新
{
static unsigned char i = 0;
DU=1;
P0 = 0x00;
DU=0;
switch(i)
{
case 0:WE= 1;P0=0x7f;i++;WE=0; DU=1;P0=LedBuff[0]; DU=0;break;
case 1:WE= 1;P0=0xbf;i++;WE=0; DU=1;P0=LedBuff[1]; DU=0;break;
case 2:WE= 1;P0=0xdf;i++;WE=0; DU=1;P0=LedBuff[2]; DU=0;break;
case 3:WE= 1;P0=0xef;i++;WE=0; DU=1;P0=LedBuff[3]; DU=0;break;
case 4:WE= 1;P0=0xf7;i++;WE=0; DU=1;P0=LedBuff[4]; DU=0;break;
case 5:WE= 1;P0=0xfb;i=0;WE=0; DU=1;P0=LedBuff[5]; DU=0;break;
default: break;
}
}
void StopwatchCount()
{
if(StopwatchRunning)//处于运行状态
{
DecimalPart++; //小数部分加1
if(DecimalPart>=100)
{
DecimalPart = 0;
IntegerPart++;
if(IntegerPart>=10000)
IntegerPart = 0;
}
StopwatchRefresh = 1;
}
}
void time_0() interrupt 1
{
static unsigned char flag10ms = 0;
TH0 = T0RH;//加载T0的重载值
TL0 = T0RL;
LedScan();
KeyScan();
flag10ms++;
if(flag10ms>=5)
{
flag10ms = 0;
StopwatchCount();
}
}
体原理每个部分都有注释