1 编程规范
1.1 变量名前缀
变量名前缀 | 含义 |
---|---|
c | char |
s | int16_t, short |
l | int32_t, long |
x | BaseType_t其它非标准的类型:结构体\task handle queue handle 等 |
u | unsigned |
p | 指针 |
uc | uint8_t, unsigned char |
pc | char 指针 |
1.2 函数名前缀
函数名前缀 | 含义 |
---|---|
v | 返回 void |
x | 返回 BaseTpye_t |
2 内存管理
五种heap实现 一般使用 heap_4.c heap_3.c是标准库实现,比较慢 heap_2.c是直接分配的,相邻空间不合并回收 heap_5.c是支持 分隔的
3 任务
3.1 动态创建
3.2 静态分配
3.3 任务调度
- 相同优先级的轮流运行
- 最高优先级的先运行 a. 高优先级的任务未执行完, 更低优先级的任务 永远无法运行 b. 一旦高优先级的任务就绪, 会马上运行 c. 最高优先级的任务有多个, 会轮流运行
3.4 空闲任务
- 检查等待终止的任务(自杀任务)
- 调用hock 函数 用于调试 注意: 在 高优先级 任务 执行后 主动使用 vTtaskDelay 进入阻塞状态 使得空闲任务 得以运行
3.5 dealy 函数
- vTaskDelay : 至少等待指定个数的 Tick Interruppt 才能运行
- vTaskDelayUntil : 等待到指定的绝对时刻,才能变成就绪态
4 同步
4.1 有缺陷的例子
[!example] 例1
有一个全局变量end=0 任务A 执行 计算 大概要 1.5秒 执行完后会设置end=1 任务B 使用 while(end) 等待计算完成 这样会导致 rtos调度 把cpu资源给了无意义 的 任务B while循环 导致 计算缓慢
[!example] 例2 三个任务 使用同一个函数 这个函数 使用i2c在屏幕上打印信息 i2c 是比较耗时的 start,dev_addr,data,stop 如果不加以同步的保护, i2c的信号是混乱的 但是即便使用全局变量来保护,由于检查和设置这个全局变量不是原子性的, 出现冲突也是不可避免的, 即便这个概率非常小 可以使用 关中断的办法 避免调度 但是 这样还是不可避免 的 会使得为获得使用权的任务占用cpu无意义等待
4.2 解决办法
- 环形缓冲区
- 队列
- 信号量
- 互斥量
互斥措施 | 阻塞-唤醒 | ||
---|---|---|---|
全局变量 | 1 | 无 | 无 |
环形缓冲区 | 多个 | 无 | 无 |
队列 | 多个 | 有 | 有 |
4.3 队列
队列的组成: 环形缓冲区 + 两个链表(发送者和接受者) 队列的本质: 环形缓冲区 + 互斥\阻塞-唤醒机制
- 如果队列不传输数据,只调整"数据个数",他就是信号量
- 如果信号量中,限定"数据个数"最大为1,它就是互斥量
如果队列空 读任务没有得到数据 可以进入阻塞 , 等待数据来到(写进程 主动唤醒 或 tick超时 唤醒)