从单片机基础到程序框架(全集 2019pdf版).pdf - 第451页
sbit P3_4= P3^4; //蜂鸣 器 sbit KEY_I NPUT1=P2^ 2; //K1 按键识别的输入口 。 sbit KEY_I NPUT2=P2^ 1; //K2 按键识别的输入口 。 volatile u nsigned c har vGu8Beep TimerFlag =0; volatile u nsigned i nt vGu16BeepTi merCnt=0; unsigned i nt Gu16Se t…

就“不再加速”,而是以该“恒定高速”进行“连续匀速”触发。这种触发方式,“加速”和“匀速”是相辅
相成缺一不可的,为什么?假如没有“加速”只有“匀速”,那么刚按下按键就直接以最高速的“匀速”进
行,就会跑过头,缺乏微调功能;而假如没有“匀速”只有“加速”,那么按下按键不松手后,速度就会一
直不断飙升,最后失控过冲。
本节例程实现的功能如下:
(1)要更改一个“设置参数”(一个全局变量),参数的范围是 0 到 800。
(2)8 个受“设置参数”控制的跑马灯在某一时刻只有 1 个 LED 亮,每触发一次 K1 按键,该“设置参
数”就自减 1,最小值为 0;相反,每触发一次 K2 按键,该“设置参数”就自加 1,最大值为 800。
(3)LED 灯实时显示“设置参数”的范围状态:
只有第 0 个 LED 灯亮:0<=“设置参数”<100。
只有第 1 个 LED 灯亮:100<=“设置参数”<200。
只有第 2 个 LED 灯亮:200<=“设置参数”<300。
只有第 3 个 LED 灯亮:300<=“设置参数”<400。
只有第 4 个 LED 灯亮:400<=“设置参数”<500。
只有第 5 个 LED 灯亮:500<=“设置参数”<600。
只有第 6 个 LED 灯亮:600<=“设置参数”<700。
只有第 7 个 LED 灯亮:700<=“设置参数”<=800。
(4)按键每“单击”一次蜂鸣器就鸣叫一次,但是,当按键“从单击进入连击”后,蜂鸣器就不鸣叫。
#include "REG52.H"
#define KEY_VOICE_TIME 50
#define KEY_SHORT_TIME 25 //按键单击的“滤波”时间
#define KEY_ENTER_CONTINUITY_TIME 300 //按键“从单击进入连击”的间隔时间
#define KEY_CONTINUITY_INITIAL_TIME 80 //按键“连击”起始的预设间隔时间
#define KEY_SUB_DT_TIME 8 //按键在“加速”时每次减小的时间。
#define KEY_CONTINUITY_MIN_TIME 10 //按键时间减小到最后的“匀速”间隔时间。
#define BUS_P0 P0 //8 个 LED 灯一一对应单片机的 P0 口总线
void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void BeepOpen(void);
void BeepClose(void);
void VoiceScan(void);
void KeyScan(void);
void KeyTask(void);
void DisplayTask(void); //显示的任务函数(LED 显示状态)

sbit P3_4=P3^4; //蜂鸣器
sbit KEY_INPUT1=P2^2; //K1 按键识别的输入口。
sbit KEY_INPUT2=P2^1; //K2 按键识别的输入口。
volatile unsigned char vGu8BeepTimerFlag=0;
volatile unsigned int vGu16BeepTimerCnt=0;
unsigned int Gu16SetData=0; //“设置参数”。范围从 0 到 800。LED 灯反映该当前值的范围状态
unsigned char Gu8DisplayUpdate=1; //显示的刷新标志
volatile unsigned char vGu8KeySec=0; //按键的触发序号
volatile unsigned char vGu8ShieldVoiceFlag=0; //屏蔽声音的标志
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
KeyTask(); //按键的任务函数
DisplayTask(); //显示的任务函数(LED 显示状态)
}
}
/* 注释一:
* Gu8DisplayUpdate 这类“显示刷新变量”在“显示框架”里是很常见的,而且屡用屡爽。
* 目的是,既能及时刷新显示,又能避免主函数“不断去执行显示代码”而影响程序效率。
*/
void DisplayTask(void) //显示的任务函数(LED 显示状态)
{
if(1==Gu8DisplayUpdate) //需要刷新一次显示
{
Gu8DisplayUpdate=0; //及时清零,避免主函数“不断去执行显示代码”而影响程序效率
if(Gu16SetData<100)
{
BUS_P0=~(1<<0); //第 0 个灯亮
}
else if(Gu16SetData<200)
{

BUS_P0=~(1<<1); //第 1 个灯亮
}
else if(Gu16SetData<300)
{
BUS_P0=~(1<<2); //第 2 个灯亮
}
else if(Gu16SetData<400)
{
BUS_P0=~(1<<3); //第 3 个灯亮
}
else if(Gu16SetData<500)
{
BUS_P0=~(1<<4); //第 4 个灯亮
}
else if(Gu16SetData<600)
{
BUS_P0=~(1<<5); //第 5 个灯亮
}
else if(Gu16SetData<700)
{
BUS_P0=~(1<<6); //第 6 个灯亮
}
else
{
BUS_P0=~(1<<7); //第 7 个灯亮
}
}
}
/* 注释二:
* 按键“先加速后匀速”的识别过程:
* 第一步:每次按一次就触发一次“单击”,如果按下去到松手的时间不超过 1 秒,则不会进入
* “连击”模式。
* 第二步:如果按下去不松手的时间超过 1 秒,则进入“连击”模式。按键触发的节奏
* 不断加快,直至到达某个极限值,然后以此极限值间隔匀速触发。这就是“先加速后匀速”。
*/
void KeyScan(void) //此函数放在定时中断里每 1ms 扫描一次
{
static unsigned char Su8KeyLock1;
static unsigned int Su16KeyCnt1;
static unsigned int Su16KeyContinuityCnt1; //连击计数器
static unsigned int Su16KeyContinuityTime1=KEY_CONTINUITY_INITIAL_TIME; //动态时间阀值