FreeRTOS API快速检索(宏定义以及常用函数)


layout: post
title: “实际使用”
date: 2023-11-22 15:39:08 +0800
tags: FreeRTOS


FreeRTOS API快速检索

宏定义

任务

#define configUSE_PREEMPTION        1

配置内核调度方式 1为抢占式 0为合作式(时间片)

#define configUSE_IDLE_HOOK            0

是否使用空闲函数的钩子函数

#define configUSE_TICK_HOOK            0

是否使用TICK滴答任务钩子函数

#define configMAX_PRIORITIES        ( 5 )

任务优先级

#define configMINIMAL_STACK_SIZE    ( ( unsigned short ) 128 )

设置任务的最小的栈的大小, 实际上好像只用在了空闲任务

#define configMAX_TASK_NAME_LEN        ( 16 )

任务的名字的长度

#define configUSE_CO_ROUTINES         0

配置是否使能合作式调度相关函数

#define configIDLE_SHOULD_YIELD        1

此参数用于使能与空闲任务同优先级的任务,只有满足以下两个条件时,此参数才有效果:
1.使能抢占式调度器。
2.有创建与空闲任务同优先级的任务。
配置为1,就可以使能此特性了,实际应用中不建议用户使用此功能,将其配置为0即可

#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

定义可供用户使用的最大的合作式任务优先级数

#define INCLUDE_vTaskPrioritySet        1       //设置任务优先级
#define INCLUDE_uxTaskPriorityGet        1        //获取任务优先级
#define INCLUDE_vTaskDelete                1        //删除任务
#define INCLUDE_vTaskCleanUpResources    0        //清理任务资源
#define INCLUDE_vTaskSuspend            1        //挂起任务
#define INCLUDE_vTaskDelayUntil            1        //延时直至
#define INCLUDE_vTaskDelay                1        //延时

是否包含这些函数

内存

#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )

FreeRTOS可以使用的堆的大小

时钟

#define configCPU_CLOCK_HZ            ( ( unsigned long ) 72000000 )  

//配置CPU主频(HZ)

#define configTICK_RATE_HZ            ( ( TickType_t ) 1000 )

配置系统时钟节拍数(HZ

其他

#define configUSE_PORT_OPTIMISED_TASK_SELECTION  1

设置使用硬件优化

#define configUSE_TRACE_FACILITY    0

可视化跟踪调试

#define configUSE_16_BIT_TICKS        0

使用16位的时钟计数

中断

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

设置FreeRTOS可以使用中断的最大优先级

#define configPRIO_BITS 4

支持的优先级的位数

#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

支持的实际的寄存器里面的值

#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

内核的中断的优先级的个数

时钟

#define configUSE_TIMERS 1

使用时钟之后需要定义以下的宏定义

#define configTIMER_TASK_PRIORITY				 1
#define configTIMER_QUEUE_LENGTH				 5
#define configTIMER_TASK_STACK_DEPTH			 100

设置以后会初始化一个时钟相关的任务

互斥锁

#define configUSE_RECURSIVE_MUTEXES 1

支持递归互斥锁

任务

创建任务

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
                       const char * const pcName,
                       const configSTACK_DEPTH_TYPE usStackDepth,
                       void * const pvParameters,
                       UBaseType_t uxPriority,
                       TaskHandle_t * const pxCreatedTask )

参数一: 要使用的函数 typedef void (*TaskFunction_t)( void * );

参数二: 函数的名字, 最大16个字符

参数三: 栈的大小

参数四: 传进去的参数

参数五: 优先级, configMAX_PRIORITIES设置最大优先级, 优先级数字越大优先级越高

参数六: 任务控制句柄

开启任务

void vTaskStartScheduler( void )

直接调用, 里面会创建一个空闲任务(使用时总的话会初始化一个时钟任务), 初始化一下时钟的参数

还会在这里面设置可以使用FromIRQ的中断的最大优先级, 时钟等设置

任务状态的改变

  • 任务挂起
void vTaskSuspend( TaskHandle_t xTaskToSuspend )

