第21章节 FreeRTOS计数信号量
注意 :该文章摘录自:
新浪博客:http://blog.sina.com.cn/s/blog_98ee3a930102wgpe.html
本章节介绍了讲解了FreeRTOS任务间的同步和资源共享机制,计数信号量。FreeRTOS中计数信号量的源码实现是基于消息队列实现。
本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429
21.1 信号量
21.2 计数信号量API函数
21.3 实验例程说明(任务间通信)
21.4 实验例程说明(中断方式通信)
21.5 总结
21.1 信号量
21.1.1 信号量的概念及其作用
信号量(Semaphores)是20世纪60年代中期的Edgser Dijkstra发明的。使用信号量的最初目的是给共享资源建立一个标志,该标志表示该共享资源被占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,在决定自己的行为。
实际的应用中,信号量的作用又该如何体现呢?比如有个30人的电脑机房,我们就可以创建信号量的初始值是30,表示30个可用资源,不理解的初学者表示信号量还可以有初始值?是的,信号量说白了就是共享资源的数量。另外我们要求一个同学使用一台电脑,这样每个同学使用一台电脑,那么信号量的数值就减一,直到30台电脑都被占用,此时的信号量的数值就是0。如果此时还有几个同学没有电脑使用,那么这几个同学就等待,直到同学离开。有一个同学离开,那么信号量的数值就加1,有两个就加2,依此类推。刚才没有电脑用的同学就有电脑可以用了,有几个同学用,信号量就减几,直到再次没有电脑可以用,这么一个过程就是使用信号量来管理共享资源的过程。
平时使用信号量主要实现以下两个功能:
-
两个任务之间或者中断函数跟任务之间的同步功能,这个和前面章节讲解的事件标志组是类似的。其实就是共享资源为1的时候。
-
多个共享资源的管理,就像上面举的机房上机的例子。
针对这两个功能,FreeRTOS分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅一个资源可用,只不过FreeRTOS对这种都提供了API,而像RTX,uCOS-II,III是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信号量和计数信号量。当然,FreeRTOS使用计数信号量也能够实现同样的效果。
实际上信号量还有很多其它用法,而且极具挑战性,可以大大的开拓大家的视野,有兴趣的同学可以阅读以下《The Little Boob Of Semaphores》,作者是Allen B.Downy。
21.1.2 FreeRTOS任务间计数信号量的实现
任务间信号量的实现是指各个任务之间使用信号量实现任务同步或者资源共享功能。下面我们通过如下框图来说明以下FreeRTOS计数信号量的实现,让大家有一个形象的认识。
运行条件:
- 创建2个任务Task1和Task2。
- 创建计数信号量可用资源为1。
运行过程描述如下:
- 任务Task1运行过程中调用函数xSemaphoreTake获取信号量资源,如果信号量没有被任务Task2占用,Task1将直接获取资源。如果信号量被Task2占用,任务Task1将由运行态转到阻塞状态,等待资源可用。一旦获取了资源并且使用完毕后会通过函数xSemaphoreGive释放掉资源。
- 任务Task2运行过程中调用了函数xSemaphoreTake获取信号量资源,如果信号量没有被任务Task1占用,Task2将直接获取资源。如果信号量被Task1占用,任务Task2由运行态转到阻塞状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数xSemaphoreGive释放掉资源。
上面是一个简单的FreeRTOS任务间计数信号量的使用过程。
21.1.3 FreeRTOS中断方式计数信号量的实现
FreeRTOS中断方式的信号量的实现是指中断函数和FreeRTOS任务之间使用信号量。信号量的中断方式主要是用于实现任务同步,与前面章节讲解事件标志组中断方式是一样的。
下面我们通过如下的框图说明一下FreeRTOS中断方式信号量的实现,让大家有一个形象的认识。
运行条件:
- 创建一个任务Task1和一个串口接受中断。
- 信号量的初始值为0,串口中断调用函数xSemaphoreGiveFromISR释放信号量,任务Task1调用函数xSemaphoreTake获取信号量资源。
运行过程描述如下:
-
任务Task1运行过程中调用函数xSemaphoreTake,由于信号量的初始值是0,没有信号量资源可用,Task1由运行状态到阻塞状态。
-
Task1阻塞的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中调用函数xSemaphoreGiveFromISR释放信号量资源,信号量数值加1,此时信号量计数值为1,任务Task1由阻塞状态进入就绪状态,在调度器的作用下由就绪状态又进入运行态,任务Task1获得信号量后,信号量数值减1,此时信号量计数又变成了0。
-
再次循环执行任务,任务Task1调用函数xSemaphoreTake由于没有资源可用再次进入到阻塞态,等待串口释放信号量资源,如此往复循环。
上面就是一个简单的FreeRTOS中断方式同步过程。实际应用中,中断方式的消息机制要注意以下四个问题:
-
中断函数的执行时间越短越好,防止其他低于这个中断优先级的异常得不到及时响应。
-
实际应用中,建议不要在中断实现消息处理,用户可以在中断服务程序里发送消息通知任务,在任务中实现消息处理,这样可以有效地保证中断服务程序的实时响应。同时此任务也需要设置为高优先级,以便中断函数后任务可以得到及时执行。
-
中断服务程序中一定要调用专用于中断的信号量设置函数,即以FromISR结尾的函数。
-
在操作系统中实现中断服务程序与裸机编程的区别:
- 如果FreeRTOS工程的中断函数中没有调用FreeRTOS的信号量API函数,与裸机编程是一样的。
- 如果FreeRTOS工程的中断函数中调用了FreeRTOS的API函数,退出的时候要检测是否有高级优先级任务就绪,如果有就绪的,需要在退出中断后进行任务切换,这点与裸机编程稍有区别,详见21.4小结实验例程说明(中断方式):
- 例外强烈推荐用户将Cortex-M3内核的STM32F103和Cortex—M4内核的STM32F407,F429的NVIC优先级分组设置为4,即:NVIC_PriorityGroupCofig(NVIC_PriorityGroup_4);这样中断优先级管理将非常方便。
- 用户要在FreeRTOS多任务开启钱就设置好优先级分组,一旦设置好切记不可再修改。
21.2计数信号量API函数
使用如下的18个函数可以实现FreeRTOS的信号量(含计数信号量,二值信号量与互斥信号):
xSemaphoreCreateBinary()
xSemaphoreCreateBinaryStatic()
vSemaphoreCreateBinary()
xSemaphoreCreateCounting()
xSemaphoreCreateCountingStatic()
xSemaphoreCreateMutex()
xSemaphoreCreateMutexStatic
xSem'CreateRecursiveMutex()
xSem'CreateRecursiveMutexStatic()
vSemaphoreDelete()
xSemaphoreGetMutexHolder()
uxSemaphoreGetCount()
xSemaphoreTake()
xSemaphoreTakeFromISR()
xSemaphoreTakeRecursive()
xSemaphoreGive()
xSemaphoreGiveRecursive()
xSemaphoreGiveRecursive()
关于这18个函数的讲解及其使用方法可以看FreeRTOS在线版手册: