2单片机设计模式
逻辑设计模式
[[Excalidraw/Drawing 2024-12-05 22.40.35.excalidraw]]
前面三种方法都无法解决降低两种耗时函数之间的影响。
第四种方法可以解决,但是实践困难
轮询设计模式
1 | // 经典单片机程序: 轮询 |
就是while循环按照从上到下的顺序依次调用函数
缺点: 如果一个函数时间太长就会导致下一个函数长时间无法被执行
前后台
1 | // 前后台程序 |
其实就是stm32中的中断程序
如果这个中断信息被触发,就会暂停while循环中的函数,直接执行中断函数
这个场景的优点是触发特别及时。
缺点: 和轮询设计模式一样,如果中断程序执行过久,就会导致原本的函数被阻塞很长时间
更大的缺点 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 前后台程序
void main()
{
while (1)
{
// 后台程序
}
}
// 前台程序
void 滴_中断()
{
回一个信息();
}
// 前台程序
void 啊_中断()
{
喂一口饭();
}
如果同时有 滴 啊 就会导致两个两个函数触发受到影响,先去执行 滴 就会耽误 啊
定时器驱动
1 | // 前后台程序: 定时器驱动 |
简单来说就是每间隔一段时间就触发一次定时器中断函数
适合周期性需要被调用的函数 并且每个函数的执行时间不能超过一个定时器周期
如果
执行时间过长 就会导致
执行被耽误 ## 基于状态机
1 | // 状态机 |
main函数还是轮询
重点在轮询的函数中 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65void 喂一口饭(void)
{
static int state = 0;
switch (state)
{
case 0:
{
/* 舀饭 */
/* 进入下一个状态 */
state++;
break;
}
case 1:
{
/* 喂饭 */
/* 进入下一个状态 */
state++;
break;
}
case 2:
{
/* 舀菜 */
/* 进入下一个状态 */
state++;
break;
}
case 3:
{
/* 喂菜 */
/* 恢复到初始状态 */
state = 0;
break;
}
}
}
void 回一个信息(void)
{
static int state = 0;
switch (state)
{
case 0:
{
/* 查看信息 */
/* 进入下一个状态 */
state++;
break;
}
case 1:
{
/* 打字 */
/* 进入下一个状态 */
state++;
break;
}
case 2:
{
/* 发送 */
/* 恢复到初始状态 */
state = 0;
break;
}
}
}
我的理解就是把一个大任务拆成了几个小任务
以这个函数为例: 这里总共有两个大任务 [[Excalidraw/Drawing 2024-12-05 23.00.22.excalidraw]]
把大任务拆成几个小任务,要求保证小任务的执行时间不能太长,在轮询中,每次只执行两个大任务中的一个小任务
多任务系统
多任务模式
裸机在多任务处理上,终究是时间长,当有两个函数需要被调用时,会有一个函数被耽误
而对于rtos来说, 其实本质上也属于一种轮询,很类似状态机设计模式 但是这个属于系统层面的调用 速度非常快
两个大任务,这次调用一下,等会调用另一个
切换的时间相当短
互斥操作
对于在多线程中 可能会涉及到一个线程需要读取一个参数 而另一个线程需要修改一个参数
这时候如果同时发生,会导致数据的错乱,所以 锁 的概念应运而生
1 | // RTOS程序 |
nnd这块的的互斥真抽象
多看几遍吧 两种进行互斥的方法
1 | void uart_print(char *str) |
第二种 1
2
3
4
5
6
7
8
9void uart_print(char *str)
{
g_canuse--; ① 减一
if( g_canuse == 0 ) ② 判断
{
printf(str); ③ 打印
}
g_canuse++; ④ 加一
}
我感觉怎么没什么用