采用二值信号量同步
二值信号量可以在某个特殊的中断发生时,让任务解除阻塞,相当于让任务与中断
同步。这样就可以让中断事件处理量大的工作在同步任务中完成,中断服务例程(ISR)
中只是快速处理少部份工作。如此,中断处理可以说是被”推迟(deferred)”到一个”处理
(handler)”任务。
如果某个中断处理要求特别紧急,其延迟处理任务的优先级可以设为最高,以保证
延迟处理任务随时都抢占系统中的其它任务。这样,延迟处理任务就成为其对应的 ISR
退出后第一个执行的任务,在时间上紧接着 ISR 执行,相当于所有的处理都在 ISR 中
完成一样。这种方案在图 图 26 中展现。
延迟处理任务对一个信号量进行带阻塞性质的”take”调用,意思是进入阻塞态以等
待事件发生。当事件发生后,ISR 对同一个信号量进行”give”操作,使得延迟处理任务
解除阻塞,从而事件在延迟处理任务中得到相应的处理。
“获取(Taking,带走,按通常的说法译为获取)”和”给出(Giving)”信号量从概念上讲,
不同的应用场合有不同的含义。在经典的信号量术语中,获取信号量等同于一个 P()
操作,而给出信号量等同于一个 V()操作。
在这种中断同步的情形下,信号量可以看作是一个深度为 1 的队列。这个队列由于
最多只能保存一个数据单元,所以其不为空则为满(所谓”二值”)。延迟处理任务调用
xSemaphoreTake()时,等效于带阻塞时间地读取队列,如果队列为空的话任务则进入
阻塞态。当事件发生后,ISR 简单地通过调用 xSemaphoreGiveFromISR()放置一个令
牌(信号量)到队列中,使得队列成为满状态。这也使得延迟处理任务切出阻塞态,并移
除令牌,使得队列再次成为空。当任务完成处理后,再次读取队列,发现队列为空,又
进入阻塞态,等待下一次事件发生。整个流程在图 图 27 中有所展现。
如图 图 27 所示,中断给出信号量,甚至是在信号量第一次被获取之前就给出;而任
务在获取信号量之后再也不给回来。这就是为什么说这种情况与读写队列相似。这也经
常会给大家造成迷惑,因为这种情形和其它信号量的使用场合大不相同。在其它场合下,
任务获得(Take)了信号量之后,必须得给(Give)回来——如同第四章描述一样。
vSemaphoreCreateBinary() API 函数
FreeRTOS 中各种信号量的句柄都存储在 xSemaphoreHandle 类型的变量中。
在 使 用 信 号 量 之 前 , 必 须 先 创 建 它 。 创 建 二 值 信 号 量 使 用
vSemaphoreCreateBinary()API 函数
void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );
xSemaphore 创建的信号量
需要说明的是 vSemaphoreCreateBinary()在实现上是一个宏,所以
信号量变量应当直接传入,而不是传址。本章中包含本函数调用的示
例可用于参考进行复制。
xSemaphoreTake() API 函数
“带走(Taking)”一个信号量意为”获取(Obtain)”或”接收(Receive)”信号量。只有当信
号量有效的时候才可以被获取。在经典信号量术中,xSemaphoreTake()等同于一次 P()
操作。
除互斥信号量(Recursive Semaphore,直译为递归信号量,按通常的说法译为互
斥信号量)外,所有类型的信号量都可以调用函数 xSemaphoreTake()来获取。
但 xSemaphoreTake()不能在中断服务例程中调用。
portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait );
xSemaphore 获取得到的信号量
信号量由定义为 xSemaphoreHandle 类型的变量引用。信号量在使
用前必须先创建。
xTicksToWait 阻塞超时时间。任务进入阻塞态以等待信号量有效的最长时间。
如果 xTicksToWait 为 0,则 xSemaphoreTake()在信号量无效时会
立即返回。
阻塞时间是以系统心跳周期为单位的,所以绝对时间取决于系统心
跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位转换
为毫秒时间单位。
如 果 把 xTicksToWait 设 置 为 portMAX_DELAY , 并 且 在
FreeRTOSConig.h 中设定 INCLUDE_vTaskSuspend 为 1,那么阻
塞等待将没有超时限制。
返回值 有两个可能的返回值:
1. pdPASS
只有一种情况会返回 pdPASS,那就是成功获得信号量。
如果设定了阻塞超时时间(xTicksToWait 非 0),在函数返回之前任务
将被转移到阻塞态以等待信号量有效。如果在超时到来前信号量变
为有效,亦可被成功获取,返回 pdPASS。
2. pdFALSE
未能获得信号量。
如果设定了阻塞超时时间(xTicksToWait 非 0),在函数返回之前任
务将被转移到阻塞态以等待信号量有效。但直到超时信号量也没有
变为有效,所以不会获得信号量,返回 pdFALSE。
xSemaphoreGiveFromISR() API 函数
除互斥信号量外,FreeRTOS 支持的其它类型的信号量都可以通过调用
xSemaphoreGiveFromISR()给出。
xSemaphoreGiveFromISR()是 xSemaphoreGive()的特殊形式,专门用于中断服务
例程中。
portBASE_TYPE xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore,
portBASE_TYPE *pxHigherPriorityTaskWoken );
xSemaphore 给出的信号量
信号量由定义为 xSemaphoreHandle 类型的变量引用。
信号量在使用前必须先创建。
pxHigherPriorityTaskWoken 对某个信号量而言,可能有不止一个任务处于阻塞态在
等待其有效。调用 xSemaphoreGiveFromISR()会让信
号量变为有效,所以会让其中一个等待任务切出阻塞
态。如果调用 xSemaphoreGiveFromISR()使得一个任
务解除阻塞,并且这个任务的优先级高于当前任务(也就
是被中断的任务),那么 xSemaphoreGiveFromISR()会
在 函 数 内 部 将 *pxHigherPriorityTaskWoken 设 为
pdTRUE。
如 果 xSemaphoreGiveFromISR() 将 此 值 设 为
pdTRUE,则在中断退出前应当进行一次上下文切换。
这样才能保证中断直接返回到就绪态任务中优先级最
高的任务中。
返回值 有两个可能的返回值:
1. pdPASS
xSemaphoreGiveFromISR()调用成功。
2. pdFAIL
如果信号量已经有效,无法给出,则返回 pdFAIL。