GCC : GNU C Compiler .顾名思义,GUN C 编译器。自从面世以后逐渐发展,现在不仅支持C语言,还支持很多语言,如C++、Ada、Java等。因此GCC得意思被定义为GUN Compiler Collection,即GNU 编译器套件。 其中GNU是一个自由软件组织。
分析C编程函数的复杂程度的计算方法。
MISRA: THE Motor Industry Software reliability Assosition 汽车工业软件可靠性联会。位于英国的也有一个跨国汽车工业协会。其成员包括了大部分欧美汽车生产商。
这个标准包括了127条C语言编码标准。代码编写遵循度越高,代码越容易维护和管理。
定义:每种支持的编译器和处理器组合被认为是一个端口。FreeRTOS 自持很多种编译器和处理器。
每个端口使用一个定时器来生成定期的滴答中断。许多端口使用其他中断来管理执行任务切换。RTOS端口所需的中断由提供的RTOS端口源文件提供服务。
线程: 运算调度的最小单位。
作用:定义一些操作,函数名称确定,只需要补全函数体。钩子函数由事件触发。函数指针指向钩子函数,调用钩子函数的过程,称为挂钩子。
作用:连接。
示例1:
#define CONN(X,Y) X##Y
##表示连接,例long n=CONN(123,456),n=123456。
示例2:如果使用osThreadDef(first,1,1,1,1),那么就生成了 os_thread_def_first
#if defined (osObjectsExternal) // object is external
#define osThreadDef(name, thread, priority, instances, stacksz) \
extern const osThreadDef_t os_thread_def_##name
#else // define the object
软实时需求: 即使违反了时间要求,也不会导致系统失效,例如按键的响应太慢会导致系统不响应按键,但是不会导致系统没用。
硬实时需求: 违反了时间要求,会导致系统的失败。例如驾驶员气囊系统,碰撞传感器响应太慢对导致很大的问题。
一个核在任何时间智能执行一个线程。核通过线程的优先级来判断当前该执行哪一个任务。硬实时需求分配较高优先级,软实时需求分配较低优先级。在FreeRTOS中,把thread 通常称为task,避免线程的概念和其他领域中的概念混淆。
适用于较为复杂的系统,任务优先级确保程序的满足处理期限。
特点:
根据需要修改其中的参数。
所有的FreeRTOS 接口中共有的文件。
提供队列和信号服务。几乎总是需要这个文件。
提供软件定时器功能。
提供事件组功能。
co-routine 协程,适用于较小的核,用的较少。官方已经不打算更新了。
低于FreeRTOS 9.0的版本必须使用heap memory manager。或者是打开了 configSUPPORT_DYNAMIC_ALLOCATION 。 使用的是heap_1 to heap_5 文件。
针对端口类型,有两个指定的数据类型定义文件,分别为 TickType_t 和 BaseType_t .
TickType_t : 滴答时间计时。根据MCU 位数定义,16或32位。
BaseType_t: 基本数据类型,通常和MCU的位数一致。
注意前缀。会指明变量的标准类型或非标准类型。以及指针类型。
注意前缀,前缀有两层,一层是返回值,一层是文件地址。
private function 前缀 是prv.
宏定义名大写字母,前缀小写字母,指明文件位置。
注意:semaphore API 信号接口几乎是用宏来定义的,但是使用函数的命名规则来写的。
内核对象: tasks queue 、semaphore and event group.
对象不是在创建时分配的,在运行时动态分配的。创建对象时分配RAM 空间,删除对象时删除RAM空间。这样的目的是减少RAM的使用空间。
使用函数: malloc() 、free(). 缺点是占用代码空间大,不适用于小型的MCU 。在FreeRTOS 中用 pvPortMalloc() 、vPortFree()代替。
函数位置 heap_1 ~ heap_5。
size_t xPortGetFreeHeapSize();
当函数被调用时,返回堆字节数量。可以用来优化堆的大小。
size_t xPortGetMinimumEverFreeHeapSize( void );
返回最小未分配的堆的字节数。作用:指示距离空间消耗完毕有多接近。
void vApplicationMallocFailedHook( void );
钩子函数,解决堆分配失败的的问题,指明接下来怎么做。
[Cortex-M users: Information on installing interrupt handers is provided in the “The application I created compiles, but does not run” FAQ]
翻译:[Cortex-M用户:“我创建的应用程序已编译但无法运行”常见问题解答中提供了有关安装中断处理程序的信息。
解决问题网址:https://www.freertos.org/FAQHelp.html
0、不退出,无限循环。
1、断点恢复执行。
2、任务切入、切出的名词:switch in \ switch out .只有任务调度器才能执行切换动作。
3、空闲任务自动创建当 scheduler 开始,保证任何时刻都有一个任务可以执行。
event-driven 时间驱动任务:只有在事件触发之后,才能让 task 进入运行状态,然后开始执行。意义是避免多优先级任务下,其他任务饿死(不执行),高优先级不进入 运行状态,调度器就不能调度他们,从而选择低优先级的、进入运行状态的任务。
Not running state:
1、 函数原型
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
任务创建失败的原因: 堆存储空间不够。
2、task 创建位置:main(); 或者另一个task 中。
使用任务函数的意义:能够有效的提取、总结函数的核心功能,将一系列操作归类为一类操作。
优先级的设置方法: 有两种,任务开始阶段创建(设定优先级参数uxPriorty),或者调用vTaskPrioritySet(); 函数。
相关函数:
void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority ); // 设置任务优先级
UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask ); // 查询任务优先级,需要事先配置 INCLUDE_uxTaskPriorityGet 为1.
注意:函数名 pxTask 设置为 null 后,返回的是该 task 的优先级。
相关参数:configMAX_PRIORITIES : 定义的最大优先级数。
优先级和数字:小数字代表低优先级,0为最低优先级。(0 - configMAX_PRIORITIES - 1).
任务调度方法: genetic method \ architecture optimized method 两种。 注意优先级数不要设置太高,否则会浪费RAM。???两种方法选择的依据?
time slicing 时间片: 针对 task 优先级相同的场景。每个任务对应一个时间片,时间片开始时进入运行状态,时间片结束时推出运行状态。
时间片原理: 使用 滴答中断 实现周期性中断。
相关参数:
1、使用软件计数延时是粗糙的做法,效率低下。
2、建议的方法:vTaskDelay(). 该函数将函数blocked指定的时间来将函数延迟。注意:事先需要将 INCLUDE_vTaskDelay 设置为1.
void vTaskDelay(TickType_t xTicksToDelay);
该函数的参数是需要延迟的时间,以Ticks 为最小单位。如果需要以ms为单位,那么就结合 pdMS_TO_TICKS() 宏来转换为ms. 如 vTaskDelay(pdMS_TO_TICKS(100)), 就制定了100ms的延时。
void vTaskDelayUntil( TickType_t * pxPreviousWakeTime, TickType_t xTimeIncrement );
该函数式精准延时函数。可以指定起始点。
意义: 1、保证时刻有一个任务处于可执行状态。2、测量备用处理能力。3、降低功耗(有一定效果)。
特点:优先级最低。
参数:
应用限制:
- idle task hook function 不能被挂起。
- 删除任务后,idle task hook function 应尽快被调用,清理资源。
void vApplicationIdleHook( void );
意义:可以删除自身或者其他任务。被删除的任务不再存在,不能再进入Running state.
相关参数: INCLUDE_vTaskDelete :设置为1 ,才可以使用删除函数。
相关函数:
注意:idle task 的清除作用。
void vTaskDelete( TaskHandle_t pxTaskToDelete ); // 参数是NUll 时,删除的是自己。
Round Robin Scheduling: 相关任务同优先级循环切换,共享处理时间。
Prioritized Pre-emptive Scheduling with Time Slicing 基于时间片的优先级调度算法:
Prioritized Pre-emptive Scheduling (without Time Slicing) 无时间片优先抢占调用: 难度大,有经验的高手使用。
Co-operative Scheduling 合作调度: 只有当前运行任务进入block状态或者使用 taskYIELD() 函数强制推迟才会切换任务。 任务不会被抢占,所以无法使用时间片。参数配置如下。
相关参数:
configUSE_PREEMPTION :设置为1 ,打开抢占
configUSE_TIME_SLICING:设置为1,打开时间片
configUSE_TICKLESS_IDLE:设置为1,关闭 tick interrupt ,节省功耗。
configIDLE_SHOULD_YIELD: 空闲 task 退让函数。设置为0,面对和 idle task 相同优先级的任务,不退让。 设置为1,退让。下图是退让的结果。
queue 提供了 task to task \ task to interrupt \ interrupt to interrupt 的通信机制。
QUEUE 的 length 和 size : length 是 items 大小。size 是 队列里可以存储的 Item 的数量。
作用:FIFO buffer.
应用手段:直接复制队列内容到另一个队列。或者使用队列的指针。
特点:
1、创建一个队列.
先创建,再使用。通过 handle 引用,类型是 QueueHandle_t。
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
2、发送队列
BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait ); // 发送数据到队首
xQueueSendToFrontFromISR(); // ISR 函数;
BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait ); // 发送数据到队尾
xQueueSendToBackFromISR(); // ISR 函数
3、接收队列
注意:有ISR版本
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );
4、查询队列
注意:有ISR版本
UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );
1、处理办法: 使用结构体指针,将数据源 和 数据 编制为结构体,实现多种数据源、数据类型的传递。
对队列限制数据来源和数据类型。
不简洁、效率不高对比单个queue 接收,所以不是必须的话不要用。
相关参数:
configUSE_QUEUE_SETS:使用队列集要打开这个使能位。
相关函数:
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ); // 创建队列
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); // queue 添加到集
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
const TickType_t xTicksToWait ); // 从队列集中选择一个queue
实现步骤
使用步骤
邮箱指代 length = 1 的 queue。一种特殊用法。
主要有两种情况:
相关函数
BaseType_t xQueueOverwrite( QueueHandle_t xQueue, const void * pvItemToQueue ); // 覆盖写queue.类似 xQueueSendToBack() ,差别在于queue full 之后,会覆盖已有的数据。
xQueueOverwriteFromISR(); // ISR 版本
BaseType_t xQueuePeek( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait ); // 从队列中读取数据,类似于xQueueReceive(),差别在于不会删除 queue 中的函数。
xQueuePeekFromISR(); // ISR版本
定义: 周期性的调用函数,执行函数被称为 callback 函数。实现依靠 FreeRTOS 核,不需要硬件定时器支持。除非callback 函数在执行,否则不会消耗时间。只能在创建的函数中运行。
相关配置:
callback函数应用要求:
函数原型
void ATimerCallback( TimerHandle_t xTimer );
period of software timer: creat – executing ,创建到执行时间。
one shot and auto-reload timer 单发和自动重装定时器: 单发只能执行一次,自动重装会周期运行。
状态: dormant 休眠 、 running 两种。
The RTOS Daemon (Timer Service) Task: callback 函数在此运行。自动创建。禁止调用API 函数的原因是会导致 deamon 函数进入BLOCKED 状态。
The Timer Command Queue: 作用是传递命令。command 传递 从timer task to daemon task ,自动创建,长度参数 configTIMER_QUEUE_LENGTH 。conmand 可以使用 time stamp 时间戳,设定传递时间。
创建步骤:
1、声明 TimerHandle_t 型变量。
2、使用函数创建定时器。
相关函数:
TimerHandle_t xTimerCreate( const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction ); // 创建 TIMER
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); // 定时器起始,有ISR 版本
相关函数:
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID ); // 设置ID,也可以创建时设定。
void *pvTimerGetTimerID( TimerHandle_t xTimer ); // 获取ID 标签。
不同来源的事件必须给出不同的处理方法。中断就是处理特殊事件的方式。task 是软件特色,和硬件无关,而中断和硬件有关。
1、画出 task 的优先级,和中断的优先级。
相关的宏
portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); // context switch
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); // 和上函数一样
注意:不同的PORT 不同的放置位置,大多数用在任何位置,小部分只能用在ISR的结尾。
部分API 函数不能再ISR中执行,可能导致任务进入blocked 状态。在ISR中使用"FromISR"版本。可以是代码更高效,相比将ISR和task结合起来的方案,减少判断条件。
高优先唤醒参数,如果在ISR中可能会发生因为优先级变化而发生的 switch ,那么用这个参数记录。不需要可以设置为NULL;
1、避免不必要的切换。
2、避免不可预测切换。
3、portability 可移植的
4、不要超过一个转换的请求。
5、避免 tick interrupt.
中断中记录原因,并且清理中断标志,需要大量操作的内容放到task 中,这样的操作被称为延迟中断处理。好处是1、减少中断处理时间。2、使用所有API函数。
1、一般来说,保证ISR尽可能短。原因如下:
没看懂
deffer interrupt processs; 大部分操作放到 task 中,记录操作放在 ISR 中。
二进制信号量用于传达事件。队列用来传达事件、传递数据。
1、二进制信号量用于“defer (推迟)” ISR 处理到task 中。
2、二进制信号量的构成:长度为1的 queue,状态始终为empty 或者 FULL,
3、“taking” 和 “give” 是信号的两种操作。
4、除了mutexes , 都可以使用xSemaphoreTake();来获取semaphore。不能用在ISR中。
相关函数:
SemaphoreHandle_t xSemaphoreCreateBinary( void ); // 创建信号
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );// 获取信号,不能用在ISR中。 一直处于BLOCKED 状态,如果没有信号量返回。
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken ); // 给予 semaphore;有ISR版本。
长度不为1的信号量。item 的数量就是 信号 count 的量。
使用场景:
相关参数:
configUSE_COUNTING_SEMAPHORES :使用计数信号量
相关函数:
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount ); // 创建计数信号
队列用来传达事件、传递数据。ISR中的特殊队列API用来ISR之间传递事件和数据。队列提供了ISR到task 传递数据的方式,但是如果数据的频率很高,那么不适合用 queue, 有更适合的DMA方式。
相关函数:
configMAX_SYSCALL_INTERRUPT_PRIORITY
configMAX_API_CALL_INTERRUPT_PRIORITY
The easiest way to use FreeRTOS is to start with one of the pre-configured demo application projects (found in the FreeRTOS/Demo directory). That way you will have the correct FreeRTOS source files included, and the correct include paths configured. Once a demo application is building and executing you can remove the demo application file, and start to add in your own application source files.
翻译:使用 demo 工程,快速开始。省的配置头文件路径什么的。
都是ARM的编译器,ARMCC支持到ARMv7 架构,ARMCLANG 可以支持到ARMv6、ARMv7、ARMv8以及以后的新处理器。
文件数量:以下源文件是必须的。
FreeRTOS/Source/tasks.c
FreeRTOS/Source/queue.c
FreeRTOS/Source/list.c
FreeRTOS/Source/portable/[compiler]/[architecture]/port.c.
FreeRTOS/Source/portable/MemMang/heap_x.c where ‘x’ is 1, 2, 3, 4 or 5.
其余文件需要什么添加什么,例如需要定时器功能,那么就添加一个timer.c文件。
1. **准备工作** 保证任务代码可以正确执行。
2. vTaskStartScheduler() 在主函数中调用这个函数,开始RTOS application.
3. 在必须的源文件基础上,选择需要的功能文件,如timer.c.
4. 头文件放到指定的文件夹中,保证路径正确。
5. 配置文件。配置文件将内核适配应用程序。因为配置文件是应用于应用程序而不是RTOS,所以它应该位于应用程序目录中,而不是RTOS内核源代码中。 堆和堆栈的大小设置,参考官方示例。
6. 中断向量。
0 middleware 的含义?
1. Middleware 下 interface 里面的选项 “CMSIS_V1” 和“CMSIS_V2”代表了什么。
2 头文件中 #define osObjectExternal 作用,除了打开
1 安装STM32F4芯片包
2 安装STM32F4芯片程序包
可能会中断多次,请耐心得多尝试几遍。
1 在"middleware"下的FREERTOS 打开CMSIS_V1,CMSIS_V2.里面的参数暂时不需要配置。等着接下来了解了参数的定义之后,再开始针对性的配置。
2 时钟配置。选择合适的时钟源和时钟频率。
3 工程配置。配置工程名称、工程目录、IDE等。工程路径中不要有中文字符,否则会报错。
4 点击页面右上角“GENERATE CODE”按钮,生成代码。会提示RTOS时钟问题,这时需要在“SYS”中把 Timebase Source 时钟基准由滴答时钟改变为定时器时钟。
宏定义和条件编译结合,实现不同代码不同配置。
这个文件不是FreeRTOS 的源文件,属于移植必须的文件。由ST 的团队开发。是内核与RTOS 的接口文件。由此来调用FreeRTOS的文件。
All definitions are prefixed with \b os to give an unique name space for CMSIS-RTOS functions.
翻译:CMSIS-RTOS 文件中函数的名称, 前缀 都是 os。
Definitions that are prefixed \b os_ are not used in the application code but local to this header file.
翻译:只在本地使用,未在应用程序中使用的,前缀都是os_
All definitions and functions that belong to a module are grouped and have a common prefix, i.e. \b osThread.
翻译:相同前缀的,属于同一模块
Definitions that are marked with CAN BE CHANGED can be adapted towards the needs of the actual CMSIS-RTOS implementation.
These definitions can be specific to the underlying RTOS kernel.
翻译:标记为 CAN BE CHANGED ,可以根据需要被改写。
Definitions that are marked with MUST REMAIN UNCHANGED cannot be altered. Otherwise the CMSIS-RTOS implementation is no longer
compliant to the standard. Note that some functions are optional and need not to be provided by every CMSIS-RTOS implementation.
翻译:标记为 MUST REMAIN UNCHANGED 的,不能修改。
Function calls from interrupt service routines
函数调用。有些函数能被中断服务函数调用,有些不可以。不能被调用的函数被调用后会报错,有些可以被多个ISR调用。可以被调用的函数如下。
- \ref osSignalSet
- \ref osSemaphoreRelease
- \ref osPoolAlloc, \ref osPoolCAlloc, \ref osPoolFree
- \ref osMessagePut, \ref osMessageGet
- \ref osMailAlloc, \ref osMailCAlloc, \ref osMailGet, \ref osMailPut, \ref osMailFree
空闲任务。
FreeRTOS referrence manual P395. 有详细介绍。
定义了一些状态。
1 函数名以FromISR结尾,才可以用在ISR中。
2 有些情况下以FromISR 结尾的函数,也不能用在ISR中。即函数优先级高于系统调用优先级。reference manual P19.
3
是啥?
是啥
是啥
是啥
aceess a resource.
意义: 实现task 对资源的控制,不被抢占。
资源访问不合理,会导致任务错误。多任务系统中,一个任务未完成对资源的访问,缺从运行状态中转换。导致资源的状态不确定,后续任务访问可能导致数据出错。
举例:写LCD。任务A要写hello World,任务B要写abort,retry,fail? 任务A的字符串写了一半被任务B给抢占了,写了字符串B的内容。导致最后显示的字符串是错误的。Hello wabort retry,fail?orld.
意义:保证重要代码完整执行。多用在代码初始化阶段,保护不能被打断的代码。
方式:是更改中断使能状态的唯一合法方式。中断场景中的函数
- taskENTER_CRITICAL(); // 普通场景,无返回值。
- taskEXIT_CRITICAL(); // 普通场景
- taskENTER_CRITICAL_FROM_ISR() ; // 中断场景函数 ,允许中断嵌套,有返回值。
- taskEXIT_CRITICAL_FROM_ISR() ; // 中断场景函数,允许中断嵌套。
示例:taskENTER_CRITICAL(); taskEXIT_CRITICAL();两个函数包围着printf( “%s”, pcString );,保证它的执行。这是一种粗糙的方法,实现原理是通过关闭中断。
定义:挂起调度器。只能保护不被 task 抢占,中断依然能够抢占。保护能力没有 basic critical 强。
函数:
void vTaskSuspendAll( void );
BaseType_t xTaskResumeAll( void );
特点:挂起调度器或恢复调度器需要花费较长时间。因此必须想好这这种方法的使用场景。
SemaphoreHandle_t xSemaphoreCreateMutex( void );
2)使用互斥信号量。在应用程序中使用互斥信号量。详情见reference manual P248.
3. (不理解):
- 必须始终返回用于互斥的信号量。
- 用于同步的信号量通常会被丢弃而不返回。
定义:高优先级等待低优先级执行完成才能执行,被称为优先级反转。属于互斥锁的陷阱。
定义:优先继承。能够让优先级反转负面影响最小化的方案。它不会修复优先级反转,只是通过确保优先级反转受到时间限制而减小优先级反转带来的负面影响。
实现原理:正在运行的、具有互斥量的、低优先级的任务,优先级提高到高优先级,优先级和尝试获取同一互斥量的任务的优先级相等(继承高优先级任务的优先级)。任务执行完成之后,优先级恢复至原来优先级。
缺点:让系统时序复杂,并且依靠它来保证正确的系统操作不是一个好的习惯。
实现方法:!!!待补充
定义:两个任务 A\B 都需要两个互斥量X\Y,而两个互斥量被两个任务各拿走一个,这时两个任务都抱着自己的互斥量,无法获得对方的互斥量进入了blocked状态。
解决办法:设计时避免这种情况的发生。
定义:一个任务自身也可能会发生死锁现象。任务多次使用同一个互斥量而不返回,就会发生死锁。为避免这种情况就设计了递归互斥。
使用方法:获取多少次地柜互斥,就要释放多少次递归互斥,见示例。P255.
使用xSemaphoreCreateMutex()创建标准互斥锁。
使用xSemaphoreCreateRecursiveMutex()创建递归互斥。这两个API函数具有相同的原型。
使用xSemaphoreTake()“获取”标准互斥锁。
使用xSemaphoreTakeRecursive()来“获取”递归互斥。这两个API函数具有相同的原型。
使用xSemaphoreGive()“释放”标准互斥锁。
使用xSemaphoreGiveRecursive()释放的递归互斥。这两个API函数具有相同的原型。
定义:如果任务1和任务2具有相同的优先级,并且任务1处于“已阻止”状态以等待任务2持有的互斥量,则当任务2“给予”互斥量时,任务1不会抢占任务2。而是,任务2将保持在“运行”状态,而任务1将仅从“阻止”状态移至“就绪”状态。
意义:解决资源管理的问题,而且不会带来 priority inversion 优先级反转以及 deadlock 死锁。
意义:同步 task ,是queue 、semaphore的综合应用。
特点:
event groups 、event flag an event bits: ‘flag’是事件发生的标志,布尔值。groups 是一系列的‘flag’,保存在一个变量中,类型为 EventBits_t,每一位代表什么,作者决定。
相关函数:
EventGroupHandle_t xEventGroupCreate( void ); // 创建事件组
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet ); // 事件组置位,有ISR 版本
注意:ISR版本不直接用在 ISR 中,用在 daemon task 中。具体原因没理解。
EventBits_t xEventGroupWaitBits( const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait ); // 等待位
意义:小数据量的传递,简单场景,方便。
Notification Value: 通知接收变量;
状态: Pending or Not-Pending;
相关参数:
configUSE_TASK_NOTIFICATIONS // 使用任务参数
1、优点
速度: 发送一个事件、字节非常快,相比其他通信中间件。
RAM: 节省RAM ,因为其他通信中间件需要都需要事先创建,占用大量RAM空间。
2、局限性 部分场景不合适。就是通信中间件的长处。比如缓存大数据量,使能多个任务,向ISR发生数据,在 blocked 中等待。
give 和 take 函数是 xTaskNotify() 和xTaskNotifyWait() 的简化版。
xTaskNotify() :可以模仿二进制信号量,可以模仿简单版本,可以模仿event groups,可以传具体数字。
xTaskNotifyWait;ulBitsToClearOnExit 和 ulBitsToClearOnEntry 的区别。
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); // 发送 notification,有ISR 版本。
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); // 在blocked 状态中等待获取 notification
轮询的坏处:浪费时间,CPU只能处理一个任务,低级别的任务无法执行。
解决方法:中断和block状态结合。使用信号量将任务置于 blocked 状态。