参数就是想要挂起的任务句柄, 为NULL的时候就是当前的任务

  • 挂起所有
void vTaskSuspendAll( void)

主要就是设置一个标志位uxSchedulerSuspended,

  • 恢复挂起
void vTaskResume( TaskHandle_t xTaskToResume )
  • 恢复挂起从中断
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )

无论通过调用一次或多次vTaskSuspend()函数而被挂起的任务,也只需调用一次xTaskResumeFromISR()函数即可解挂

返回的值进行表明是不是需要任务切换, 当任务恢复成功并且需要任务切换的话则重置为pdTRUE

使用portYIELD_FROM_ISR()进行上下文切换

通常被认为是一个危险的函数,因为它的调用并非是固定的,中断可能随时来来临。所以,xTaskResumeFromISR()不能用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失

  • 恢复所有的
BaseType_t xTaskResumeAll( void)

任务删除

void vTaskDelete( TaskHandle_t xTaskToDelete )

删除任务, NULL删除当前任务

这时候删除的任务, 会在空闲任务里面的prvCheckTasksWaitingTermination(), 释放内存

延时函数

void vTaskDelay( constTickType_t xTicksToDelay )

阻塞延时, 经过一定时间以后接触阻塞, 并不适用与周期性执行任务的场合

其它任务和中断活动,也会影响到vTaskDelay()的调用

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

绝对延时函数vTaskDelayUntil(),这个绝对延时常用于较精确的周期运行任务

适用于周期性执行的任务。当(*pxPreviousWakeTime + xTimeIncrement)时间到达后,vTaskDelayUntil()函数立刻返回,如果任务是最高优先级的,那么任务会立马解除阻塞

参数:

指针,指向一个变量,该变量保存任务最后一次解除阻塞的的时刻。第一次使用时,该变量必须初始化为当前时间,之后这个变量会在vTaskDelayUntil()函数内自动更新。

周期循环时间: 当时间等于(*pxPreviousWakeTime + xTimeIncrement)时,任务解除阻塞

可以使用函数pdMS_TO_TICKS(1000)获取参数二, xTaskGetTickCount()获取参数一

消息队列Queue

用于任务之间的信息传递, 没有任务的时候, 会进入阻塞, 支持FIFO和LIFO, 消息可以设置大小以及个数, 传递的时候使用的是copy

创建

#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
//相当于
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,UBaseType_t uxItemSize );

uxQueueLength队列能够存储的最大消息单元数目,即队列长度。

uxItemSize队列中消息单元的大小,以字节为单位。

如果创建成功则返回一个队列句柄,用于访问创建的队列。如果创建不成功则返回NULL

QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,
                                 UBaseType_t uxItemSize,
                                 uint8_t *pucQueueStorageBuffer,
                                 StaticQueue_t *pxQueueBuffer );

uxQueueLength队列能够存储的最大单元数目,即队列深度。

uxItemSize队列中数据单元的长度,以字节为单位。

pucQueueStorageBuffer指针,指向一个uint8_t类型的数组,数组的大小至少有uxQueueLength*uxItemSize个字节。当uxItemSize为0时,pucQueueStorageBuffer可以为NULL。

pxQueueBuffer指针,指向StaticQueue_t类型的变量,该变量用于存储队列

如果创建成功则返回一个队列句柄,用于访问创建的队列。如果创建不成功则返回NULL,可能原因是创建队列需要的RAM无法分配成功

删除

void vQueueDelete( QueueHandle_t xQueue )

如果删除消息队列时,有任务正在等待消息,则不应该进行删除操作

官方说的是不允许进行删除操作,但是源码并没有禁止删除的操作,使用的时候注意一下就行了

发送消息

#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,而不是以引用的形式。该函数绝对不能在中断服务程序里面被调用,中断中必须使用带有中断保护功能的xQueueSendFromISR()来代替

xQueue队列句柄。

pvItemToQueue指针,指向要发送到队列尾部的队列消息。

