简单的(伪)多任务调度
本文章主要介绍一种不使用 FreeRTOS 来实现简单的多任务调度的方式。
问题
同时执行多个任务是嵌入式开发中非常经典的问题。常见的场景有:
- 智能时钟内,设置时间闪烁的同时不影响按键的使用
- 播放音乐时不影响按键的使用
- 内置计时器不够用
- 通过 MQTT 将数据在一定时间间隔后上传至云端,同时不影响其他功能
如果只是简单地使用 HAL_Delay 或其他的延时来实现是不够的。
c
void loop() {
// 逻辑 1
HAL_Delay(1000);
}
while (1) {
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) {
// 逻辑 2
}
loop();
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
这样能够实现每 1000ms 执行一次 逻辑 1 的效果。但这样做的问题也很明显,按下按键后 逻辑 2 不能够稳定执行了。
解决方案 1
记录一下 while 循环的次数,循环至一定次数后,执行 逻辑 1,同时清空次数。
c
uint16_t counter = 0;
void loop() {
if (counter == 1000) {
// 逻辑 1
// 清空次数
counter = 0;
}
}
while (1) {
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) {
// 逻辑 2
}
loop();
counter++;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
解决方案 2
不妨来看看 HAL 库内 HAL_Delay 函数的原理:
c
__weak void HAL_Delay(uint32_t Delay)
{
// 获取当前时刻
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
// 处理最小等待时间,该部分可以忽略
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
// 若没到达指定等待时刻,执行空循环
while ((HAL_GetTick() - tickstart) < wait)
{
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
可见,因为该函数内部执行了空循环,所以程序的其他部分才会受到影响。
那能不能让程序的主循环来代替这个空循环呢?这样在等待的过程中,按键也能够被检测了。
于是可以把 HAL_Delay 中的实现照搬过来:
c
// 获取当前时刻
uint32_t tickstart = HAL_GetTick();
uint32_t wait = 1000;
void loop() {
// 若该 if 的条件不满足,则会回到主循环
if ((HAL_GetTick() - tickstart) >= wait) {
// 逻辑 1
// 重置 tickstart
tickstart = HAL_GetTick();
}
}
while (1) {
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) {
// 逻辑 2
}
loop();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21