从单片机基础到程序框架(全集 2019pdf版) - 第609页
{ SystemIn itial(); Delay(10 000); Peripher alInitial() ; while(1) { KeyTask( ); //按键的任务函数 DisplayT ask(); //数码管 显示的上层 任务函数 RunTask( ); //秒表的应用程序 } } void KeyTa sk(void) // 按键的任务 函数 { if(0==vG u8KeySec) { return; } switc…

0x06, //1 序号 1
0x5b, //2 序号 2
0x4f, //3 序号 3
0x66, //4 序号 4
0x6d, //5 序号 5
0x7d, //6 序号 6
0x07, //7 序号 7
0x7f, //8 序号 8
0x6f, //9 序号 9
0x00, //不显示 序号 10
};
//数码管底层驱动扫描的软件定时器
volatile unsigned char vGu8ScanTimerFlag=0;
volatile unsigned int vGu16ScanTimerCnt=0;
//秒表的软件定时器,注意,这里是 unsigned long 类型,范围是 0 到 4294967295 毫秒
volatile unsigned char vGu8StopWatchTimerFlag=0;
volatile unsigned long vGu32StopWatchTimerCnt=0;
//数码管上层每 10ms 就定时刷新一次显示的软件定时器。用于及时更新显示秒表当前的实时数值
volatile unsigned char vGu8UpdateTimerFlag=0;
volatile unsigned int vGu16UpdateTimerCnt=0;
unsigned char Gu8RunStart=0; //应用程序的总启动
unsigned char Gu8RunStep=0; //应用程序的总运行步骤。建议跟 vGu8RunStart 成双成对出现
unsigned char Gu8RunStatus=0; //当前秒表的状态。0 代表停止,1 代表正在工作中,2 代表暂停
unsigned char Gu8WdUpdate=1; //开机默认整屏更新一次显示。此变量在显示框架中是非常重要的变量
volatile unsigned char vGu8Display_Righ_4=10; //开机默认最高位数码管显示一个“不显示”数据
volatile unsigned char vGu8Display_Righ_3=0;
volatile unsigned char vGu8Display_Righ_2=0;
volatile unsigned char vGu8Display_Righ_1=0;
volatile unsigned char vGu8Display_Righ_Dot_4=0;
volatile unsigned char vGu8Display_Righ_Dot_3=1; //开机默认保留显示 2 个小数点
volatile unsigned char vGu8Display_Righ_Dot_2=0;
volatile unsigned char vGu8Display_Righ_Dot_1=0;
volatile unsigned char vGu8KeySec=0;
void main()

{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
KeyTask(); //按键的任务函数
DisplayTask(); //数码管显示的上层任务函数
RunTask(); //秒表的应用程序
}
}
void KeyTask(void) //按键的任务函数
{
if(0==vGu8KeySec)
{
return;
}
switch(vGu8KeySec)
{
case 1: //复位按键
Gu8RunStatus=0; //秒表返回停止的状态
Gu8RunStart=0; //秒表停止
Gu8RunStep=0; //总运行步骤归零。建议跟 vGu8RunStart 成双成对出现
vGu8StopWatchTimerFlag=0;
vGu32StopWatchTimerCnt=0; //秒表的软件定时器清零
Gu8WdUpdate=1; //整屏更新一次显示
vGu8KeySec=0;
break;
case 2: //启动与暂停的按键
if(0==Gu8RunStatus) //在停止状态下
{
Gu8RunStatus=1; //秒表处于工作状态
vGu8StopWatchTimerFlag=0;
vGu32StopWatchTimerCnt=0;
vGu8StopWatchTimerFlag=1; //启动秒表的软件定时器

Gu8RunStart=1; //秒表总开关启动
Gu8RunStep=0; //总运行步骤归零。建议跟 vGu8RunStart 成双成对出现
}
else if(1==Gu8RunStatus) //在工作状态下
{
Gu8RunStatus=2; //秒表处于暂停状态
}
else //在暂停状态下
{
Gu8RunStatus=1; //秒表处于工作状态
}
Gu8WdUpdate=1; //整屏更新一次显示,确保在暂停的时候能显示到最新的数据
vGu8KeySec=0;
break;
}
}
void DisplayTask(void) //数码管显示的上层任务函数
{
//需要借用的中间变量,用来拆分数据位
static unsigned char Su8Temp_4,Su8Temp_3,Su8Temp_2,Su8Temp_1; //需要借用的中间变量
/* 注释一:
* 此处为什么要多加 4 个中间过渡变量 Su8Temp_X?是因为 vGu32StopWatchTimerCnt 分解数据的时候
* 需要进行除法和求余数的运算,就会用到好多条指令,就会耗掉一点时间,类似延时了一会。我们
* 的定时器每隔一段时间都会产生中断,然后在中断里驱动数码管显示,当 vGu32StopWatchTimerCnt
* 还没完全分解出 4 位有效数据时,这个时候来的定时中断,就有可能导致显示的数据瞬间产生不完整,
* 影响显示效果。因此,为了把需要显示的数据过渡最快,所以采取了先分解,再过渡显示的方法。
*/
if(1==Gu8WdUpdate) //如果需要整屏更新
{
Gu8WdUpdate=0; //及时清零,只更新一次显示即可,避免一直进来更新显示
//先分解数据
//Su8Temp_4 提取“十秒”位。
Su8Temp_4=vGu32StopWatchTimerCnt/10000%10; //实际精度是 0.001 秒,但显示精度是 0.01 秒