xTicksToWait队列满时,等待队列空闲的最大超时时间。如果队列满并且xTicksToWait被设置成0,函数立刻返回。超时时间的单位为系统节拍周期,常量portTICK_PERIOD_MS用于辅助计算真实的时间,单位为ms。如果INCLUDE_vTaskSuspend设置成1,并且指定延时为portMAX_DELAY将导致任务挂起(没有超时)。

消息发送成功成功返回pdTRUE,否则返回errQUEUE_FULL。

#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )

#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )

xQueueSendToBackFromISR等同于xQueueSendFromISR()。

xQueue队列句柄。

pvItemToQueue指针,指向要发送到队列尾部的消息。

pxHigherPriorityTaskWoken如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,则将*pxHigherPriorityTaskWoken设置成pdTRUE,然后在中断退出前需要进行一次上下文切换,去执行被唤醒的优先级更高的任务。从FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken作为一个可选参数,可以设置为NULL。

消息发送成功返回pdTRUE,否则返回errQUEUE_FULL

#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )

xQueue队列句柄。

pvItemToQueue指针,指向要发送到队首的消息。

xTicksToWait队列满时,等待队列空闲的最大超时时间。如果队列满并且xTicksToWait被设置成0,函数立刻返回。超时时间的单位为系统节拍周期,常量portTICK_PERIOD_MS用于辅助计算真实的时间,单位为ms。如果INCLUDE_vTaskSuspend设置成1,并且指定延时为portMAX_DELAY将导致任务无限阻塞(没有超时)。

BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )

实际上调用的函数

第一个参数: 一个队列

第二个参数: 要写入的值

第三个参数: 等待的时间

第四个参数: queueSEND_TO_BACK, queueSEND_TO_FRONT

BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )

第一个参数: 一个队列

第二个参数: 要写入的值

第三个参数:是否要

第四个参数: queueSEND_TO_BACK, queueSEND_TO_FRONT

接受消息

BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );

xQueue队列句柄。pvBuffer指针,指向接收到要保存的数据。

xTicksToWait队列空时,阻塞超时的最大时间。如果该参数设置为0,函数立刻返回。超时时间的单位为系统节拍周期,常量

portTICK_PERIOD_MS用于辅助计算真实的时间,单位为ms。如果INCLUDE_vTaskSuspend设置成1,并且指定延时为portMAX_DELAY将导致任务无限阻塞(没有超时)

队列项接收成功返回pdTRUE,否则返回pdFALSE

BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )

偷看函数, 不会从队列里面去除数据

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void *pvBuffer,BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void *pvBuffer);

中断中的函数

BaseType_t xQueueGenericReceive(
QueueHandle_t xQueue,
void*pvBuffer
TickType_t xTicksToWait
BaseType_t xJustPeek)

多了一个函数参数xJustPeek当为pdTRUE的时候就不用删除,也就是说再调用函数xQueueReceive()获取到的队列项是一样的。当为pdFALSE的时候就会删除掉这个队列项。

这个函数没找到

信号量Semaphore

一个非负的整数, 获取的任务减一, 为0的时候获取就阻塞

通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数

**二值信号量: ** 互斥, 但是优先级不继承, 在信号量中,由于已经不存在可用的信号量,任务递归获取信号量时会发生主动挂起任务最终形成死锁。

**互斥信号量: **有优先级继承的二值信号量

**递归信号量: **获取到信号量的任务可以重复获取信号量, 获取几次就需要释放几次

信号量API函数实际上都是宏,它使用现有的队列机制,这些宏定义在semphr.h文件中,如果使用信号量或者互斥量,需要包含semphr.h头文件

二值信号量

创建
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

实际上就是一个长度为1, 每一个信息大小是1的队列结构体

计数信号量

创建
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

uxMaxCount计数信号量的最大值,当达到这个值的时候,信号量不能再被释放。

uxInitialCount创建计数信号量的初始值。

通用

删除
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

xSemaphore信号量句柄。

不会取消任务的阻塞, 不建议使用

释放
#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )	xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )

获取

#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )

xSemaphore信号量句柄。

xBlockTime等待信号量可用的最大超时时间,单位为tick(即系统节拍周期)。如果宏INCLUDE_vTaskSuspend定义为1且形参xTicksToWait设置为portMAX_DELAY ,则任务将一直阻塞在该信号量上(即没有超时时间)。

