type
status
password
date
slug
summary
category
URL
tags
icon
1. 事件控制块的原理与创建
1.1 问题与解决方案
下述问题的解决方案就是引入事件控制块,基于事件控制块可以实现信号量、消息队列、事件标志等组件
说明:小结一下,要解决的问题就是同步、互斥、通信(任务之间 & 任务与ISR之间)
1.1.1 如何同步两个任务的运行
任务B运行到同步点后需要等待任务A也到达同步点再继续运行
1.1.2 如何处理多个任务共享资源时的冲突
1.1.3 如何在多个任务间传递消息通信
1.1.4 如何在中断ISR与任务之间传递多个事件标志
1.2 事件控制块原理
1.2.1 事件控制块工作流程
- 任务在事件控制块上等待,暂停运行
- 事件发生,通知事件控制块
- 事件控制块通知等待任务列表中的任务
- 被通知的任务脱离事件控制块,继续运行
1.2.2 事件控制块核心功能
事件控制块有2个核心功能:
- 任务能够在事件控制块上实现等待:将任务从就绪队列移动到等待队列
- 事件发生时能够将特定任务唤醒:将任务从等待队列移动到就绪队列
1.3 设计实现
1.3.1 定义事件控制块结构
事件控制块的核心为,
- 事件类型:用于标识实现的不同组件
- 任务等待队列:用于等待事件的任务实现等待
1.3.2 任务结构增设事件控制块相关字段
说明1:事件控制块相关字段说明
- tEvent *waitEvent:描述任务正在等待的事件,任务会在该事件的等待列表中实现等待
- void *eventMsg:该字段用于实现任务与事件之间的相互通信(e.g. 任务开始等待事件时,任务向事件传递消息;事件唤醒任务时,事件向任务传递消息)
- uint32_t waitEventResult:任务等待事件的结果(当事件唤醒任务时填写)
说明2:任务结构中只有一个等待事件。因为一个任务在同一时刻只会在一个事件上进行等待
1.3.3 添加事件控制块初始化函数
2. 事件控制块的等待与通知
2.1 概述
2.1.1 现有任务状态切换
- 挂起:挂起操作肯定是处于运行态的任务发起的,但是既可以挂起自己也可以挂起其他任务,所以才会有就绪态和运行态都可以进入挂起态的情况
tips:我们现在不允许延时的任务挂起
- 延时态返回运行态或就绪态:延时完成后的操作是将任务从延时队列移除并加入就绪队列,然后触发任务切换,所以如果延时结束的任务恰好是当前优先级最高队列的首个任务,则会进入运行态;否则就是进入就绪态
- 事件控制块的等待:任务从就绪队列移除,进入事件控制块等待队列中暂停运行
- 事件控制块的通知:事件发生后,也是将等待的任务加入就绪队列并触发任务切换,如果是当前优先级最高队列的首个任务则进入运行态。如果等待事件的同时设置超时值,则任务将同时进入2个队列:事件等待队列和延时队列,此时需要注意,
- 如果等待的事件先发生,需要同时将任务从延时队列移除
- 如果等待超时先发生,则需要同时将任务从事件等待队列移除,并标识等待结果为超时
2.2 设计原理
2.2.1 等待事件控制块
任务从就绪队列移除,进入事件控制块等待队列中暂停运行
2.2.2 从事件控制块恢复任务
当事件发生时,将事件控制块等待队列上的任务重新加入就绪队列。至此,RTOS内部任务组织如下:1、就绪队列 2、延时队列 3、被挂起的任务 4、事件控制块等待队列
2.3 设计实现
2.3.1 添加tEventWait函数
说明1:state & msg参数将在后续基于任务控制块实现信号量、消息队列等机制时使用
说明2:tEventWait函数仅完成等待事件的核心功能,并未触发任务调度,任务调度由使用事件控制块的组件调用
说明3:事件等待与唤醒策略
目前实现最简单的策略,等待的任务加入事件等待队列尾部,唤醒时先唤醒事件等待队列的头部,即先等待先唤醒
当然也可以实现更复杂的唤醒策略,比如基于优先级唤醒
2.3.2 添加tEventWakeUp函数
说明1:本RTOS中将task结构state字段的高16位作为不同事件的标识
说明2:唤醒事件时,事件等待队列可能为空,即还没有任务在其上等待,所以需要判断事件等待队列是否为空
说明3:tEventWakeUp函数中也不触发任务调度,也交由外层函数进行
2.3.3 添加tEventRemoveTask函数
说明1:此处没有检查task->waitEvent字段是否为空,因为既然是从事件中移除,就认为该字段不为空
说明2:使用场景
当任务等待的事件超时,需要将该任务强制从事件控制块移除,配合后续对SysTick_Handler的修改就很好理解了
2.3.4 修改SysTick_Handler
说明:SysTick_Handler中新增的部分就是用于处理在等待事件的同时设置了超时,且超时发生时等待的事件尚未发生
3. 事件控制块的清空与状态查询
3.1 清空事件控制块原理
将事件控制块清空,就是将事件控制块等待队列上的任务全部移除并加入相应的就绪队列。如果等待的任务还有超时没有完成,需用同时将任务从延时队列删除
3.2 设计实现
3.2.1 添加tEventRemoveAll函数
3.2.2 添加tEventWaitCount函数
说明:目前实现的事件控制块状态查询函数比较简单,就是查询在事件上等待的任务数量