从单片机基础到程序框架(全集 2019pdf版).pdf - 第357页

借用了函数 入口的局部变 量 u32DelayTim e 来充当 for 循环 的变量,而省 去了一个 i 变量 。因此, “累减型” 比“累加型” 强一点。 【82.4 Delay 函数让初学者容易犯的错误。 】 初学者刚接 触 Delay 函数, 常常容易 犯的错误就是 忽略了 f or 循环变量的 类型,fo r 循环变量的 类型决 定 了 你 能 输 入 的 数 值 范 围 , 比 如 上 面 例 子 中 用 到 的 是 uns…

100%1 / 836
void Delay(unsigned long u32DelayTime) //产生“阻塞延时”的延时函数
{
static unsigned long i; //函数在频繁调用时,加 static 可以省去一条额外的初始化语句的开销
for(i=0;i<u32DelayTime;i++);
}
void main()
{
while(1)
{
//第(1)步
P0_0=0; //LED 灯亮。
//第(2)步
Delay(5000); //这里就是阻塞延时,时间就越长,“亮”持续的时间就越长
//第(3)步
P0_0=1; //LED 灯灭。
//第(4)步
Delay(5000); //这里就是阻塞延时,时间就越长,“灭”持续的时间就越长
//第(5)步:这里已经触碰到主循环 while(1)的“底线”,所以接着跳转到第(1)步继续循环
}
}
【82.3 累加型和累减型的两种 Delay 函数,哪家强?】
上述 82.2 例子中,用到一个 Delay 函数,该函数内部的 for 循环用的是“累加型”的,比如:
void Delay(unsigned long u32DelayTime)
{
static unsigned long i; //“累加型”函数内部多开销了一个变量 i。
for(i=0;i<u32DelayTime;i++); //因为这里的“i++”是加法运算,所以称为“累加型”
}
现在在跟大家分享一种“累减型”的 Delay 函数,例子如下:
void Delay(unsigned long u32DelayTime)
{
//“累减型”函数内部节省了一个变量 i
for(;u32DelayTime>0;u32DelayTime--); //“u32DelayTime--”意味着“累减型”
}
仔细对比“累加型”和“累减型,会发现在实现同样“阻塞延时”的功能下,因为“累减型”巧妙
借用了函数入口的局部变 u32DelayTime 来充当 for 循环的变量,而省去了一个 i 变量。因此,“累减型”
比“累加型”强一点。
【82.4 Delay 函数让初学者容易犯的错误。
初学者刚接 Delay 函数,常常容易犯的错误就是忽略了 for 循环变量的类型,for 循环变量的类型决
unsigned long
Delay(4294967295)。如果是 unsigned int 变量,最大可以输入 Delay(65535)。如果是 unsigned char
量,最大可以输入 Delay(255)。
【82.5 Delay 内部的 for 循环嵌套可产生无穷长的时间。
刚才讲到,如果用最大的变量类型 unsigned long ,最大的输入是 Delay(4294967295),那么问题来,
难道 Delay 函数的阻塞延时的时间有最大极限?其实不存在最大极限理论上,你要多大的延时都可以
需要在 Delay 函数内部用上 for 循环的嵌套,就可以产生“乘法级”的无穷长的时间,例子如下:
void Delay(unsigned long u32DelayTime)
{
static unsigned long i;
static unsigned long k;
for(i=0;i<u32DelayTime;i++)
{
for(k=0;k<5000;k++); //内部嵌套的 for 循环,意味着乘法的关系 u32DelayTime 5000 倍!
}
}
【82.6 “阻塞延时”与“非阻塞延时”的各自应用范围。
“阻塞延时”一般应用在两个地方,一个是上电初始化进入主循环之前的延时,另一个是进入主循环之
后,跟外部驱动芯片通信时候产生的时钟节拍小延时,而这个类延时一般是低于 1ms 的小延时。
“非阻塞延时”在项目中是被大量应用的,进入主循环之后只要大于或等于 1ms 的延时,大多数都采
样“非阻塞延时”因为进“任务框架级”的层面,只有“非阻塞延”才能保证项目可以继续“多任
并行处理”非阻塞延时”的方式后续章节会讲到
综上所述,1ms 是“塞延时”与“非阻塞延时”的一个分界线,1ms 个时间不是绝对的,只是一
经验值。
第八十三节: 累计主循环的“非阻塞”延时控制 LED 闪烁。
【83.1 累计主循环的“非阻塞”
上一节提到 Delay “阻塞”时间超过 1ms 并且被频繁调用的时候由于 Delay “独占式无用功”
而消耗的延时太长,会影响其它任务的并行处理,整个系统给人的感觉非常卡顿不流畅。为了解决此问题,
本节引入累计主循环的“非阻塞同时,希望通过此例子,让大家第一次感受到 switch 语句在多任务并行
处理时候的优越性。switch 的精髓在于“根据某个判断条件实现步骤之间的灵活跳转”,这个思路是以后做
所有大项目的框架性思路
为什么“累计主循环”可以兼顾到其它任务的并行处理?因为单片机进入 main 函数以后,在一个主
环里要扫描 N 个任务,从头到尾,把 N 任务扫描一遍,每扫描一遍算“一次主循环每一次“主循环”
都是要消耗一点时间,累计的“主循环”次数越多,所要消耗的时间就越长,但是跟 Delay 唯一的差别是,
Delay 做延时的时候没有办法扫描其它任务,而“累计主循环”内部本身就是在不断扫描其它任务,产生
间越长扫描其它任务的次数就越多,两者是完全相互促进而没有矛盾的。具体内容,请看下面的例子。
【83.2 累计主循环“非阻塞”的一个例子。
现在利用“累计主循环非阻塞”编写一个练习程序,让一 LED 灯闪烁。例子如下
83.2.1 灌入式驱动 8 LED
#include "REG52.H"
#define CYCLE_SUM 5000 //累计主循环次数的设定阀值,该值决定了 LED 闪烁频率
sbit P0_0=P0^0; //利用 sbit 和符号“^”的组合,把变量名字 P0_0 P0.0 引脚关联起
unsigned char Gu8CycleStep=0; //switch 的跳转步骤
unsigned long Gu32CycleCnt=0; //累计主循环的计数器