从单片机基础到程序框架(全集 2019pdf版).pdf - 第452页
BUS_P0=~(1 <<1); //第 1 个灯亮 } else if(Gu 16SetData <300) { BUS_P0=~(1 <<2); //第 2 个灯亮 } else if(Gu 16SetData <400) { BUS_P0=~(1 <<3); //第 3 个灯亮 } else if(Gu 16SetData <500) { BUS_P0=~(1 <<…

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; //动态时间阀值

static unsigned char Su8KeyLock2;
static unsigned int Su16KeyCnt2;
static unsigned int Su16KeyContinuityCnt2; //连击计数器
static unsigned int Su16KeyContinuityTime2=KEY_CONTINUITY_INITIAL_TIME; //动态时间阀值
//K1 按键
if(0!=KEY_INPUT1)//单个 K1 按键没有按下,及时清零一些标志。
{
Su8KeyLock1=0; //按键解锁
Su16KeyCnt1=0; //去抖动延时计数器清零,此行非常巧妙,是全场的亮点。
Su16KeyContinuityCnt1=0; //连击计数器
Su16KeyContinuityTime1=KEY_CONTINUITY_INITIAL_TIME; //动态时间阀值。重装初始值。
}
else if(0==Su8KeyLock1)//单个按键 K1 被按下
{
Su16KeyCnt1++; //累加定时中断次数,每一次累加额度是 1ms
if(Su16KeyCnt1>=KEY_SHORT_TIME) //按键的“滤波”时间 25ms
{
Su8KeyLock1=1; //“自锁”
vGu8KeySec=1; //触发一次 K1 按键
Su16KeyCnt1=0; //清零,为了下一步用来累加“从单击进入连击的间隔时间 300ms”
}
}
else if(Su16KeyCnt1<=KEY_ENTER_CONTINUITY_TIME)//按住不松手累加到 300ms
{
Su16KeyCnt1++; //累加定时中断次数,每一次累加额度是 1ms
}
else //按住累加到 300ms 后仍然不放手,这个时候进入有节奏的连续触发
{
Su16KeyContinuityCnt1++; //连击计数器开始累加,每一次累加额度是 1ms
if(Su16KeyContinuityCnt1>=Su16KeyContinuityTime1) //按住没松手,每隔一会就触发一次
{
Su16KeyContinuityCnt1=0; //清零,为了继续连击。
vGu8KeySec=1; //触发一次 K1 按键
vGu8ShieldVoiceFlag=1; //把当前按键触发的声音屏蔽掉
if(Su16KeyContinuityTime1>=KEY_SUB_DT_TIME)
{
//此数值不断被减小,按键的触发速度就不断变快
Su16KeyContinuityTime1=Su16KeyContinuityTime1-KEY_SUB_DT_TIME;//变快节奏
}
if(Su16KeyContinuityTime1<KEY_CONTINUITY_MIN_TIME) //最小间隔时间就是“高速匀速”
{
Su16KeyContinuityTime1=KEY_CONTINUITY_MIN_TIME; //最后以此最高速进行“匀速”