利用定时器对代码的优化
①对按键代码的优化
之前的按键代码
unsigned char Key()
{
unsigned char KeyNumber=0;
if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
return KeyNumber;
}
该代码中为了消抖加入了一个死循环,子函数中加入死循环对主函数main的运行有极大的影响,比如当我按下按键不松手的时候,main中的程序就无法执行。因此,我们可以利用定时器,定时扫描按键,是否按下。
优化后的代码
main.c中,利用定时器0,每隔20ms对Key_Loop函数进行一次调用,
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count1,T0Count2,T0Count3;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count1++;
if(T0Count1>=20)//每隔20ms对Key_Loop这个函数调用
{
T0Count1=0;
Key_Loop();
}
}
key.c中,定时器每隔20ms对Key_Loop函数进行一次调用,因为定时器是中断函数,因此每次结束时会销毁调用函数的所有变量,所以我们设置两个静态变量NowState,LastState来记录每次按键的状态,每到20ms时对Key_Loop函数调用,此时NowState已经不是该时刻的按键状态了,因此将它赋值给上一个按键状态LastState,然后NowState再通过Key_GetState函数重新获取当前的按键状态,然后在进行if判断当前按键状态和上一个按键状态,如果上一个时间点按下,这个时间点未按下,则是松手的瞬间,按键有效,然后将Key_KeyNumber置数判断是哪个按键按下,这里还需要调用一个函数用来获取当前按键键码并且对Key_KeyNumber清零,否则即使松手也会一直返回按键键码。而在main.c当中我们只需要调用Key函数就可以获取按键键码。并且无论我们长时间的按下按键也不会对主函数中的程序运行有任何影响。
#include <REGX52.H>
#include "Delay.h"
unsigned char Key_KeyNumber;
/**
* @brief 获取按键键码
* @param 无
* @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
*/
unsigned char Key(void)
{
unsigned char Temp=0;
Temp=Key_KeyNumber;
Key_KeyNumber=0;
return Temp;
}
/**
* @brief 获取当前按键的状态,无消抖及松手检测
* @param 无
* @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
*/
unsigned char Key_GetState()
{
unsigned char KeyNumber=0;
if(P3_1==0){KeyNumber=1;}
if(P3_0==0){KeyNumber=2;}
if(P3_2==0){KeyNumber=3;}
if(P3_3==0){KeyNumber=4;}
return KeyNumber;
}
/**
* @brief 按键驱动函数,在中断中调用
* @param 无
* @retval 无
*/
void Key_Loop(void)
{
static unsigned char NowState,LastState;
LastState=NowState; //按键状态更新
NowState=Key_GetState();//获取当前按键状态
//如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
if(LastState==1 && NowState==0)
{
Key_KeyNumber=1;
}
if(LastState==2 && NowState==0)
{
Key_KeyNumber=2;
}
if(LastState==3 && NowState==0)
{
Key_KeyNumber=3;
}
if(LastState==4 && NowState==0)
{
Key_KeyNumber=4;
}
}
②对数码管扫描的代码进行优化
之前的数码管代码,通过在主函数中调用,依靠主函数中while的运行来实现动态的扫描
void Shuma(unsigned char Location,Number)
{
switch(Location)
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=shumatable[Number];
Delay(1);
P0=0x00;//消影
}
优化后的代码
main.c中,利用定时器0,每隔2ms对Shuma_Loop函数进行一次调用,显示一位数码管,每2ms显示一位,以此来实现动态数码管的显示
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count1,T0Count2,T0Count3;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count2++;
if(T0Count2>=2)
{
T0Count2=0;
Shuma_Loop();
}
}
shuma.c中,因为我们定义数码管的Location是1-8位,因此我们的Shuma_Loop函数每2ms被调用一次,显示一位数码管总共显示8位数码管实现一个动态的循环,因为该函数时写在定时器中断里边的,又有数码管扫描显示函数,因此该函数不需要在主函数中调用,数码管就可以显示;至于Shuma_Scan函数中第几位要显示什么数字,需要我们自行设定,因此引入一个数码管显示缓存区,通过设置缓存区所要显示的数字,因为数码管是2ms显示一位,等待下一次循环显示该位时,直接从缓存区里拿到需要显示的数字。因此在主函数中只需要调用Shuma_SetBuf函数就可以。
#include <REGX52.H>
#include "Delay.h"
//数码管显示缓存区
unsigned char Shuma_Buf[9]={0,10,10,10,10,10,10,10,10};
//数码管段码表
unsigned char shumatable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
//0,1,2,3,4,5,6,7,8,9,空,横杠
/**
* @brief 设置显示缓存区
* @param Location 要设置的位置,范围:1~8
* @param Number 要设置的数字,范围:段码表索引范围
* @retval 无
*/
void Shuma_SetBuf(unsigned char Location,Number)
{
Shuma_Buf[Location]=Number;
}
/**
* @brief 数码管扫描显示
* @param Location 要显示的位置,范围:1~8
* @param Number 要显示的数字,范围:段码表索引范围
* @retval 无
*/
void Shuma_Scan(unsigned char Location,Number)
{
P0=0x00; //段码清0,消影
switch(Location) //位码输出
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
}
P0=shumatable[Number];//段码输出
}
/**
* @brief 数码管驱动函数,在中断中调用
* @param 无
* @retval 无
*/
void Shuma_Loop(void)
{
static unsigned char i=1;
Shuma_Scan(i,Shuma_Buf[i]);
i++;
if(i>=9){i=1;}
}
③完整代码
#include <REGX52.H>
#include "Delay.h"
#include "key.h"
#include "Shuma.h"
#include "Timer0.h"
#include "AT24C02.h"
unsigned char KeyNum;
unsigned char Min,Sec,MiniSec;
unsigned char RunFlag;
void main()
{
Timer0_Init();
while(1)
{
KeyNum=Key();
if(KeyNum==1)
{
RunFlag=!RunFlag;
}
if(KeyNum==2)
{
Min=0;
Sec=0;
MiniSec=0;
}
if(KeyNum==3)
{
AT24C02_WriteByte(0,Min);
Delay(5);
AT24C02_WriteByte(1,Sec);
Delay(5);
AT24C02_WriteByte(2,MiniSec);
Delay(5);
}
if(KeyNum==4)
{
Min=AT24C02_ReadByte(0);
Sec=AT24C02_ReadByte(1);
MiniSec=AT24C02_ReadByte(2);
}
Shuma_SetBuf(1,Min/10);
Shuma_SetBuf(2,Min%10);
Shuma_SetBuf(3,11);
Shuma_SetBuf(4,Sec/10);
Shuma_SetBuf(5,Sec%10);
Shuma_SetBuf(6,11);
Shuma_SetBuf(7,MiniSec/10);
Shuma_SetBuf(8,MiniSec%10);
}
}
void Sec_Loop(void)
{
if(RunFlag)
{
MiniSec++;
if(MiniSec>=100)
{
MiniSec=0;
Sec++;
if(Sec>=60)
{
Sec=0;
Min++;
if(Min>=60)
{
Min=0;
}
}
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count1,T0Count2,T0Count3;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count1++;
if(T0Count1>=20)//每隔20ms对Key_Loop这个函数调用
{
T0Count1=0;
Key_Loop();
}
T0Count2++;
if(T0Count2>=2)
{
T0Count2=0;
Shuma_Loop();
}
T0Count3++;
if(T0Count3>=10)
{
T0Count3=0;
Sec_Loop();
}
}