获取成功则返回pdTRUE,在指定的超时时间中没有获取成功则返回errQUEUE_EMPTY。

#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )	xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )

互斥量

特殊的二值信号量, 它支持互斥量所有权、递归访问以及防止优先级翻转的特性

持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问,也就是递归互斥量的特性

另外需要注意的是互斥量不能在中断服务函数中使用,因为其特有的优先级继承机制只在任务起作用,在中断的上下文环境毫无意义。

创建(普通)

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) )

创建并且初始化uxQueueType

创建(递归)

#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

删除

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

获取

#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )

获取(递归)

#define xSemaphoreTakeRecursive( xMutex, xBlockTime )	xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

释放

#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

释放(递归)

#define xSemaphoreGiveRecursive( xMutex )	xQueueGiveMutexRecursive( ( xMutex ) )

事件

可以实现任务之间的信息传递, 可以一对多, 多对多, 只可以用于同步, 不可以传递数据

可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。

事件组存储在一个EventBits_t类型的变量中, 有24个位用来实现事件标志组。每一位代表一个事件,任务通过“逻辑与”或“逻辑或”与一个或多个事件建立关联,形成一个事件组。

获取

EventGroupHandle_t xEventGroupCreate( void )
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )

删除

void vEventGroupDelete( EventGroupHandle_t xEventGroup )

置位

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )

参数是要设置的那几个位, 用的是或的方式

#define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken )

xEventGroupSetBitsFromISR()给FreeRTOS的守护任务发送一个消息,让置位事件组的操作在守护任务里面完成

因此FreeRTOS的守护任务与其他任务一样,都是系统调度器根据其优先级进行任务调度的,但守护任务的优先级必须比任何任务的优先级都要高,保证在需要的时候能立即切换任务从而达到快速处理的目的

必须把configUSE_TIMERS 和INCLUDE_xTimerPendFunctionCall这些宏在FreeRTOSConfig.h中都定义为1,并且把FreeRTOS/source/event_groups.c这个C文件添加到工程中编译。

等待标志位

EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToWaitFor,
                                const BaseType_t xClearOnExit,
                                const BaseType_t xWaitForAllBits,
                                TickType_t xTicksToWait );

xEventGroup事件句柄。

uxBitsToWaitFor一个按位或的值,指定需要等待事件组中的哪些位置1。如果需要等待bit 0 and/or bit 2那么uxBitsToWaitFor配置为0x05(0101b)。如果需要等待bits 0 and/or bit 1 and/or bit 2那么uxBitsToWaitFor配置为0x07(0111b)。

xClearOnExit: pdTRUE:当xEventGroupWaitBits()等待到满足任务唤醒的事件时,系统将清除由形参uxBitsToWaitFor指定的事件标志位。pdFALSE:不会清除由形参uxBitsToWaitFor指定的事件标志位。

xWaitForAllBits: pdTRUE:当形参uxBitsToWaitFor指定的位都置位的时候,xEventGroupWaitBits()才满足任务唤醒的条件,这也是“逻辑与”等待事件,并且在没有超时的情况下返回对应的事件标志位的值。pdFALSE:当形参uxBitsToWaitFor指定的位有其中任意一个置位的时候,这也是常说的“逻辑或”等待事件,在没有超时的情况下函数返回对应的事件标志位的值。

xTicksToWait最大超时时间,单位为系统节拍周期,常量portTICK_PERIOD_MS用于辅助把时间转换成MS。

清除事件标志位

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )

这个任务实际上就是会把清除放在一个高优先级的任务里面

获取当前的事件

#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )

软件定时器

而使用软件定时器时,需要我们在创建软件定时器时指定时间到达后要调用的函数

FreeRTOS提供的软件定时器支持单次模式和周期模式,单次模式和周期模式的定时时间到之后都会调用软件定时器的回调函数

创建时钟

TimerHandle_t xTimerCreate(	const char * const pcTimerName,	
                           const TickType_t xTimerPeriodInTicks,
                           const UBaseType_t uxAutoReload,
                           void * const pvTimerID,
                           TimerCallbackFunction_t pxCallbackFunction )

