一、FreeRTOS内核控制控制函数:
1、请求任务切换函数:
函数原型:#define taskYIELD()
函数解析:实际上是一个宏定义的函数,调用一次会触发pendSV中断来实现任务切换;
///
2、在任务中进入临界区函数:
函数原型:#define taskENTER_CRITICAL()
函数解析:实际上是一个宏定义函数,通过屏蔽受FreeRTOS管理的中断来实现;函数支持嵌套调用;
///
3、在任务中退出临界区函数:
函数原型:#define taskEXIT_CRITICAL()
函数解析:实际上是一个宏定义函数,通过使能全部中断的方式来实现;函数支持嵌套调用;
///
4、在中断服务函数中进入临界区函数:
函数原型:#define taskENTER_CRITICAL_FROM_ISR()
函数解析:这是一个宏定义的函数,通过屏蔽中断来实现,
函数会返回中断屏蔽寄存器的历史值;函数
不允许嵌套调用;
///
5、在中断服务函数中退出临界区函数:
函数原型:#define taskEXIT_CRITICAL_FROM_ISR( x )
函数解析:这是一个宏定义函数,通过往中断屏蔽寄存器
写入x值来实现;函数不允许嵌套调用;
///
6、关闭受FreeRTOS管理的中断:
函数原型:#define taskDISABLE_INTERRUPTS()
函数解析:这是一个宏定义的函数,通过屏蔽中断来实现;
///
7、打开中断函数:
函数原型:#define taskENABLE_INTERRUPTS()
函数解析:这是一个宏定义的函数,通过取消中断屏蔽来实现;
///
8、开启任务调度器函数:
函数原型:void vTaskStartScheduler( void )
函数解析:创建空闲任务和软件定时器任务;配置启动systick和pendSV中断;并通过汇编代码启动第一个任务;
///
9、关闭任务调度器函数:
函数原型:void vTaskEndScheduler( void )
函数解析:关闭所有中断,并配置任务调度器运行状态为false;此函数只适用于X86架构的MCU,ARM结构将会触发断言;
///
10、挂起任务调度器函数:
函数原型:void vTaskSuspendAll( void )
函数解析:这里只是对任务挂起标志数做++,systick将取消任务调度;
函数允许嵌套使用;
///
11、恢复任务调度器函数:
函数原型:BaseType_t xTaskResumeAll( void )
函数解析:函数
支持嵌套调用;函数会将所有阻塞任务迁移到就绪列表中,并
返回是否需要手动切换任务;函数会自动恢复挂起期间的任务阻塞时间并恢复遗漏的systick时钟节拍;
///
12、设置系统时钟节拍计数器函数:
函数原型:void vTaskStepTick( TickType_t xTicksToJump )
函数解析:函数在configUSE_TICKLESS_IDLE=1时有效(
sysless低功耗模式);函数用于手动恢复之前丢失的systick节拍;
更新后的时间不能超过下一个任务阻塞的时间;
///
13、中断后修正系统时钟节拍函数:
函数原型:BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp )
函数解析:函数
返回是否需要手动切换任务;函数
在中断处理函数中使用,主要是用过
更新全局变量
xPendedTicks实现的,全局变量xPendedTicks用于
计数系统使用节拍在任务调度器挂起时被忽略处理的次数;
///
|
//
二、FreeRTOS的任务调试API函数(
查手册
):
1、任务状态与信息查询函数:
///
2、任务运行时间统计函数:
///
|
//
三、FreeRTOS时间管理(
核心
):
1、FreeRTOS时钟节拍:
源码中是通过一个
全局变量xTickcount来记录累计的运行节拍数;通常
在sysTick中断中实现系统节拍。在sysTick中断中调用
函数
xPortSysTickHandler来实现;在函数xPortSysTickHandler中,通过
调用函数
xTaskIncrementTick来完成
节拍计数并判断是否需要切换任务。
函数
xTaskIncrementTick
的过程解析如下:
① 判断任务调度器是否运行,否则就记录错过的节拍,后面调度器运行后会补上;
② 变量xTickcount每次加1,如果计数器溢出,则切换阻塞任务列表;
③ 根据当前节拍数,判断是否有阻塞任务超时,如果
不存在阻塞任务,则将下一次超时时间设置为最大32位的值;
④ 获取阻塞任务列表的第一个任务,进一步判断任务是否超时;如果未超时,则设置下一个阻塞任务的超时时间为目前的等待超时时间;
⑤ 超时了,将阻塞任务添加到就绪列表中;判断是否有任务可以抢占调度或进行时间片轮询;
2、FreeRTOS任务延时函数:
① 相对延时函数:void
vTaskDelay( const TickType_t xTicksToDelay )
指每次延时都是从执行 延时函数 开始,直到延时指定的
系统节拍个数结束。
实现原理的
简单理解:延时目标时间=当前tick节拍+目标延时节拍;当tick节拍 大于 目标节拍数时,时间到达。
② 绝对延时函数:BaseType_t
xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement )
指将整个
任务的运行周期
看成一个整体,适用于需要按照一定频率运行的任务。
绝对延时时间包括任务其他部分执行时间加上距离上一次调用函数的间隔时间(休眠时间)
;
具体使用方法如下
:
|
//
四、FreeRTOS队列:
1、队列的简介:
数据存储:队列通常采用 FIFO(
先进先出)的存储缓冲机制,当有新的数据被写入队列中时,永远都
是写入到队列的尾部;而从队列中读取数据时,永远都是读取队列的头部数据。但同时 FreeRTOS
的队列也支持将数据写入到队列的头部,并且还可以指定是否覆盖先前已经在队列头部的数据。
多任务访问
:队列不属于某个特定的任务,可以在任何的任务或中断中往队列中写入消息,或者从队列
中读取消息。
队列读取阻塞
:当任务从队列中读取消息时,可以
指定一个阻塞等待超时时间
;任务将从就绪列表中转移到阻塞列表,
等待队列中可用的消息出现
。如果队列中存在可读取的消息,则任务将从阻塞态切换为就绪态,并读取队列中的消息;如果
超过等待时间
,任务也会恢复为就绪态,
但不会读取队列中的消息
。
如果存在多个任务等待同一个队列,则会根据等待的优先顺序与任务优先级来判断哪个任务先读取(
先判断任务优先级
)
。
队列写入阻塞
:任务往队列写入消息时,如果队列已满,也可以阻塞等待;当队列有空闲位置时,任务将从阻塞态切换为就绪态,并向队列写入消息;如果任务超时仍旧没用空闲,则也会从阻塞态切换为就绪态,但不会写入消息。
如果存在多个任务同时等待写入同个队列,则会根据等待的先后顺序以及任务的优先级来选择就绪的任务(
先判断任务优先级
)
。
2、FreeRTOS队列的相关API函数:
队列描述结构体:
typedef
struct QueueDefinition
{
int8_t * pcHead;
/* 队列存储区域的起始地址 */
int8_t * pcWriteTo;
/* 指向队列的下一个写入消息的位置 */
/* 信号量也是由队列来实现的。
所以,但结构体对象表示队列时,使用联合体中的xQueue;
当表示信号量时,则使用xSemaphore */
union
{
QueuePointers_t xQueue;
SemaphoreData_t xSemaphore;
} u;
List_t xTasksWaitingToSend;
/* 写入消息的任务阻塞列表,按阻塞等待的优先顺序排序 */
List_t xTasksWaitingToReceive;
/* 读取消息的任务阻塞列表,按阻塞等待的优先顺序排序 */
volatile UBaseType_t uxMessagesWaiting;
/* 非空闲项目的数量 */
UBaseType_t uxLength;
/* 队列中项目的空间(可以有多少个项目) */
UBaseType_t uxItemSize;
/* 队列中每个项目的大小 */
/* 锁用于在任务因队列操作被阻塞前,防止中断或其他任务操作队列。
* 上锁期间,队列可以写入和读取消息,但不会操作队列阻塞任务列表,
* 当有消息写入时,cTxLock 加 1,当有消息被读取时,cRxLock 加 1,
* 在解锁时,会统一处理队列的阻塞任务列表
*/
volatile int8_t cRxLock;
volatile int8_t cTxLock;
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
/* 如果队列采用静态内存分配的方式,这里保证不会尝试释放内存 */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition * pxQueueSetContainer;
/* 使能队列集的标志?? */
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
//使能可视化跟踪调试后,队列用于跟踪的变量
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
/* 重新命名为Queue_t
*/
typedef xQUEUE Queue_t;
创建队列函数:
① 动态创建队列函数(
configSUPPORT_DYNAMIC_ALLOCATION=1
):#define
xQueueCreate( uxQueueLength, uxItemSize )
参数解释:
uxQueueLength:队列长度(多少个项目);
uxItemSize:队列项目的大小
返回值:
QueueHandle_t对象
② 静态创建队列函数(
configSUPPORT_STATIC_ALLOCATION=1
):#define
xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer )
参数解释:
uxQueueLength:队列长度(多少个项目);
uxItemSize:队列项目的大小;
pucQueueStorage:队列存储区域的起始地址;
pxQueueBuffer:用于确认队列描述结构体的大小(
给结构体本身提供内存),这里FreeRTOS
提供了StaticQueue_t结构体给用户使用;
返回值:
QueueHandle_t对象
队列写入消息
:
① 在
任务中往队列尾部写入消息:#define
xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
参数解释:
xQueue:目标的队列;
pvItemToQueue:要添加的消息;
xTicksToWait:消息写入阻塞等待周期时间;
返回值:dpTRUE/errQUEUE_FULL
② 在
任务中往队列头部写入消息:#define
xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
参数解释:
xQueue:目标的队列;
pvItemToQueue:要添加的消息;
xTicksToWait:消息写入阻塞等待周期时间;
返回值:dpTRUE/errQUEUE_FULL
③
覆盖队列中的消息(
仅限队列中只有1个项目的):#define
xQueueOverwrite( xQueue, pvItemToQueue )
参数解释:
xQueue:目标的队列;
pvItemToQueue:要添加的消息;
返回值:dpTRUE/errQUEUE_FULL
④ 在
中断中往队列尾部写入消息:#define
xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
参数解释:
xQueue:目标的队列;
pvItemToQueue:要添加的消息;
pxHigherPriorityTaskWoken:需要任务切换标记(用于返回是否要手动切换任务);
返回值:dpTRUE/errQUEUE_FULL
⑤ 在
中断中往队列头部写入消息:#define
xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )
参数解释:
xQueue:目标的队列;
pvItemToQueue:要添加的消息;
pxHigherPriorityTaskWoken:需要任务切换标记(用于返回是否要手动切换任务);
返回值:dpTRUE/errQUEUE_FULL
⑥ 在
中断中覆盖队列消息(
针对队列中只有1个目标的情况):#define
xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken );
参数解释:
xQueue:目标的队列;
pvItemToQueue:要添加的消息;
pxHigherPriorityTaskWoken:需要任务切换标记(用于返回是否要手动切换任务);
返回值:dpTRUE/errQUEUE_FULL
队列读取消息:
① 从队列
头部读取消息并删除队列中的项目:BaseType_t
xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait );
参数解释:
xQueue:目标的队列;
pvBuffer:读取的消息的缓存区;
xTicksToWait:读取阻塞等待周期时间;
返回值
:pdTRUE/pdFALSE
② 从队列
头部读取
消息:BaseType_t
xQueuePeek( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait )
参数解释
:
xQueue:目标队列;
pvBuffer:读取消息的缓存区;
xTicksToWait:读取阻塞等待周期时间;
返回值:
pdTRUE/pdFALSE
③ 在
中断处理函数中从队列头部读取消息并删除
对应的项目:BaseType_t
xQueueReceiveFromISR( QueueHandle_t xQueue,
void * const pvBuffer,
BaseType_t * const pxHigherPriorityTaskWoken )
参数解释:
xQueue:目标队列;
pvBuffer:读取消息的缓存区;
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值
:
pdTRUE/pdFALSE
④ 在
中断函数中从队列头部读取
消息:BaseType_t
xQueuePeekFromISR( QueueHandle_t xQueue,
void * const pvBuffer )
参数解释
:
xQueue:目标队列;
pvBuffer:读取消息的缓存区;
返回值
:
pdTRUE/pdFALSE
队列锁
:
① 队列
上锁
函数:#define
prvLockQueue( pxQueue )
作用:
② 队列
解锁函数:static void
prvUnlockQueue( Queue_t * const pxQueue )
作用:
3、FreeRTOS的队列集:
简单讲就是队列的集合;用于
自动管理多个队列,返回有消息
可读取的队列。
队列集的相关API函数:
① 创建
队列集函数(
创建一个队列,每个项目包含一个队列控制块):QueueSetHandle_t
xQueueCreateSet( const UBaseType_t uxEventQueueLength )
参数解释:
uxEventQueueLength:队列集所能容纳的最多队列;
返回值:QueueSetHandle_t结构体对象
② 将
队列添加到队列集中(
队列在被添加之前,不能有有效消息):BaseType_t
xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet )
参数解释:
xQueueOrSemaphore:目标要添加的队列;
xQueueSet:目标队列集
返回值:pdPASS/pdFALL
③ 从
队列集中移除队列(
队列在被移除之前,必须没有有效消息):BaseType_t
xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet )
参数解释
:
xQueueOrSemaphore:需要移除的队列;
xQueueSet:从哪个队列集移除;
返回值
:pdPASS/pdFALL
④ 在
任务中获取
队列集中有有效消息的队列(
本质上就是读取队列项目
):QueueSetMemberHandle_t
xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait )
参数解释:
xQueueSet:目标的队列集;
xTicksToWait:阻塞等待最大周期时间
返回值:有有效消息的队列;
⑤ 在
中断中获取
队列集中有有效消息的队列:QueueSetMemberHandle_t
xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet )
参数解释:
xQueueSet:目标的队列集;
返回值:有有效消息的队列;
|
//
五、FreeRTOS信号量:
1、二值信号量:
二值信号量实际上就是
一个
队列长度为 1 的队列
,在这种情况下,队列就
只有空和满两种情况
,这不就是二值情况吗?与互斥信号量类似,但二值信号量可能
会导致任务优先级翻转
的问题;优先级翻转问题指的是,当一个高优先级任务因获取一个被低优先级任
务获取而处于没有资源状态的二值信号量时,这个高优先级的任务将被阻塞,直到低优先级的
任务释放二值信号量,而在这之前,如果有一个优先级介于这个高优先级任务和低优先级任务
之间的任务就绪,那么这个中等优先级的任务就会抢占低优先级任务的运行,这么一来,这三
个任务中
优先级最高的任务反而要最后才运行
。
和队列一样,在获取二值信号量的时候,允许
设置一个阻塞超时时间,阻塞超时时间是当
任务获取二值信号量时,由于二值信号量处于没有资源的状态,而导致任务进入阻塞状态的最
大系统时钟节拍数。如果
多个任务同时
因获取同一个处于没有资源状态的二值信号量而
被阻塞
,
那么在二值信号量有资源的时候,这些阻塞任务中
优先级高的任务将优先获得二值信号量
的资
源并解除阻塞。
相关的API函数:
① 动态创建一个二值信号量(
也是个宏定义):#define
xSemaphoreCreateBinary()
返回值:二值信号量对象(
队列对象)
② 静态创建一个二值信号量(
也是个宏定义):#define
xSemaphoreCreateBinaryStatic( pxStaticSemaphore )
参数解释:
pxStaticSemaphore:提供信号量对象内存;
返回值:二值信号量对象
③ 在任务中获取信号量:#define
xSemaphoreTake( xSemaphore, xBlockTime )
参数解释:
xSemaphore:目标的二值信号量
xBlockTime:最大阻塞等待周期时间
返回值:pdTRUE/pdFALSE
④ 在中断中获取信号量:#define
xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )
参数解释:
xSemaphore:目标的二值信号量
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务
返回值:pdPASS/pdQUEUE_FULL
⑤ 在任务中
释放信号量(
实际上是队列的项目尾添
):#define
xSemaphoreGive( xSemaphore )
参数解释:
xSemaphore:目标的二值信号量
返回值:pdPASS/pdQUEUE_FULL
⑥ 在
中断中释放信号量:#define
xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )
参数解释:
xSemaphore:目标的二值信号量
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值:dpRASS/dpFALL
⑦ 删除目标二值信号量:#define
vSemaphoreDelete( xSemaphore )
参数解释:
xSemaphore:目标的二值信号量;
无返回值
//
2、计数型信号量:
计数型信号量与二值信号量类似,二值信号量相当于队列长度为 1 的队列,因此二值信号
量只能容纳一个资源,这也是为什么命名为二值信号量;而计数型信号量相当于队列长度大于
0 的队列
,因此计数型信号量
能够容纳多个资源
,这是在计数型
信号量被创建的时候确定
的。计数型信号量通常
适用于以下两种场景
:
事件计数
:在这种场合下,每次事件发生后,在事件处理函数中释放计数型信号量(计数型信号量的
资源数加 1),其他等待事件发生的任务获取计数型信号量(
计数型信号量的资源数减 1
),这么
一来等待事件发生的任务就可以在成功获取到计数型信号量之后执行相应的操作。在这种场合
下,计数型信号量的资源数一般在创建时设置为 0。
资源管理
:在这种场合下,计数型信号量的资源数代表着共享资源的可用数量,例如前面举例中停车
场中的空车位。一个任务想要访问共享资源,就必须先获取这个共享资源的计数型信号量,之
后在成功获取了计数型信号量之后,才可以对这个共享资源进行访问操作,当然,在使用完共
享资源后也要释放这个共享资源的计数型信号量。在这种场合下,计数型信号量的资源数一般
在创建时设置为受其管理的共享资源的最大可用数量。
相关的API函数
:
①
动态创建计数型信号量:#define
xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )
参数解释:
uxMaxCount:信号量最大的资源数量;
uxInitialCount:信号量初始化拥有的资源;
返回值:QueueHandle_t对象
②
静态创建计数型信号量:#define
xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer )
参数解释:
uxMaxCount:信号量最大的资源数量;
uxInitialCount:信号量初始化拥有的资源;
pxSemaphoreBuffer:为信号量对象提供内存;
返回值:QueueHandle_t对象
③ 获取没目标信号量
拥有的资源数:#define
uxSemaphoreGetCount( xSemaphore )
参数解释:
xSemaphore:目标的计数型信号量
返回值:当前的资源数
获取与释放信号量函数 与 二值信号量使用同一个函数!
//
3、互斥信号量:
互斥信号量其实就是一个
拥有优先级继承的二值信号量,在同步的应用中(任务与任务或
中断与任务之间的同步)二值信号量最适合,互斥信号量
适合用于那些需要互斥访问的应用中
。
在互斥访问中互斥信号量相当于一把钥匙,当任务想要访问共享资源的时候就必须先获得这把
钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访
问资源。
互斥信号量使用和二值信号量相同的API 操作函数,所以互斥信号量也可以设置阻塞时间,
不同于二值信号量的是
互斥信号量具有优先级继承的机制
。当一个互斥信号量正在被一个低优
先级的任务持有时,如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优
先级的任务就会被阻塞;不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相
同的优先级,这个过程就是优先级继承。优先级继承尽可能的减少了高优先级任务处于阻塞态
的时间,并且将“优先级翻转”的影响降到最低。
互斥信号量不能用于中断服务函数中
。
相关的API函数
:
①
动态方式创建互斥信号量:#define
xSemaphoreCreateMutex()
返回值:QueueHandle_t对象;
②
静态方式创建互斥信号量:#define
xSemaphoreCreateMutexStatic( pxMutexBuffer )
参数解释:
pxMutexBuffer:为互斥信号量提供内存空间;
返回值:QueueHandle_t对象;
其他函数接口与二值信号量共用!
//
4、递归互斥信号量:
//
|
//
六、FreeRTOS的事件标记组:
1、事件标志组的简介:
事件标志是一个用于指示事件是否发生的
布尔值,一个事件标志只有 0 或 1 两种状态,
FreeRTOS
将多个事件标志储存在一个变量类型为 EventBits_t 变量
中,这个变量就是
事件组
。事件组是一组事件标志的集合,
一个事件组就包含了一个 EventBites_t 数据类型的变量。
EventBits_t 实际上是一个
16 位或 32 位无符号的数据类型。当
configUSE_16_BIT_TICKS 配置为 0 时,EventBits_t 是一个 32 位无符号的数据类型;当
configUSE_16_BIT_TICKS 配置为 1 时,EventBits_t 是一个 16 为无符号的数据类型。FreeRTOS 将这个 EventBits_t 数据类型的变量
拆分成两部分,其中
低 24 位[23:0] (
configUSE_16_BIT_TICKS 配置位 1 时,是低 8 位[7:0])
用于存储事件标志,而高 8 位[31:24](
configUSE_16_BIT_TICKS 配置位 1 时,依然是高 8 位
[15:8]
)用作存储事件标志组的一些控制信息。
//
2、事件标志组相关的API函数:
事件标志组对象
结构体
原型:
typedef struct EventGroupDef_t
{
EventBits_t uxEventBits;
//事件标志组变量
List_t xTasksWaitingForBits;
/* 等待事件的任务列表 */
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxEventGroupNumber;
#endif
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
#endif
}
EventGroup_t;
①
动态方式创建事件标志组对象:EventGroupHandle_t
xEventGroupCreate( void )
返回值:事件标志结构体对象
②
静态方式创建事件标志组对象:EventGroupHandle_t
xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer )
参数解释:
pxEventGroupBuffer:为事件标志对象提供内存
返回值:事件标志结构体对象
③
删除事件标志组对象:void
vEventGroupDelete( EventGroupHandle_t xEventGroup )
参数解释:
xEventGroup:需要删除的事件标志对象;
④ 等待事件标志位:EventBits_t
xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
参数解释
:
xEventGroup:目标要等待的事件标志组
uxBitsToWaitFor:需要等待的具体标志位(
可以使用 逻辑或 同时等待多个中的一个
)
xClearOnExit:成功等到标志位后,是否自动清除;
xWaitForAllBits:被等待的标志位是否需要同时成立(
逻辑与
)
xTicksToWait:等待事件最长的阻塞周期时间
返回值:等待到的事件标志位/当前的事件标志位
⑤ 在
任务中设置事件标志组的标志位:EventBits_t
xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
参数解释
:
xEventGroup:目标的事件标志组
uxBitsToSet:需要设置的具体标志位(
可以用 逻辑与 同时设置多个
)
返回值
:当前标志组的实际标志位
⑥ 在
中断中设置
事件标志组的标志位:BaseType_t
xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t * pxHigherPriorityTaskWoken )
参数解释
:
xEventGroup:目标的事件标志组
uxBitsToSet:
需要设置的具体标志位(
可以用 逻辑与 同时设置多个
)
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值
:pdPASS/pdFAIL
⑦
在任务中清除事件标志组的标志位:EventBits_t
xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
参数解释
:
xEventGroup:目标的事件标志组
uxBitsToClear:指定需要清除的标志位(
可以用 逻辑与 同时设置多个
)
返回值:
清除标志位
之前的标志组
⑧ 在
中断中清除事件标志组的标志位:BaseType_t
xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
参数解释:
xEventGroup:目标的事件标志组
uxBitsToClear:指定需要清除的标志位(
可以用 逻辑与 同时设置多个
)
返回值
:
pdPASS/pdFAIL
⑨
在任务中
获取事件标志组的标志值(
实际上调用清除位,只不过不清除
):#define
xEventGroupGetBits( xEventGroup )
参数解释:
xEventGroup:目标的事件标志组
返回值:当前的标志组的值
⑩
在中断中获取事件标志组的标志值:EventBits_t
xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
参数解释:
xEventGroup:目标的事件标志组
返回值:当前的标志组的值
(11)
多任务同步等待标志位函数(
任务间的事件同步):EventBits_t
xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait )
参数解释:
xEventGroup:目标的事件标志组
uxBitsToSet:目标要设置的标志
uxBitsToWaitFor:目标要等待并清除的标志
xTicksToWait:任务最大阻塞等待的周期时间
返回值:等待到的事件标志位/当前的事件标志位
///
|
七、FreeRTOS的任务通知:
1、任务通知的简介:
在 FreeRTOS 中,每一个任务都有两个用于任务通知功能的数组,分别为
任务通知数组和
任务通知状态数组
。其中
任务通知数组
中的每一个元素都是
一个 32 位无符号类型的通知值
;而
任务通知状态数组
中的元素则
表示与之对应的任务通知的状态
。
任务通知数组中的
32 位无符号通知值,用于任务到任务或中断到任务发送通知的“媒介”;
当通知值为 0 时,表示没有任务通知;当通知值不为 0 时,表示有任务通知,并且通知值就是
通知的内容。
任务通知状态数组中的元素,用于标记任务通知数组中通知的状态,
任务通知有三种状态,
分别为:
未等待通知状态、等待被通知状态 和 等待被接收状态
;其中
未等待通知状态
为任务通知
的复位状态;当任务在没有通知的时候接收通知时,在任务阻塞等待任务通知的这段时间内,
任务所等待的任务通知就处于
等待被通知状态
;当有其他任务向任务发送通知,但任务还未接收
这一通知的这段期间内,任务通知就处于
等待被接收状态
。
任务通知功能所使用到的任务通知数组和任务通知状态数组为
任务控制块中的成员变量,因此任务通知的传输是直接传出到任务中的,不同于 通过任务的通讯对象(队列、事件标志组和信号量就属于通讯对象)这个间接的方式。
任务通知的优势:使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量
快得多;并且使用
任务通知代替队列、事件标志组或信号量,可以
节省大量的内存
,这是因为每个通讯对象在使
用之前都需要被创建,而任务通知功能中的每个通知只需要在每个任务中占用固定的 5 字节内
存(
4个字节的内容+1个字节的状态
)。
任务通知的缺点
:无法
从任务到中断
;无法广播通知多个任务;无法缓冲多个通知;无法阻塞等待通知
被接收
。
//
2、任务通知相关的API函数:
表 17.2.1 所列出的 API 函数都是对任务通知相关数组中
下标为 0 的元素进行操作;而
表17.2.2
中列出的 API 函数可以指定对任务通知相关数组中的
任意元素
进行操作。
①
在任务中
发送任务通知函数:#define
xTaskNotify( xTaskToNotify, ulValue, eAction )
参数解释:
xTaskToNotify:接收通知的目标任务句柄
ulValue:通知的值
eAction:通知的方式
关于通知方式的定义:
typedef
enum
{
eNoAction = 0,
/* 通知任务,但通知的值不更新 */
eSetBits,
/* 类似于事件标志组,按位设置 */
eIncrement,
/* 类似于计数型信号量,通知值++ */
eSetValueWithOverwrite,
/* 将通知的值直接覆盖,不管上一个值是否被接收 */
eSetValueWithoutOverwrite
/* 如果上一个通知值被接收,则赋值新值 */
}
eNotifyAction;
返回值:pdPASS/pdFAIL
②
在任务中发送任务通知,
通知值每次加1:#define
xTaskNotifyGive( xTaskToNotify )
参数解释:
xTaskToNotify:接收通知的目标任务句柄
返回值:pdPASS/pdFAIL
③
在任务中发送任务通知,记录上一次
任务历史值:#define
xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction,pulPreviousNotifyValue )
参数解释:
xTaskToNotify:接收通知的目标任务句柄
ulValue:通知的值;
eAction:通知的方式;
pulPreviousNotifyValue:用于存放旧的通知值;
返回值:pdPASS/pdFAIL
④
在中断中发送任务通知:#define
xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken )
参数解释:
xTaskToNotify:接收通知的目标任务句柄
ulValue:通知的值
eAction:通知的方式;
pxHigherPriorityTaskWoken:返回是否需要手动切换任务
返回值:pdPASS/pdFAIL
⑤
在中断中发送任务通知,记录
上一个通知的值:#define
xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken )
参数解释:
xTaskToNotify:接收通知的目标任务句柄
ulValue:通知的值
eAction:通知的方式;
pulPreviousNotifyValue:用于存放旧的通知值;
pxHigherPriorityTaskWoken:返回是否需要手动切换任务
返回值:pdPASS/pdFAIL
⑥
在中断中发送任务通知,通知的
值每次加1:#define
vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken )
参数解释:
xTaskToNotify:接收通知的目标任务句柄
pxHigherPriorityTaskWoken:返回是否需要手动切换任务
返回值:pdPASS/pdFAIL
⑦
获取任务通知的通知值,成功后
通知值-1 或 通知值清零:#define
ulTaskNotifyTake( xClearCountOnExit, xTicksToWait )
参数解释:
xClearCountOnExit:是否要清零通知值;
xTicksToWait:最大超时周期时间
返回值:具体通知值或0
⑧ 获取
指定的任务通知值的某位,此函数可以在等待前和成功等待
到任务通知通知值中的指定比特位被置一后清零指定比特位,并且还能获取等待超时后任务通
知的通知值:#define
xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait )
参数解释:
ulBitsToClearOnEntry:等待前指定清零的任务通知通知值比特位
ulBitsToClearOnExit:成功等待后指定清零的任务通知通知值比特位
pulNotificationValue:等待超时后任务通知的通知值
xTicksToWait:最大超时周期时间
返回值:pdTRUE/pdFALSE
//
|
八、FreeRTOS的软件定时器:
1、软件定时器的简介:
软件定时器是指具有定时功能的软件,FreeRTOS 提供的软件定时器允许在创建前设置一个
软件定时器定时超时时间,在软件定时器成功创建并启动后,软件定时器开始定时,当软件定
时器的定时时间达到或超过先前设置好的软件定时器定时器超时时间时,软件定时器就处于超
时状态,
此时软件定时器就会调用相应的回调函数
,一般这个回调函数的处理的事务就是需要
周期处理的事务。
FreeRTOS 提供的软件定时器还
能够根据需要设置成
单次定时器和周期定时器;当单次定时
器定时超时后,不会自动启动下一个周期的定时;而周期定时器在定时超时后,会自动地启动
下一个周期的定时。软件定时器
回调函数不允许调用导致阻塞的函数
,因为这会阻塞到对应的软件定时器任务。
FreeRTOS软件定时器服务任务
:使能了软件定时器功能后,在调用函数 vTaskStartScheduler()开启任务调度器的时候,会
创
建一个用于管理软件定时器的任务
,这个任务就叫做
软件定时器服务任务
。软件定时器服务任
务,主要负责软件定时器超时的逻辑判断、调用超时软件定时器的超时回调函数以及处理软件
定时器命令队列。
FreeRTOS 提供了许多软件定时器相关的 API 函数,
这些 API 函数,大部分都是
往定时器
的队列中写入消息(发送命令),这个队列叫做软件定时器命令队列;是提供给 FreeRTOS 中的软件定时器任务使用的,用户是不能直接访问的。
软件定时器命令队列的操作过程如下图所示:
/
2、FreeRTOSConfig.h中的相关配置:
① #define
configUSE_TIMERS:此宏用于
使能软件定时器功能,如果要使用软件定时器功能,则需要将该宏定义定义为 1;
开启软件定时器功能后,系统会系统创建软件定时器服务任务。
② #define
configTIMER_TASK_PRIORITY:此宏用于配置
软件定时器服务任务的任务优先级,当使能了软件定时器功能后,需要配置
该宏定义,此宏定义可以配置为 0~(configMAX_PRIORITY-1)的任意值。
③ #define
configTIMER_QUEUE_LENGTH:此宏用于配置
软件定时器命令队列的队列长度,当使能了软件定时器功能后,需要配置该
宏定义,若要正常使用软件定时器功能,此宏定义需定义成一个大于 0 的值。
④ #define
configTIMER_TASK_STACK_DEPTH:此宏用于
配置软件定时器服务任务的栈大小,当使能了软件定时器功能后,需要配置该宏
定义,由于所有软件定时器的定时器超时回调函数都是由软件定时器服务任务调用的,因此这
些
软件定时器超时回调函数运行时使用的都是软件定时器服务任务的栈
。
/
3、软件定时器相关的API函数:
软件定时器结构体:
typedef struct
tmrTimerControl
{
const char * pcTimerName;
/* 软件定时器对象的名字 */
ListItem_t xTimerListItem;
/* 软件定时器列表项,用于将定时器插入定时列表中 */
TickType_t xTimerPeriodInTicks;
/* 软件定时器超时时间周期 */
void * pvTimerID;
/* 共用回调函数时,用于区分不同的软件定时器的 */
TimerCallbackFunction_t pxCallbackFunction;
/* 软件定时器超时回调函数入口 */
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
#endif
uint8_t ucStatus;
/* 标记软件定时器的运行模式 */
}
xTIMER;
①
动态创建软件定时器对象:TimerHandle_t
xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const BaseType_t xAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
参数解释
:
pcTimerName:定时器的名字;
xTimerPeriodInTicks:定时器初始配置的定时周期时间;
xAutoReload:是否为周期定时器(或者是单次定时器);
pvTimerID:软件定时器的ID号(
用于多个定时器公用一个回调函数
);
pxCallbackFunction:软件定时器回调处理函数入口地址;
返回值
:TimerHandle_t句柄
②
静态创建
软件定时器对象:TimerHandle_t
xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const BaseType_t xAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t * pxTimerBuffer )
参数解释
:
pcTimerName:定时器的名字;
xTimerPeriodInTicks:定时器初始配置的定时周期时间;
xAutoReload:是否为周期定时器(或者是单次定时器);
pvTimerID:软件定时器的ID号(
用于多个定时器公用一个回调函数
);
pxCallbackFunction:软件定时器回调处理函数入口地址;
pxTimerBuffer:为软件定时器对象提供内存空间;
返回值
:TimerHandle_t句柄
③
在任务中开启软件定时器定时(
本质是发送消息命令):#define
xTimerStart( xTimer, xTicksToWait )
参数解释:
xTimer:需要启动的软件定时器对象;
xTicksToWait:发送命令阻塞等待时间;
返回值:pdPASS/pdFAIL
④
在中断中开启软件定时器定时(
本质也是发送消息命令):#define
xTimerStartFromISR( xTimer,pxHigherPriorityTaskWoken )
参数解释:
xTimer:需要启动的软件定时器对象;
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值:pdPASS/pdFAIL
⑤
在任务中停止软件定时器:#define
xTimerStop( xTimer, xTicksToWait )
参数解释:
xTimer:需要停止的软件定时器对象;
xTicksToWait:发送命令阻塞等待时间;
返回值:pdPASS/pdFAIL
⑥
在中断中停止软件定时器:#define
xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken )
参数解释:
xTimer:需要停止的软件定时器对象
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值:pdPASS/pdFAIL
⑦
在任务中复位软件定时器:#define
xTimerReset( xTimer, xTicksToWait )
参数解释:
xTimer:需要复位的软件定时器对象;
xTicksToWait:发送命令阻塞等待时间;
返回值:pdPASS/pdFAIL
⑧
在中断中复位软件定时器:#define
xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken )
参数解释:
xTimer:需要复位的软件定时器对象;
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值:pdPASS/pdFAIL
⑨
在任务中更改软件定时器的超时时间:#define
xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait )
参数解释:
xTimer:需要更改超时时间的软件定时器对象;
xNewPeriod:新的超时时间;
xTicksToWait:发送命令阻塞等待时间;
返回值:pdPASS/pdFAIL
⑩
在中断中更改软件定时器的超时时间:#define
xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken )
参数解释:
xTimer:需要更改超时时间的软件定时器对象;
xNewPeriod:新的超时时间;
pxHigherPriorityTaskWoken:用于返回是否需要手动切换任务;
返回值:pdPASS/pdFAIL
(11) 删除目标的软件定时器:#define
xTimerDelete( xTimer, xTicksToWait )
参数解释:
xTimer:需要删除的目标软件定时器对象;
xTicksToWait:发送命令阻塞等待时间;
返回值:pdPASS/pdFAIL
/
|