freeRtos学习笔记
freeRtos信号量
信号量种类
信号量分为四种:二值信号量,互斥信号量,计数信号量和递归互斥信号量,其中计数信号量用于管理系统多个共享资源,用计数值表示可用资源数目;二值信号量是一种特殊的计数信号量,和事件组类似,相当于裸机中的标志位,常用于任务之间的同步;互斥信号量和二值信号量类似,相比二值信号量避免了任务优先级翻转问题,因此常用来保护临界资源,但是互斥信号量不可以在中断中使用,只能在任务中使用;递归互斥信号量是一种特殊的互斥信号量,如果任务获取到递归互斥信号量后,该任务中不需要等待递归互斥信号量释放就可以再次获取递归互斥信号量,只需递归层数加一.
计数信号量创建
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, /* 支持最大计数值 */
UBaseType_t uxInitialCount);/* 初始计数值 */
创建计数信号量函数
- 第一个参数设置技术信号量最大计数值
- 第二个参数设置初始计数值
- 返回值 如果创建成功会返回计数信号量句柄,如果heap不足则返回NULL
- 注意 信号量底层都是调用消息队列实现的
二值信号量创建
SemaphoreHandle_t xSemaphoreCreateBinary( void );
二值信号量创建函数
- 返回值 如果创建成功会返回计数信号量句柄,如果heap不足则返回NULL
- 注意 创建成功后 二值信号量初始值为0
- 一般使用任务消息通知来代替二值信号量,效率会更高
互斥信号量创建
SemaphoreHandle_t xSemaphoreCreateMutex( void );
互斥信号量创建函数
- 返回值 如果创建成功会返回计数信号量句柄,如果heap不足则返回NULL
- 注意 创建成功后 互斥信号量初始值为0
- 一般用来保护任务中的临界资源
递归互斥信号量创建
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
递归互斥信号量创建函数
- 返回值 如果创建成功会返回计数信号量句柄,如果heap不足则返回NULL
- 注意 创建成功后 互斥信号量初始值为0
释放信号量
/* 释放一个信号量, 该函数适用于计数信号量 二值信号量 互斥信号量 */
BaseType_t xSemaphoreGive(SemphoreHandle_t xSemaphore)
/* 在中断中释放一个信号量, 该函数适用于计数信号量 二值信号量 */
BaseType_t xSemaphoreGiveFromISR(SemphoreHandle_t xSemaphore,
BaseType_t * const pxHigherPriorityTaskWoken)
/* 释放一个信号量, 该函数适用于递归互斥信号量 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
释放信号量函数
- 第一个参数信号量句柄
- 在中断中使用FormISR结尾的函数,在中断中 第 2 个参数是释放信号量后,是否有更高级别的任务就绪,如果有更高级别任务就绪(优先级更高的任务堵塞在获取信号量上,这里释放了信号量,导致任务从堵塞态转变为就绪态),则pxHigherPriorityTaskWoken变为pdTRUE,然后在中断结束处可以调用taskYIELD()进行任务调度。
- 返回值 pdTRUE释放成功
- 注意 不同信号量使用不同的信号量释放函数,互斥信号量不可以在中断中使用,只能在任务中使用;
获取信号量
/* 获取一个信号量, 该函数适用于计数信号量 二值信号量 互斥信号量 */
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait );
/* 在中断中获取一个信号量, 该函数适用于计数信号量 二值信号量 */
BaseType_t xSemaphoreTakeFromISR
(
SemaphoreHandle_t xSemaphore,
signed BaseType_t *pxHigherPriorityTaskWoken
)
/* 获取一个信号量, 该函数适用于递归互斥信号量 */
BaseType_t xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex,
TickType_t xTicksToWait );
获取信号量函数
- 第一个参数信号量句柄
- 第二个参数为等待时间,信号量底层调用消息队列实现,如果消息队列满,则进行等待的时间
- 在中断中 第 2 个参数是获取信号量后,是否有更高级别的任务就绪,如果有更高级别任务就绪(优先级更高的任务堵塞在释放信号量上,这里获取了一个信号量,导致该任务从堵塞态转变为就绪态),则pxHigherPriorityTaskWoken变为pdTRUE,然后在中断结束处可以调用taskYIELD()进行任务调度。
- 返回值 pdTRUE获取成功
- 注意 不同信号量使用不同的信号量获取函数,互斥信号量不可以在中断中使用,只能在任务中使用;
优先级翻转问题
如果使用普通的二值信号量进行临界资源保护会出现一下问题.
- 有task1 task2 task3 三个任务,任务优先级 task1 > task2 > task3
- task1和task3互斥访问串口printf
- task1执行完成后挂起,然后task3运行,task3运行时获取了串口printf资源
- task3运行过程中,task1由挂起态转变为就绪态,task1运行需要获取串口printf资源,由于task3已经获取了该资源,task1因等待串口资源进入堵塞态.
- task2就绪,由于task2优先级比task3高,所以task2运行.
- task2运行结束挂起后,task3继续执行
- task3运行结束,释放串口资源,task1恢复运行
仔细观察步骤5 6 7会发现,task2优先级明明比task1低,但是task2却先执行,这就是任务优先级翻转
互斥信号量为了解决这个问题,在二值信号量的基础上增加了优先级继承功能. 在步骤4 task1由挂起态转变为就绪态获取串口资源时,会将正在持有该资源的task3优先级改为和task1一样高. 步骤5 task2就绪,但是task3的优先级已经和task1一样,所以task3继续执行,task3执行完毕后,释放串口资源时,会将task3优先级改会原来,然后task1得到执行,taks1执行结束后task2才会得到执行.
本文参考 freertos官方文档 https://freertos.org/a00110.html
《安富莱 STM32-V6 开发板 FreeRTOS 教程》