pcTimerName: 名字

xTimerPeriodInTicks: 记录周期

uxAutoReload: 设置为pdTRUE,那么软件定时器的工作模式就是周期模式,一直会以用户指定的xTimerPeriod周期去执行回调函数。如果uxAutoReload 设置为pdFALSE,那么软件定时器就在用户指定的xTimerPeriod周期下运行一次后就进入休眠态。

软件定时器ID,数字形式

回调函数

启动

#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )

实际上是给时钟的处理函数发送一个命令

#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ),( pxHigherPriorityTaskWoken ), 0U )

时钟停止

#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U )

时钟删除

#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) )

任务通知

每个任务都有一个32位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件组,也可以替代长度为1的队列

  • 发送通知给任务,如果有通知未读,不覆盖通知值。
  • 发送通知给任务,直接覆盖通知值。
  • 发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。
  • 发送通知给任务,递增通知值,可以当做计数信号量使用。

缺点

  • 只能有一个任务接收通知消息,因为必须指定接收通知的任务。。
  • 只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会因为发送失败而进入阻塞态。

可以使用的模式

  • eNoAction: 对象任务接收任务通知,但是任务自身的任务通知值不更新,即形参ulValue没有用。
  • eSetBits: 对象任务接收任务通知,同时任务自身的任务通知值与ulValue按位或。如果ulValue设置为0x01,那么任务的通知值的位0将被置为1。同样的如果ulValue设置为0x04,那么任务的通知值的位2将被置为1。在这种方式下,任务通知可以看成是事件标志的一种轻量型的实现,速度更快。
  • eIncrement: 对象任务接收任务通知,任务自身的任务通知值加1,即形参ulValue没有用。这个时候调用xTaskNotify()等同于调用xTaskNotifyGive()。
  • eSetValueWithOverwrite对象任务接收任务通知,且任务自身的任务通知值会无条件的被设置为ulValue。在这种方式下,任务通知可以看成是函数xQueueOverwrite()的一种轻量型的实现,速度更快。
  • eSetValueWithoutOverwrite: 对象任务接收任务通知,且对象任务没有通知值,那么通知值就会被设置为ulValue。

发送一个任务通知

BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )

xTaskToNotify: 通知的任务

ulValue: 要发送的值

eAction: 一个枚举类型, 指明更新的方式

pulPreviousNotificationValue: 返回原来的值

BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken )

多了一个需要进行切换的标志位

使用(发送)

xTaskNotifyGive()#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )

即向一个任务发送通知,并将对方的任务通知值加1。

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken )

中断中使用

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL )

xTaskToNotify需要接收通知的任务句柄。ulValue用于更新接收任务通知的任务通知值,具体如何更新由形参eAction决定。eAction任务通知值更新方式

实际上就是一个不需要返回值的xTaskGenericNotify

#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) )

同上

#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )

这个的使用就是xTaskGenericNotify

使用(接收)

获取任务通知函数只能用在任务中,没有带中断保护版本

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )

使得值减一

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )

ulBitsToClearOnEntry: ulBitsToClearOnEntry表示在使用通知之前,将任务通知值的哪些位 清0,实现 过程就是将任务的通知值与参数ulBitsToClearOnEntry的按位取反值按位与操作。

ulBitsToClearOnExitulBitsToClearOnExit表示在函数xTaskNotifyWait()退出前,决定任务接收到的通知值的哪些位会被清0,实现过程就是将任务的通知值与参数ulBitsToClearOnExit的按位取反值按位与操作。在清0前,接收到的任务通知值会先被保存到形参*pulNotificationValue中。

pulNotificationValue用于保存接收到的任务通知值。如果接收到的任务通知不需要使用, 则设置 为NULL即可。这个通知值在参 数ulBitsToClearOnExit起 作 用 前 将 通 知 值 拷 贝 到*pulNotificationValue中。

xTicksToWait等待超时时间,单位为系统节拍周期。宏pdMS_TO_TICKS用于将单位毫秒转化为系统节拍数。

