本文章总结基于官方 FreeRTOS 手册, 测试系统为 ESP32 的 IDF 4.0
本篇续上一篇《不可被忽视的操作系统( FreeRTOS )[1] 》
其中上一篇主要内容为:
FreeRTOS 介绍
FreeRTOS 在 ESP32 中的特殊性
任务相关函数
队列相关函数
本篇内容主要有:
信号量相关函数
计时器相关函数
事件组相关函数
任务通知相关函数
信号量
reertos / include / freertos / semphr.h
信号量是操作系统中重要的一部分, 信号量一般用来进行资源管理和任务同步, FreeRTOS 中信号量又分为
l 二值信号量,
l 计数型信号量,
l 互斥信号量,
l 递归互斥信号量
不同的信号量其应用场景不同, 但有些应用场景是可以互换着使用的
创建二值信号量
创建一个新的二进制信号量 (二值信号量) 实例, 并返回一个句柄, 通过该句柄可以引用新的信号量.
通过使用现有队列机制创建信号量的函数. 队列长度为 1, 因为它是二进制信号量. 数据大小为 0, 因为实际上没有任何存储 - 重要的是队列是否为空或已满(二进制信号量是否可用).
这种信号量可用于任务之间或中断与任务之间的纯同步. 信号量不必一经获得就退还, 因此一个任务 / 中断可以连续地 "提供" 该信号量, 而另一任务 / 中断则可以连续地 "获取" 该信号量. 因此, 这种信号量不使用优先级继承机制. 有关不使用优先级继承的替代方法, 请参见 xSemaphoreCreateMutex().
东小东使用说明:
二值信号量即可以实现事件的计数, 通过 xSemaphoreGive()相关函数释放信号量也就是将计数值加一, 当前其最大值为 1, 通过 xSemaphoreTake()相关函数获取信号量也就是将计数值减一, 当 xSemaphoreTake()最终将计数值减到 0 时, 将会触发 xSemaphoreTake()参数的超时等待.
SemaphoreHandle_t xSemaphoreCreateBinary ()
创建计数型信号量
创建一个新的计数信号量实例, 并返回一个句柄, 通过该句柄可以引用新的计数信号量.
计数信号量通常用于两件事:
1)计数事件.
在这种使用情况下, 事件处理程序将在每次事件发生时 "给出" 信号量(增加信号量计数值), 而处理程序任务将在每次处理事件时 "获得" 信号量(减少信号量计数值). 因此, 计数值是已发生的事件数与已处理的事件数之间的差. 在这种情况下, 期望初始计数值为零.
2)资源管理.
在这种使用情况下, 计数值指示可用资源的数量. 为了获得对资源的控制, 任务必须首先获得一个信号量 - 减少信号量计数值. 当计数值达到零时, 将没有可用资源. 当任务使用资源完成时, 它将 "给予" 信号量 - 增加信号量计数值. 在这种情况下, 期望初始计数值等于最大计数值, 指示所有资源都是空闲的.
东小东使用说明:
计数型信号量即可以实现事件的计数, 通过 xSemaphoreGive()相关函数释放信号量也就是将计数值加一, 其最大值为创建时设置的 uxMaxCount, 通过 xSemaphoreTake()相关函数获取信号量也就是将计数值减一, 当 xSemaphoreTake()最终将计数值减到 0 时, 将会触发 xSemaphoreTake()参数的超时等待.
参数:
uxMaxCount: 可以达到的最大计数值. 当信号量达到此值时, 就不能再 "给予" 它了.
uxInitialCount: 初值, 创建信号时分配给信号量的计数值
返回:
计数型信号量句柄, 失败则返回 null
SemaphoreHandle_t xSemaphoreCreateCounting( uxMaxCount,uxInitialCount )
获取计数型信号量的当前计数值
如果该信号量是一个计数信号量, 则 uxSemaphoreGetCount()返回其当前计数值. 如果该信号量是二进制信号量, 那么如果该信号量可用, 则 uxSemaphoreGetCount()返回 1; 如果该信号量不可用, 则返回 0.
参数: 信号量句柄
UBaseType_t uxSemaphoreGetCount( xSemaphore )
获取信号量
作用于二值信号量, 计数型信号量, 互斥信号量
宏获取信号量. 信号量必须事先通过调用 vSemaphoreCreateBinary(),xSemaphoreCreateMutex()或 xSemaphoreCreateCounting()来创建
通过 xSemaphoreGive 释放后才有效, 才能获取为 pdTRUE
参数:
xSemaphore: 获取信号量的句柄 - 创建信号量时获得.
xBlockTime: 等待信号量可用的时间(以毫秒为单位). 宏端口 TICK_PERIOD_MS 可用于将其转换为实时. 零的阻止时间可用于轮询信号量. portMAX_DELAY 的阻塞时间可以无限期地阻塞(在 FreeRTOSConfig.h 中将 INCLUDE_vTaskSuspend 设置为 1).
返回
如果获得了信号量, 则为 pdTRUE. 如果 xBlockTime 到期而信号灯不可用, 则为 pdFALSE.
BaseType_t xSemaphoreTake( xSemaphore,xBlockTime )
中断服务函数中获取信号量
作用于二值信号量, 计数型信号量, 互斥信号量
用于从 ISR 获取信号量的宏. 信号量必须事先通过调用 vSemaphoreCreateBinary()或 xSemaphoreCreateCounting()来创建.
互斥类型信号量 (通过调用 xSemaphoreCreateMutex() 创建的信号量)不得与此宏一起使用.
可以从 ISR 使用此宏, 但是从 ISR 获取信号量并不常见. 仅当中断从资源池中获取对象时(当信号量指示可用资源的数量时), 才使用计数信号量.
参数:
xSemaphore: 正在使用的信号量的句柄. 这是创建信号量时返回的句柄.
pxHigherPriorityTaskWoken: 如果采用信号量导致任务取消阻止, 并且未阻止任务的优先级高于当前运行的任务, 则 xSemaphoreTakeFromISR()会将 * pxHigherPriorityTaskWoken 设置为 pdTRUE. 如果 xSemaphoreTakeFromISR()将此值设置为 pdTRUE, 则应在退出中断之前请求上下文切换.
返回
如果成功获取了信号, 则为 pdTRUE, 否则为 pdFALSE
BaseType_t xSemaphoreTakeFromISR( xSemaphore,pxHigherPriorityTaskWoken )
释放信号量
作用于二值信号量, 计数型信号量, 互斥信号量
释放信号量的宏. 信号量必须事先通过调用 vSemaphoreCreateBinary(),xSemaphoreCreateMutex()或 xSemaphoreCreateCounting()来创建. 并使用 sSemaphoreTake()获得.
不得从 ISR 使用此宏. 有关可以从 ISR 使用的替代方法, 请参见 xSemaphoreGiveFromISR().
此宏也不得用于使用 xSemaphoreCreateRecursiveMutex()创建的信号量.
参数:
xSemaphore: 释放信号量的句柄. 这是创建信号量时返回的句柄.
返回:
如果已释放信号, 则为 pdTRUE. 如果发生错误, 则为 pdFALSE. 信号量是使用队列实现的. 如果队列上没有空间可发布消息, 则可能会发生错误 - 指示未正确正确获取信号量.
BaseType_t xSemaphoreGive( xSemaphore)
中断服务函数中释放信号量
作用于二值信号量, 计数型信号量, 互斥信号量
释放信号量的宏. 信号量必须事先通过调用 vSemaphoreCreateBinary()或 xSemaphoreCreateCounting()来创建.
互斥类型信号量 (通过调用 xSemaphoreCreateMutex() 创建的信号量)不得与此宏一起使用.
可以从 ISR 使用此宏.
参量
xSemaphore: 释放信号量的句柄. 这是创建信号量时返回的句柄.
pxHigherPriorityTaskWoken: 如果提供信号量导致任务取消阻止, 并且未阻止的任务的优先级高于当前运行的任务, 则 xSemaphoreGiveFromISR()会将 * pxHigherPriorityTaskWoken 设置为 pdTRUE. 如果 xSemaphoreGiveFromISR()将此值设置为 pdTRUE, 则应在退出中断之前请求上下文切换.
返回
如果成功提供了信号量, 则为 pdTRUE, 否则为 errQUEUE_FULL.
BaseType_t xSemaphoreGiveFromISR( xSemaphore,pxHigherPriorityTaskWoken )
小试牛刀之二值信号量任务同步测试
输出效果为 3S 间隔输出两条, 分别为 1 条释放, 1 条获取, 实现任务间的运行同步
必须先释放才能获取
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- // 二值信号量句柄
- SemaphoreHandle_t xSemaphore = NULL;
- // 创建二值信号量来保护共享资源.
- void dong_creat_binary()
- {
- // 调用创建函数, 二进制值信号量
- xSemaphore = xSemaphoreCreateBinary ();
- if(xSemaphore != NULL)
- {
- printf("信号量 xSemaphore 创建成功了");
- }
- }
- // 任务 0 处理函数
- void Task_Run_0(){
- uint32_t num=0;
- while(1){
- num++;
- // 死等获取二值信号量
- xSemaphoreTake(xSemaphore,portMAX_DELAY);
- printf("[task 0] 二值信号量获取成功啦, 触发次数:%d\r\n",num);
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 创建二值信号量
- dong_creat_binary();
- // 启动任务 0, 简化
- // 函数, 名字, 字节大小, 参数, 优先级[0,16](16 最优先), 任务句柄
- BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,NULL);
- if(t0res==pdPASS){
- printf("任务 0 启动成功....\r\n");
- }
- while(1){
- vTaskDelay(3000 / portTICK_PERIOD_MS);// 延时 3S
- xSemaphoreGive(xSemaphore);
- printf("[main] 释放一次二值信号量, 获取将立即执行一次 \ r\n");
- }
- }
小试牛刀之二值信号量任务共享资源保护
测试公共资源访问
测试可见, 公共资源在什么任务里开始, 必须在当前任务里接收后才能被其它任务开始, 起到资源保护作用
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- // 二值信号量句柄
- SemaphoreHandle_t xSemaphore = NULL;
- // 创建二值信号量来保护共享资源.
- void dong_creat_binary()
- {
- // 调用创建函数, 二进制值信号量
- xSemaphore = xSemaphoreCreateBinary ();
- if(xSemaphore != NULL)
- {
- printf("信号量 xSemaphore 创建成功了");
- }
- }
- // 任务 0 处理函数
- void Task_Run_0(){
- while(1){
- // 延时 3S, 延时的时候将会使得低优先级 main 获取 CPU
- vTaskDelay(3000 / portTICK_PERIOD_MS);
- printf("[task 0] 开始信号量等待 ---\r\n");
- // 死等获取二值信号量
- xSemaphoreTake(xSemaphore,portMAX_DELAY);
- printf("[task 0] ***** 公共资源开始 *****-----\r\n");
- vTaskDelay(6000 / portTICK_PERIOD_MS);
- printf("[task 0] ***** 公共资源结束 *****------\r\n");
- xSemaphoreGive(xSemaphore);// 释放信号量
- printf("[task 0] 结束信号量等待 ---\r\n");
- taskYIELD();// 在超时之处尽量调用任务切换检查
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 创建二值信号量
- dong_creat_binary();
- xSemaphoreGive(xSemaphore);// 释放信号量, 刚启动是必须释放一次才能获取
- // 启动任务 0, 简化
- // 函数, 名字, 字节大小, 参数, 优先级[0,16](16 最优先), 任务句柄
- BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,NULL);
- if(t0res==pdPASS){
- printf("任务 0 启动成功....\r\n");
- }
- while(1){
- printf("[main] 开始信号量等待 //////\r\n");
- // 死等获取二值信号量
- xSemaphoreTake(xSemaphore,portMAX_DELAY);
- printf("[main] ***** 公共资源开始 *****//////\r\n");
- vTaskDelay(6000 / portTICK_PERIOD_MS);
- printf("[main] ***** 公共资源结束 *****//////\r\n");
- xSemaphoreGive(xSemaphore);// 释放信号量
- printf("[main] 结束信号量等待 //////\r\n");
- }
- }
计时器
freertos / include / freertos / timers.h
相关的一些宏
- #define configUSE_TIMERS 1
- #define configTIMER_TASK_PRIORITY 1
- #define configTIMER_QUEUE_LENGTH 10
- #define configTIMER_TASK_STACK_DEPTH 2048
第一个: 表示是否启动计时器
第二个: 表示计时器任务的优先级
第三个: 表示计时器队列长度
第四个: 定时器堆栈, 2048 个字节
硬件定时器
CPU 内部自带的定时器模块, 通过初始化, 配置可以实现定时, 定时时间到以后就会执行相应的定时器中断处理函数. 硬件定时器一般都带有其它功能, 比如 PWM 输出, 输入捕获等等功能. 但是缺点是硬件定时器数量少!
软件定时器
软件定时器允许设置一段时间, 当设置的时间到达之后就执行指定的功能函数, 被定时器调用的这个功能函数叫做定时器的回调函数. 回调函数的两次执行间隔叫做定时器的定时周期, 简而言之, 当定时器的定时周期到了以后就会执行回调函数.
创建计时器实例
创建一个新的软件计时器实例, 并返回一个句柄, 通过该句柄可以引用创建的软件计时器.
在 FreeRTOS 的内部实现中, 软件计时器使用一块内存, 其中存储着计时器数据结构. 如果使用 xTimerCreate()创建软件计时器, 则所需的内存将自动在 xTimerCreate()函数内部动态分配.
创建后计时器处于休眠状态. xTimerStart(),xTimerReset(),xTimerStartFromISR(),xTimerResetFromISR(),xTimerChangePeriod()和 xTimerChangePeriodFromISR()API 函数均可用于将计时器转换为活动状态.
参数
pcTimerName: 分配给计时器的文本名称. 这样做纯粹是为了协助调试. 内核本身仅通过其句柄引用计时器, 而从未通过其名称引用计时器.
xTimerPeriodInTicks: 计时器时间段. 时间以滴答周期定义, 因此常数 portTICK_PERIOD_MS 可用于转换以毫秒为单位指定的时间. 例如, 如果计时器必须在 100 个滴答之后过期, 则 xTimerPeriodInTicks 应该设置为 100. 或者, 如果计时器必须在 500ms 之后过期, 则可以将 xPeriod 设置为(500 / portTICK_PERIOD_MS), 前提是 configTICK_RATE_HZ 小于或等于 1000.
uxAutoReload: 如果将 uxAutoReload 设置为 pdTRUE, 则计时器将以 xTimerPeriodInTicks 参数设置的频率重复终止. 如果将 uxAutoReload 设置为 pdFALSE, 则该计时器将是单次计时器, 并在其到期后进入休眠状态.
pvTimerID: 分配给正在创建的计时器的标识符. 通常, 在将同一回调函数分配给多个计时器时, 将在计时器回调函数中使用它来标识哪个计时器到期.
pxCallbackFunction: 计时器到期时要调用的函数. 回调函数必须具有由 TimerCallbackFunction_t 定义的原型, 即 "void vCallbackFunction(TimerHandle_t xTimer);".
TimerHandle_t xTimerCreate(const char * const pcTimerName,const TickType_t xTimerPeriodInTicks,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunction )
获取计时器的 ID
使用用于创建计时器的 xTimerCreated()调用的 pvTimerID 参数将 ID 分配给计时器.
如果将同一个回调函数分配给多个计时器, 则可以在回调函数中使用计时器 ID 来标识实际终止了哪个计时器.
参量
xTimer: 计时器句柄
返回
查询的计时器的 ID
void *pvTimerGetTimerID( const TimerHandle_t xTimer )
设置计时器 ID
设置分配给计时器的 ID.
使用用于创建计时器的 xTimerCreated()调用的 pvTimerID 参数将 ID 分配给计时器.
如果将相同的回调函数分配给多个计时器, 则计时器 ID 可用作特定时间 (本地计时器) 的存储.
参量
xTimer: 计时器句柄
pvNewID: 分配给计时器的 ID.
void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID )
启动计时器
必须将 configUSE_TIMERS 配置常量设置为 1,xTimerStart()才可用.
xTimerStart()启动一个计时器, 该计时器先前是使用 xTimerCreate()API 函数创建的. 如果计时器已经启动并且已经处于活动状态, 则 xTimerStart()具有与 xTimerReset()API 函数等效的功能.
启动计时器可确保计时器处于活动状态. 如果同时没有停止, 删除或重置计时器, 则在调用 xTimerStart()之后, 与计时器关联的回调函数将被称为 "n" 滴答, 其中 "n" 是计时器定义的时间段.
在启动调度程序之前调用 xTimerStart()是有效的, 但是完成此操作后, 计时器将不会真正启动, 直到启动调度程序为止, 并且计时器的到期时间将与启动调度程序有关, 而不是与启动调度程序有关当 xTimerStart()被调用时.
参量
xTimer: 正在启动 / 重新启动的计时器的句柄.
xTicksToWait: 指定在调用 xTimerStart()时, 如果队列已满, 则调用任务应保持在 Blocked 状态的时间, 以等待启动命令成功发送到计时器命令队列的时间(以秒为单位). 如果在调度程序启动之前调用了 xTimerStart(), 则 xTicksToWait 将被忽略, 该参数的值请参考队列
返回
如果即使经过 xTicksToWait 滴答声后仍无法将启动命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级, 尽管计时器的到期时间与实际调用 xTimerStart()有关. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置.
BaseType_t xTimerStart( xTimer, xTicksToWait )
停止计时器
必须将 configUSE_TIMERS 配置常量设置为 1,xTimerStop()才可用.
xTimerStop()停止使用 xTimerStart(),xTimerReset(),xTimerStartFromISR(),xTimerResetFromISR(),xTimerChangePeriod()或 xTimerChangePeriodFromISR()API 函数之一启动的计时器.
停止计时器可确保计时器未处于活动状态.
参量
xTimer: 定时器的句柄正在停止.
xTicksToWait: 指定在调用 xTimerStop()时, 如果队列已满, 则调用任务应保持在 Blocked 状态的时间, 以等待 stop 命令成功发送到计时器命令队列. 如果在调度程序启动之前调用了 xTimerStop(), 则 xTicksToWait 将被忽略.
返回
如果即使经过 xTicksToWait 滴答声也无法将 stop 命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置. 该参数的值请参考队列
BaseType_t xTimerStop( xTimer,xTicksToWait )
重置计时器
必须将 configUSE_TIMERS 配置常量设置为 1,xTimerReset()才可用
xTimerReset()重新启动以前使用 xTimerCreate()API 函数创建的计时器. 如果计时器已经启动并且已经处于活动状态, 则 xTimerReset()将使计时器重新评估其到期时间, 以使其与调用 xTimerReset()的时间有关. 如果计时器处于休眠状态, 则 xTimerReset()具有与 xTimerStart()API 函数等效的功能.
重置计时器可确保计时器处于活动状态. 如果同时没有停止, 删除或重置计时器, 则在调用 xTimerReset()之后, 与计时器关联的回调函数将被称为 "n" 滴答, 其中 "n" 是计时器定义的时间段.
在启动调度程序之前调用 xTimerReset()是有效的, 但是完成此操作后, 计时器将不会真正启动, 直到启动调度程序为止, 并且计时器的到期时间将与启动调度程序的时间有关, 而不是相对于启动调度程序的时间当 xTimerReset()被调用时.
参数:
xTimer: 正在重置 / 启动 / 重新启动的计时器的句柄.
xTicksToWait: 指定在调用 xTimerReset()时, 如果队列已满, 则调用任务应保持在 Blocked 状态的时间, 以等待重置命令成功发送到计时器命令队列的时间(以秒为单位). 如果在调度程序启动之前调用了 xTimerReset(), 则 xTicksToWait 将被忽略.
返回
如果即使经过 xTicksToWait 滴答声后仍无法将重置命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级, 尽管计时器的到期时间与实际调用 xTimerStart()有关. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置.
BaseType_t xTimerReset( xTimer,xTicksToWait )
删除计时器
必须将 configUSE_TIMERS 配置常量设置为 1,xTimerDelete()才可用.
xTimerDelete()删除以前使用 xTimerCreate()API 函数创建的计时器.
参量
xTimer: 定时器的句柄被删除.
xTicksToWait: 指定在调用 xTimerDelete()时, 如果队列已满, 则调用任务应保持在 Blocked 状态的时间, 以等待删除命令成功发送到计时器命令队列. 如果在启动调度程序之前调用了 xTimerDelete(), 则 xTicksToWait 将被忽略.
返回
如果即使经过 xTicksToWait 滴答声后仍无法将删除命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置
BaseType_t xTimerDelete( xTimer,xTicksToWait )
查看计时器的状态
计时器处于休眠状态. xTimerStart(),xTimerReset(),xTimerStartFromISR(),xTimerResetFromISR(),xTimerChangePeriod()和 xTimerChangePeriodFromISR()API 函数均可用于将计时器转换为活动状态.
参量
xTimer: 计时器句柄
返回
如果计时器处于休眠状态, 则将返回 pdFALSE. 如果计时器处于活动状态, 则将返回 pdFALSE 以外的值.
BaseType_t xTimerIsTimerActive(TimerHandle_t xTimer )
更改计时器周期
必须将 configUSE_TIMERS 配置常量设置为 1,xTimerChangePeriod()才可用
xTimerChangePeriod()更改以前使用 xTimerCreate()API 函数创建的计时器的周期.
可以调用 xTimerChangePeriod()来更改活动或休眠状态计时器的周期.
参数
xTimer: 正在更改其周期的计时器的句柄.
xNewPeriod:xTimer 的新时期. 计时器周期以滴答周期指定, 因此常数 portTICK_PERIOD_MS 可用于转换以毫秒为单位指定的时间. 例如, 如果计时器必须在 100 个滴答之后过期, 则 xNewPeriod 应该设置为 100. 或者, 如果计时器必须在 500ms 之后过期, 那么可以将 xNewPeriod 设置为(500 / portTICK_PERIOD_MS), 前提是 configTICK_RATE_HZ 小于或等于 1000. .
xTicksToWait: 指定在调用 xTimerChangePeriod()时队列已满的情况下, 调用任务应保持在 "阻塞" 状态, 等待更改周期命令成功发送到计时器命令队列的时间(以滴答为单位). 如果在调度程序启动之前调用了 xTimerChangePeriod(), 则 xTicksToWait 将被忽略.
返回
如果即使经过 xTicksToWait 滴答声之后仍无法将更改周期命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置.
BaseType_t xTimerChangePeriod( xTimer,xNewPeriod,xTicksToWait )
获取定时器周期(嘀嗒数)
参量
xTimer: 计时器句柄
返回
计时器的周期, 以滴答为单位
TickType_t xTimerGetPeriod(TimerHandle_t xTimer )
获取定时器周期(秒)
以秒为单位返回计时器将到期的时间. 如果小于当前滴答计数, 则到期时间从当前时间开始溢出.
参量
xTimer: 计时器句柄
返回
如果计时器正在运行, 则将返回计时器下一次到期的时间(以秒为单位). 如果计时器未运行, 则返回值不确定
TickType_t xTimerGetExpiryTime(TimerHandle_t xTimer )
获取计时器名称
返回创建计时器时分配给计时器的名称.
参量
xTimer: 计时器句柄
返回
分配给 xTimer 参数指定的计时器的名称
const char * pcTimerGetTimerName(TimerHandle_t xTimer )
计时器启动 / 停止 / 重置对应的中断函数有
参数
xTimer: 正在启动 / 重新启动的计时器的句柄.
pxHigherPriorityTaskWoken: 计时器服务 / 守护程序任务的大部分时间都处于 "阻塞" 状态, 等待消息到达计时器命令队列. 调用 xTimerStartFromISR()会将消息写入计时器命令队列, 因此有可能将计时器服务 / 守护程序任务转换为 "已阻止" 状态. 如果调用 xTimerStartFromISR()导致计时器服务 / 守护程序任务退出 "阻塞" 状态, 并且计时器服务 / 守护程序任务的优先级等于或大于当前正在执行的任务 (被中断的任务), 则 * pxHigherPriorityTaskWoken 将获得在 xTimerStartFromISR() 函数内部将其设置为 pdTRUE. 如果 xTimerStartFromISR()将此值设置为 pdTRUE, 则应在中断退出之前执行上下文切换.
返回:
如果无法将启动命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级, 尽管计时器的到期时间与实际调用 xTimerStartFromISR()的时间有关. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置
启动计时器
TickType_t xTimerStartFromISR( xTimer,pxHigherPriorityTaskWoken )
停止计时器
TickType_t xTimerStopFromISR( xTimer,pxHigherPriorityTaskWoken )
重置计时器
TickType_t xTimerResetFromISR( xTimer,pxHigherPriorityTaskWoken )
计时器设置周期中断使用函数
参量
xTimer: 正在更改其周期的计时器的句柄.
xNewPeriod:xTimer 的新时期. 计时器周期以滴答周期指定, 因此常数 portTICK_PERIOD_MS 可用于转换以毫秒为单位指定的时间. 例如, 如果计时器必须在 100 个滴答之后过期, 则 xNewPeriod 应该设置为 100. 或者, 如果计时器必须在 500ms 之后过期, 那么可以将 xNewPeriod 设置为(500 / portTICK_PERIOD_MS), 前提是 configTICK_RATE_HZ 小于或等于 1000. .
pxHigherPriorityTaskWoken: 计时器服务 / 守护程序任务的大部分时间都处于 "阻塞" 状态, 等待消息到达计时器命令队列. 调用 xTimerChangePeriodFromISR()会将消息写入计时器命令队列, 因此有可能将计时器服务 / 守护程序任务从 "阻塞" 状态转换出来. 如果调用 xTimerChangePeriodFromISR()导致计时器服务 / 守护程序任务退出 "阻塞" 状态, 并且计时器服务 / 守护程序任务的优先级等于或大于当前正在执行的任务 (被中断的任务), 则 * pxHigherPriorityTaskWoken 将获得在 xTimerChangePeriodFromISR() 函数内部将其设置为 pdTRUE. 如果 xTimerChangePeriodFromISR()将此值设置为 pdTRUE, 则应在中断退出之前执行上下文切换.
返回:
如果无法将更改计时器周期的命令发送到计时器命令队列, 则将返回 pdFAIL. 如果命令已成功发送到计时器命令队列, 则将返回 pdPASS. 实际处理命令的时间将取决于计时器服务 / 守护程序任务相对于系统中其他任务的优先级. 计时器服务 / 守护程序任务优先级由 configTIMER_TASK_PRIORITY 配置常量设置
TickType_t xTimerChangePeriodFromISR( xTimer,xNewPeriod,pxHigherPriorityTaskWoken )
小试牛刀
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- #include "freertos/timers.h"
- // 计时器器句柄
- TimerHandle_t onetimer = NULL;
- TimerHandle_t cirtimer = NULL;
- // 计时器回调函数
- void oneTimerCallback( TimerHandle_t pxTimer )
- {
- uint32_t timerid=(uint32_t)pvTimerGetTimerID(pxTimer);// 获取 ID
- printf("[单次计时任务 onetimer] 回调函数触发, ID 为:%d\r\n",timerid);
- if( xTimerReset( pxTimer, 100 ) == pdPASS )
- {
- printf("[单次计时任务 onetimer] 重置成功 *****\r\n");
- }
- }
- // 计时器回调函数
- void cirTimerCallback( TimerHandle_t pxTimer )
- {
- printf("[循环计时任务 cirtimer] 回调函数触发 \ r\n");
- }
- void dong_create_timer()
- {
- // 创建一个一次性计时任务
- onetimer = xTimerCreate("dong_onetimer", // 设置一个名词, 便于调试
- ( 2000 / portTICK_PERIOD_MS), // 定时周期
- pdFALSE, // pdFALSE 为单次, pdTRUE 为循环
- (void *)100, // 设置一个 ID 用于标识
- oneTimerCallback // 回调函数
- );
- if( onetimer != NULL )
- {
- printf("[单次计时任务 onetimer] 创建成功 \ r\n");
- xTimerStart( onetimer, 10 );// 开启计数器
- }
- // 创建一个循环计时任务
- cirtimer = xTimerCreate("dong_cirtimer", // 设置一个名词, 便于调试
- ( 4000 / portTICK_PERIOD_MS), // 定时周期
- pdTRUE, // pdFALSE 为单次, pdTRUE 为循环
- (void *)11, // 设置一个 ID 用于标识
- cirTimerCallback // 回调函数
- );
- if( cirtimer != NULL )
- {
- printf("[循环计时任务 cirtimer] 创建成功 \ r\n");
- xTimerStart( cirtimer, 10 );// 开启计时器
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 创建并开始计时器
- dong_create_timer();
- while(1){
- //taskYIELD();// 任务调度
- vTaskDelay(10);// 还是的用延时函数进行任务调度
- }
- }
在上述例程中添加延时函数
- // 计时器回调函数
- void oneTimerCallback( TimerHandle_t pxTimer )
- {
- uint32_t timerid=(uint32_t)pvTimerGetTimerID(pxTimer);// 获取 ID
- printf("[单次计时任务 onetimer] 回调函数触发, ID 为:%d\r\n",timerid);
- printf("[单次计时任务 onetimer] 10/// 秒延时开始啦 \ r\n");
- vTaskDelay(10000 / portTICK_PERIOD_MS);
- printf("[单次计时任务 onetimer] 10/// 秒延时结束啦 \ r\n");
- if( xTimerReset( pxTimer, 100 ) == pdPASS )
- {
- printf("[单次计时任务 onetimer] 重置成功 *****\r\n");
- }
- }
输出:
可见:
延时函数将阻塞定时任务, 会影响到其它定时任务功能, 所以定时回调函数中操作尽量少
事件组
freertos / include / freertos / event_groups.h
以数据位标志事件
创建事件组
尽管事件组与滴答无关, 但出于内部实现的原因, 事件组中可使用的位数取决于 FreeRTOSConfig.h 中的 configUSE_16_BIT_TICKS 设置. 如果 configUSE_16_BIT_TICKS 为 1, 则每个事件组包含 8 个可用位(位 0 至位 7). 如果 configUSE_16_BIT_TICKS 设置为 0, 则每个事件组都有 24 个可用位(位 0 到位 23).EventBits_t 类型用于在事件组中存储事件位.
返回: 事件组句柄, 创建失败为 NULL
EventGroupHandle_t xEventGroupCreate()
等待事件位
[可能]阻止等待先前创建的事件组中的一个或多个位设置.
不能从中断中调用此函数
参数:
xEventGroup: 事件组句柄. 事件组必须先前已通过调用 xEventGroupCreate()创建
uxBitsToWaitFor: 按位的值, 指示事件组中要测试的一位或多位. 例如, 要等待位 0 和 / 或位 2, 请将 uxBitsToWaitFor 设置为 0x05. 要等待位 0 和 / 或位 1 和 / 或位 2, 请将 uxBitsToWaitFor 设置为 0x07. 等等
xClearOnExit: 如果 xClearOnExit 设置为 pdTRUE, 则如果满足等待条件 (如果函数由于超时以外的原因返回), 则在 xEventGroupWaitBits() 返回之前, 将清除事件组中设置的 uxBitsToWaitFor 中的任何位. 如果 xClearOnExit 设置为 pdFALSE, 则在对 xEventGroupWaitBits()的调用返回时, 事件组中设置的位不会更改.
xWaitForAllBits: 如果 xWaitForAllBits 设置为 pdTRUE, 则当 uxBitsToWaitFor 中的所有位都已设置或指定的块时间到期时, xEventGroupWaitBits()将返回. 如果将 xWaitForAllBits 设置为 pdFALSE, 则当设置 uxBitsToWaitFor 中设置的任何一位或指定的块时间到期时, xEventGroupWaitBits()将返回. 阻止时间由 xTicksToWait 参数指定
xTicksToWait: 等待设置 uxBitsToWaitFor 指定的位中的一个 / 全部 (取决于 xWaitForAllBits 值) 的最长时间(以 "ticks" 指定), 与其它设置等待时间一致, 可以立即, 一段, 永远.
返回
在等待位被置位或块时间到期时事件组的值. 测试返回值以了解设置了哪些位. 如果 xEventGroupWaitBits()由于其超时到期而返回, 则不会设置所有等待的位. 如果由于设置了 xEventGroupWaitBits()而等待的位而返回, 则在 xClearOnExit 参数设置为 pdTRUE 的情况下, 返回值是在自动清除任何位之前的事件组值.
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait )
获取事件组中当前值
返回事件组中位的当前值. 不能从中断使用此功能.
参量
xEventGroup: 正在查询的事件组
返回
调用 xEventGroupGetBits()时的事件组位
EventBits_t xEventGroupGetBits( xEventGroup )
清除事件组中的位
清除事件组中的位. 不能从中断中调用此函数.
参数:
xEventGroup: 事件组句柄, 其中的位将被清除
uxBitsToClear: 按位表示在事件组中要清除的一个或多个位. 例如, 仅清除位 3, 将 uxBitsToClear 设置为 0x08. 要清除位 3 和位 0, 请将 uxBitsToClear 设置为 0x09.
返回:
清除指定位之前的事件组的值
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToClear )
设置事件组中的位
设置事件组中的位. 不能从中断中调用此函数. xEventGroupSetBitsFromISR()是可以从中断中调用的版本.
在事件组中设置位将自动解除阻止等待这些位的任务.
参数:
xEventGroup: 要设置位的事件组.
uxBitsToSet: 按位的值, 指示要设置的一个或多个位. 例如, 要仅设置位 3, 请将 uxBitsToSet 设置为 0x08. 要设置位 3 和位 0, 请将 uxBitsToSet 设置为 0x09.
返回
返回 xEventGroupSetBits()调用时事件组的值. 返回值可能清除了 uxBitsToSet 参数指定的位有两个原因. 首先, 如果设置一个位导致正在等待该位离开阻塞状态的任务, 则有可能该位将被自动清除 (请参阅 xEventGroupWaitBits() 的 xClearBitOnExit 参数). 其次, 任何优先级高于被称为 xEventGroupSetBits()的任务的无阻塞 (或就绪状态) 任务都将执行, 并且可能会在调用 xEventGroupSetBits()返回之前更改事件组的值.
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet )
删除事件组
删除以前通过调用 xEventGroupCreate()创建的事件组. 在事件组上被阻止的任务将被取消阻止, 并获得 0 作为事件组的值.
参数:
xEventGroup 事件组句柄
Void vEventGroupDelete(EventGroupHandle_t xEventGroup )
中断中获取事件组的位
可以从 ISR 调用的 xEventGroupGetBits()版本
参量
xEventGroup: 正在查询的事件组
返回
调用 xEventGroupGetBitsFromISR()时的事件组位
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup )
中断中清除数据组的位
可以从中断中调用的 xEventGroupClearBits()版本.
在事件组中设置位不是确定性操作, 因为可能有未知数量的任务正在等待设置一个或多个位. FreeRTOS 不允许在禁用中断时执行不确定的操作, 因此通过挂起调度程序而不是禁用中断来保护从任务访问的事件组. 结果, 不能从中断服务程序直接访问事件组. 因此, xEventGroupClearBitsFromISR()向计时器任务发送一条消息, 以在计时器任务的上下文中执行清除操作.
参量
xEventGroup: 事件组, 其中的位将被清除.
uxBitsToClear: 按位的值, 指示要清除的一个或多个位. 例如, 仅清除位 3, 将 uxBitsToClear 设置为 0x08. 要清除位 3 和位 0, 请将 uxBitsToClear 设置为 0x09
返回
如果成功执行了执行功能的请求, 则返回 pdPASS, 否则返回 pdFALSE. 如果计时器服务队列已满, 则将返回 pdFALSE
xEventGroupClearBitsFromISR( xEventGroup,uxBitsToClear )
中断中设置事件组的位
可以从中断中调用的 xEventGroupSetBits()版本.
在事件组中设置位不是确定性操作, 因为可能有未知数量的任务正在等待设置一个或多个位. FreeRTOS 不允许在中断或关键部分执行不确定的操作. 因此, xEventGroupSetBitFromISR()向计时器任务发送一条消息, 以在计时器任务的上下文中执行设置操作 - 在该上下文中, 使用调度程序锁来代替关键节.
参量
xEventGroup: 要设置位的事件组.
uxBitsToSet: 按位的值, 指示要设置的一个或多个位. 例如, 要仅设置位 3, 请将 uxBitsToSet 设置为 0x08. 要设置位 3 和位 0, 请将 uxBitsToSet 设置为 0x09.
pxHigherPriorityTaskWoken: 如上所述, 调用此函数将导致消息发送到计时器守护程序任务. 如果计时器守护程序任务的优先级高于当前正在运行的任务 (中断被中断的任务) 的优先级, 则 xEventGroupSetBitsFromISR()将 * pxHigherPriorityTaskWoken 设置为 pdTRUE, 指示应在中断退出之前请求上下文切换. 因此, 必须将 * pxHigherPriorityTaskWoken 初始化为 pdFALSE.
返回
如果成功执行了执行功能的请求, 则返回 pdPASS, 否则返回 pdFALSE. 如果计时器服务队列已满, 则将返回 pdFALSE.
xEventGroupSetBitsFromISR( xEventGroup,uxBitsToSet,pxHigherPriorityTaskWoken )
小试牛刀
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/event_groups.h"
- // 事件组句柄
- EventGroupHandle_t eventgroup=NULL;
- // 管理的位,
- #define BIT_0 (1 <<0)
- #define BIT_5 (1 << 5)
- // 任务 0 处理函数
- void Task_Run_0(){
- while(1){
- printf("[task 0] 事件标志组等待开始 ---\r\n");
- xEventGroupWaitBits(
- eventgroup, // 事件标志组句柄
- BIT_0 | BIT_5, // 时间标志位
- pdTRUE, //pdTRUE 为满足条件则清除返回, pdFALSE 为不清除返回
- pdTRUE, //pdTRUE 为所有标志位满足才有效, pdFALSE 为部分满足有效
- portMAX_DELAY); // 等待时间, portMAX_DELAY 为一直等待, 0 为立即返回, 其它为节拍数
- printf("[task 0] 事件标志组等待结束 ---\r\n");
- printf("[task 0] 开始动作啦 ---\r\n");
- printf("[task 0] .......................---\r\n");
- printf("[task 0] 结束动作啦 ---\r\n");
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 创建时间组
- eventgroup=xEventGroupCreate();
- // 启动任务 0, 简化
- // 函数, 名字, 字节大小, 参数, 优先级[0,16](16 最优先), 任务句柄
- BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,NULL);
- if(t0res==pdPASS){
- printf("任务 0 启动成功....\r\n");
- }
- while(1){
- printf("[main] /////// 开始设置位 //////\r\n");
- printf("[main] 设置位: BIT_0\r\n");
- xEventGroupSetBits(eventgroup, BIT_0);
- vTaskDelay(2000 / portTICK_PERIOD_MS);
- printf("[main] 设置位: BIT_5\r\n");
- xEventGroupSetBits(eventgroup, BIT_5);
- vTaskDelay(3000 / portTICK_PERIOD_MS);
- printf("[main] 设置位: BIT_5|BIT_0 \r\n");
- xEventGroupSetBits(eventgroup, BIT_5|BIT_0);
- vTaskDelay(3000 / portTICK_PERIOD_MS);
- }
- }
任务通知
任务通知在 FreeRTOS 中是一个可选的功能, 要使用任务通知的话就需要将宏 contfigUSE TASK NOTIFICATIONS 定义为 1
FreeRTOS 的每个任务都有一个 32 位的通知值, 任务控制块中的成员变量 ulNotifiedValue 就是这个通知值. 任务通知是一个事件, 假如某个任务通知的接收任务因为等待任务通知而阻塞的话, 向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态. 也可以更新接收任务的任务通知值, 任务通知可以通过如下方法更新接收任务的通知值:
不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理).
覆盖接收任务的通知值.
更新接收任务通知值的一个或多个 bit. 增加接收任务的通知值.
合理, 灵活的使用上面这些更改任务通知值的方法可以在一些场合中替代队列, 二值信号. 量, 计数型信号量和事件标志组. 使用任务通知来实现二值信号量功能的时候, 解除任务阻塞的时间比直接使用二值信号量要快 45%(FreeRTOS 官方测试结果, 使用 v8.1.2 版本中的二值信号量, GCC 编译器,-02 优化的条件下测试的, 没有使能断言函数 configASSERTO), 并且使用的 RAM 更少!
任务通知的发送使用函数 xTaskNotify)或者 xTaskNotifyGive)(还有此函数的中断版本)来完成, 这个通知值会一直被保存着, 直到接收任务调用函数 xTaskNotifyWait)或者 ulTaskNotifyTake)来获取这个通知值. 假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态. 任务通知虽然可以提高速度, 并且减少 RAM 的使用, 但是任务通知也是有使用限制的:
FreeRTOS 的任务通知只能有一个接收任务, 其实大多数的应用都是这种情况.
接收任务可以因为接收任务通知而进入阻塞态, 但是发送任务不会因为任务通知发送失败而阻塞
发送任务通知
configUSE_TASK_NOTIFICATIONS 必须未定义或定义为 1 才能使此功能可用.
将 configUSE_TASK_NOTIFICATIONS 设置为 1 时, 每个任务都有其自己的专用 "通知值", 该值是 32 位无符号整数(uint32_t).
可以使用中介对象将事件发送到任务. 此类对象的示例是队列, 信号量, 互斥对象和事件组. 任务通知是一种将事件直接发送到任务而无需此类中介对象的方法.
发送给任务的通知可以选择执行某项操作, 例如更新, 覆盖或增加任务的通知值. 这样, 任务通知可用于将数据发送到任务, 或用作轻量级和快速二进制或计数信号量.
发送给任务的通知将保持待处理状态, 直到任务调用 xTaskNotifyWait()或 ulTaskNotifyTake()将其清除为止. 如果通知到达时任务已经处于 "阻止" 状态以等待通知, 则该任务将自动从 "阻止" 状态中删除(取消阻止), 并清除通知.
任务可以使用 xTaskNotifyWait()来 [可选地] 阻塞以等待通知挂起, 或者使用 ulTaskNotifyTake()来 [可选地] 阻塞以等待其通知值具有非零值. 处于 "阻塞" 状态时, 该任务不会消耗任何 CPU 时间.
参数:
xTaskToNotify: 通知任务的句柄. 可以从用于创建任务的 xTaskCreate()API 函数返回任务的句柄, 并且可以通过调用 xTaskGetCurrentTaskHandle()获得当前正在运行的任务的句柄.
ulValue: 可以随通知一起发送的数据. 数据的使用方式取决于 eAction 参数的值.
eAction: 指定通知如何更新任务的通知值(如果有的话).eAction 的有效值如下:
eNoAction = 0, 无动作, 通知任务而不更新其通知值. 始终返回 pdPASS.
eSetBits, 任务的通知值与 ulValue 按位或, 始终返回 pdPASS.
eIncrement, 任务的通知值增加 1, 始终返回 pdPASS.
eSetValueWithOverwrite, 复写方式更新通知值, 不管任务是否读取该值, 都将更新通知值, 始终返回 pdPASS
esetvaluewithoutoverwrite 果任务已经读取了之前的值, 则设置任务的通知值. 如果被通知的任务尚未有待处理的通知, 则该任务的通知值将设置为 ulValue,xTaskNotify()将返回 pdPASS. 如果要通知的任务已经有待处理的通知, 则不执行任何操作, 并返回 pdFAIL.
BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction )
ISR 发送任务通知
configUSE_TASK_NOTIFICATIONS 必须未定义或定义为 1 才能使此功能可用.
相较于 xTaskNotify 多了如下参数:
pxHigherPriorityTaskWoken: 如果发送通知导致发送通知的任务离开阻塞状态, 并且未阻塞任务的优先级高于当前运行的任务, 则 xTaskNotifyFromISR()会将 * pxHigherPriorityTaskWoken 设置为 pdTRUE. 如果 xTaskNotifyFromISR()将此值设置为 pdTRUE, 则应在退出中断之前请求上下文切换. 从 ISR 请求上下文切换的方式取决于端口 - 有关正在使用的端口, 请参阅文档页面
BaseType_t xTaskNotifyFromISR(TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t * pxHigherPriorityTaskWoken )
等待任务通知
configUSE_TASK_NOTIFICATIONS 必须未定义或定义为 1 才能使此功能可用.
任务可以使用 xTaskNotifyWait()来 [可选地] 阻塞以等待通知挂起, 或者使用 ulTaskNotifyTake()来 [可选地] 阻塞以等待其通知值具有非零值. 处于 "阻塞" 状态时, 该任务不会消耗任何 CPU 时间.
参数:
l ulBitsToClearOnEntry 注意: 等待前的清除, 在检查任务是否有待处理的通知之前, 将在调用任务的通知值中清除 ulBitsToClearOnEntry 值中设置的位, 如果没有待处理的通知, 则将阻塞该任务. 将 ulBitsToClearOnEntry 设置为 ULONG_MAX(如果包括 limits.h)或 0xffffffffUL(如果不包括 limits.h)将具有将任务的通知值重置为 0 的作用. 将 ulBitsToClearOnEntry 设置为 0 将使任务的通知值保持不变.
l ulBitsToClearOnExit: 退出等待的清除, 如果在调用任务退出 xTaskNotifyWait()函数之前有待处理或已收到通知, 则使用 pulNotificationValue 参数传递任务的通知值 (请参阅 xTaskNotify()API 函数). 然后, 将在任务的通知值中清除在 ulBitsToClearOnExit 中设置的所有位(请注意, 在清除任何位之前已设置 * pulNotificationValue). 将 ulBitsToClearOnExit 设置为 ULONG_MAX(如果包括 limits.h) 或 0xffffffffUL(如果不包括 limits.h)将在功能退出之前将任务的通知值重置为 0. 将 ulBitsToClearOnExit 设置为 0 将在函数退出时使任务的通知值保持不变(在这种情况下, 在 pulNotificationValue 中传递的值将与任务的通知值匹配).
l pulNotificationValue: 用于将任务的通知值传递出函数. 注意, 由于 ulBitsToClearOnExit 非零而导致清除任何位, 不会影响传递的值.
l xTicksToWait: 如果在调用 xTaskNotifyWait()时通知尚未挂起, 则任务在 "已阻止" 状态下等待接收通知的最长时间. 处于 "阻塞" 状态时, 该任务不会消耗任何处理时间. 这是在内核滴答中指定的, 宏 pdMS_TO_TICSK(value_in_ms)可用于将以毫秒为单位指定的时间转换为以滴答为单位指定的时间.
返回
如果收到通知(包括调用 xTaskNotifyWait 时已待处理的通知), 则返回 pdPASS. 否则, 返回 pdFAIL.
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t * pulNotificationValue,TickType_t xTicksToWait )
简化的通知
适用于模拟任务对任务的二值信号量和计数型信号量功能
参数: 接收的任务句柄
返回: 始终返回 pdPASS
xTaskNotifyGive( xTaskToNotify )
函数原型
可见, 其功能是给 eAction 参数的值进行加一操作
#define xTaskNotifyGive( xTaskToNotify ) xTaskNotify( ( xTaskToNotify ), 0, eIncrement )
简化的接收
适用于模拟任务对任务的二值信号量和计数型信号量功能
参数:
xClearCountOnExit: 如果 xClearCountOnExit 为 pdFALSE, 则函数退出时任务的通知值将减小. 这样, 通知值就像计数信号量一样. 如果 xClearCountOnExit 不是 pdFALSE, 则函数退出时, 任务的通知值将清除为零. 这样, 通知值的作用就像一个二进制信号量.
xTicksToWait: 如果在调用 ulTaskNotifyTake()时计数尚未大于零, 则任务应在 "阻塞" 状态下等待的最大时间, 以使该任务的通知值大于零. 处于 "阻塞" 状态时, 该任务不会消耗任何处理时间. 这是在内核滴答中指定的, 宏 pdMS_TO_TICSK(value_in_ms)可用于将以毫秒为单位指定的时间转换为以滴答为单位指定的时间.
返回
任务的通知计数在清零或递减之前(请参阅 xClearCountOnExit 参数)
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,TickType_t xTicksToWait )
其功能是给 eAction 参数的值进行减一或清零操作
小试牛刀(任务同步效果)
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- // 任务句柄
- TaskHandle_t TaskH_0=NULL;
- // 任务 0 处理函数
- void Task_Run_0(){
- uint32_t Noti_vlaue=0;
- while(1){
- /*
- 参数 1: 进入时, 传递的参数取反后按位与来设定任务通知初值(ULONG_MAX 表示清零, 0 表示值不变, 其他值按规则)
- 参数 2: 退出时, 传递的参数取反后按位与来设定任务通知初值(ULONG_MAX 表示清零, 0 表示值不变, 其他值按规则)
- 参数 3: 通知值获取
- 参数 4: 等待时间
- */
- xTaskNotifyWait(ULONG_MAX,ULONG_MAX,&Noti_vlaue,portMAX_DELAY);
- printf("[task 0] 接收到通知, 其值为:%d\r\n",Noti_vlaue);
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 启动任务 0, 简化
- // 函数, 名字, 字节大小, 参数, 优先级[0,16](16 最优先), 任务句柄
- BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,&TaskH_0);
- if(t0res==pdPASS){
- printf("任务 0 启动成功....\r\n");
- }
- while(1){
- vTaskDelay(3000 / portTICK_PERIOD_MS);// 延时 3S
- printf("[main] 即将发送一次通知 \ r\n");
- /*
- 参数 1: 任务句柄
- 参数 2: 通知值
- 参数 3:
- eNoAction = 0, 无动作, 通知任务而不更新其通知值
- eSetBits, 任务的通知值与 ulValue 按位或
- eIncrement, 任务的通知值增加 1
- eSetValueWithOverwrite, 复写方式更新通知值, 不管任务是否读取该值, 都将更新通知值
- eSetValueWithoutOverwrite 果任务已经读取了之前的值, 则设置任务的通知值
- */
- xTaskNotify(TaskH_0,1,eNoAction);
- }
- }
小试牛刀(任务同步与值传递效果)
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- // 任务句柄
- TaskHandle_t TaskH_0=NULL;
- // 任务 0 处理函数
- void Task_Run_0(){
- uint32_t Noti_vlaue=0;
- while(1){
- /*
- 参数 1: 进入时, 传递的参数取反后按位与来设定任务通知初值(ULONG_MAX 表示清零, 0 表示值不变, 其他值按规则)
- 参数 2: 退出时, 传递的参数取反后按位与来设定任务通知初值(ULONG_MAX 表示清零, 0 表示值不变, 其他值按规则)
- 参数 3: 通知值获取
- 参数 4: 等待时间
- */
- xTaskNotifyWait(ULONG_MAX,ULONG_MAX,&Noti_vlaue,portMAX_DELAY);
- printf("[task 0] 接收到通知, 其值为:%d\r\n",Noti_vlaue);
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 启动任务 0, 简化
- // 函数, 名字, 字节大小, 参数, 优先级[0,16](16 最优先), 任务句柄
- BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,&TaskH_0);
- if(t0res==pdPASS){
- printf("任务 0 启动成功....\r\n");
- }
- uint32_t num=0;
- while(1){
- vTaskDelay(3000 / portTICK_PERIOD_MS);// 延时 3S
- printf("[main] 即将发送一次通知 \ r\n");
- num++;
- /*
- 参数 1: 任务句柄
- 参数 2: 通知值
- 参数 3:
- eNoAction = 0, 无动作, 通知任务而不更新其通知值
- eSetBits, 任务的通知值与 ulValue 按位或
- eIncrement, 任务的通知值增加 1
- eSetValueWithOverwrite, 复写方式更新通知值, 不管任务是否读取该值, 都将更新通知值
- eSetValueWithoutOverwrite 如果任务已经读取了之前的值, 则设置任务的通知值
- */
- xTaskNotify(TaskH_0,num,eSetValueWithoutOverwrite);
- }
- }
小试牛刀(任务同步与自动加一效果)
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"//freertos 相关
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- // 任务句柄
- TaskHandle_t TaskH_0=NULL;
- // 任务 0 处理函数
- void Task_Run_0(){
- uint32_t Noti_vlaue=0;
- while(1){
- /*
- 参数 1: 进入时, 传递的参数取反后按位与来设定任务通知初值(ULONG_MAX 表示清零, 0 表示值不变, 其他值按规则)
- 参数 2: 退出时, 传递的参数取反后按位与来设定任务通知初值(ULONG_MAX 表示清零, 0 表示值不变, 其他值按规则)
- 参数 3: 通知值获取
- 参数 4: 等待时间
- */
- xTaskNotifyWait(0,0,&Noti_vlaue,portMAX_DELAY);
- printf("[task 0] 接收到通知, 其值为:%d\r\n",Noti_vlaue);
- }
- }
- // 主函数, 优先级为 1
- void app_main()
- {
- printf("\r\n--------------DONGIXAODONG FreeRTOS-----------------\r\n");
- // 启动任务 0, 简化
- // 函数, 名字, 字节大小, 参数, 优先级[0,16](16 最优先), 任务句柄
- BaseType_t t0res=xTaskCreate(Task_Run_0,"DONG Task_Run_0",1024*2,NULL,7,&TaskH_0);
- if(t0res==pdPASS){
- printf("任务 0 启动成功....\r\n");
- }
- while(1){
- vTaskDelay(3000 / portTICK_PERIOD_MS);// 延时 3S
- printf("[main] DONG 即将发送一次通知 \ r\n");
- /*
- 参数 1: 任务句柄
- 参数 2: 通知值
- 参数 3:
- eNoAction = 0, 无动作, 通知任务而不更新其通知值
- eSetBits, 任务的通知值与 ulValue 按位或
- eIncrement, 任务的通知值增加 1
- eSetValueWithOverwrite, 复写方式更新通知值, 不管任务是否读取该值, 都将更新通知值
- eSetValueWithoutOverwrite 如果任务已经读取了之前的值, 则设置任务的通知值
- */
- xTaskNotify(TaskH_0,0,eIncrement);
- }
- }
参考:
https://zhidao.baidu.com/question/7412988.html
官网: https://www.freertos.org/
ESP32 文档:
正点原子
来源: https://www.cnblogs.com/dongxiaodong/p/12640208.html