内存管理

嵌入式时实时操作系统的需求是

  1. 实时性, 分配使用的时间是确定的
  2. 需要的内存比较小
  3. 操控的内存不会碎片化

实现的接口

void*pvPortMalloc( size_txSize ); //内存申请函数
voidvPortFree( void*pv ); //内存释放函数
voidvPortInitialiseBlocks( void); //初始化内存堆函数
size_txPortGetFreeHeapSize( void); //获取当前未分配的内存堆大小
size_txPortGetMinimumEverFreeHeapSize( void); //获取未分配的内存堆历史最小值

不同的实现

heap_1, heap_2, heap_4使用的方案是有一个大的数组, heap_3对C库的malloc和free进行封装, heap_5允许用户使用多个不连续的内存, 每个内存堆的起始地址和大小由用户定义

heap_1

它只能申请内存而不能进行内存释放,并且申请内存的时间是一个常量,这样子对于要求安全的嵌入式设备来说是最好的,因为不允许内存释放,就不会产生内存碎片而导致系统崩溃

heap_2

可以进行申请和释放, 返回的是大小最接近的一个内存的位置, 但是不能进行相邻内存的合并, 适用于分配的内存大小比较接近的情况

heap_3

只是简单的封装了标准C库中的malloc()和free()函数

重新封装后的malloc()和free()函数具有保护功能,采用的封装方式是操作内存前挂起调度器、完成后再恢复调度器

使用heap_3.c方案时,FreeRTOSConfig.h文 件 中 的configTOTAL_HEAP_SIZE宏定义不起作用

heap_4

heap_4.c方案与heap_2.c方案一样都采用最佳匹配算法来实现动态的内存分配,但是不一样的是heap_4.c方案还包含了一种合并算法,能把相邻的空闲的内存块合并成一个更大的块,这样可以减少内存碎片

移植层中可以直接使用pvPortMalloc()和vPortFree()函数来分配和释放内存的代码

heap_5

在4的基础上加了一个可以使用多块内存

通过调用vPortDefineHeapRegions()函数来实现系统管理的内存初始化

void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
typedef struct HeapRegion
{
    uint8_t * pucStartAddress;
    size_t xSizeInBytes;
} HeapRegion_t;

用户需要指定每个内存堆区域的起始地址和内存堆大小、将它们放在一个HeapRegion_t结构体类型数组中,这个数组必须用一个NULL指针和0作为结尾,起始地址必须从小到大排列。

中断管理

用户可以定义中断的优先级, 在比这个优先级高的中断函数里面不可以使用FreeRTOS的API函数

CPU使用率统计

会使用一个频率很高的时钟进行统计, 这个记录有一个毛病, 没有做时钟溢出的处理, 一旦溢出就会导致出问题

需要配置两个宏定义

//启动运行时间统计
#define configGENERATE_RUN_TIME_STATS 1 
//启用一个可视化的跟踪
#define configUSE_TRACE_FACILITY		1
//一个恢复时钟为0的宏定义, 这一个宏实际的用途的初始化
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (CPU_RunTime = 0ul)
//一个获取时钟的宏
#define portGET_RUN_TIME_COUNTER_VALUE()             CPU_RunTime
  • portGET_RUN_TIME_COUNTER_VALUE():直接返回时钟值
  • portALT_GET_RUN_TIME_COUNTER_VALUE(Time):设置Time变量等于时钟值

需要实现其中一个, 用于获取时钟

void vTaskList( char * pcWriteBuffer );  //获取任务运行时间信息
printf("---------------------------------------------\r\n");
printf("任务名任务状态优先级剩余栈任务序号\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
void vTaskGetRunTimeStats( char *pcWriteBuffer );
printf("任务名运行计数使用率\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n\n");

使用的时候出现的问题

中断开启时间

FreeRTOS的中断里面如果使用FromIRQ函数, 需要在开启任务以后

中断优先级

设置的中断如果使用FromIRQ函数需要优先级比较低

你可能感兴趣的:(FreRTOS笔记,stm32,单片机,mcu,笔记)