FreeRTOS应用
1.任务管理
1.1.任务函数
void ATaskFunction( void *pvParameters )
{
for( ;; )
{
}
vTaskDelete( NULL );
}
1.2.创建任务
portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask );
1.3.删除任务
void vTaskDelete( xTaskHandle pxTaskToDelete );
1.4.任务优先级
应用程序在文件 FreeRTOSConfig.h 中 设 定 的 编 译 时 配 置 常 量configMAX_PRIORITIES 的值,即是最多可具有的优先级数目。
低优先级号表示任务的优先级低,优先级号 0 表示最低优先级。有效的优先级号范围从 0 到(configMAX_PRIORITES – 1)
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );
UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask );
1.5.任务状态
1.运行态
2.就绪态
3.阻塞态
4.挂起态
1.6.其他
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) ;
void vTaskResume( TaskHandle_t xTaskToResume ) ;
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) ;
2.队列管理
队列可以保存有限个具有确定长度的数据单元。往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把队列中的数据拷贝删除。
2.1.创建队列
xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize );
2.2.发送队列
portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );
portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );
xQueueSend()完全等同于 xQueueSendToBack()。
portBASE_TYPE xQueueSendToFrontFromISR( xQueueHandle xQueue, void *pvItemToQueue portBASE_TYPE *pxHigherPriorityTaskWoken );
portBASE_TYPE xQueueSendToBackFromISR( xQueueHandle xQueue, void *pvItemToQueue portBASE_TYPE *pxHigherPriorityTaskWoken);
2.3.接收队列
xQueueReceive()用于从队列头部接收(读取)数据单元。接收到的单元同时会从队列中删除。
xQueuePeek()也是从队列头部接收数据单元,不同的是并不从队列中删出接收到的单元。
portBASE_TYPE xQueueReceive( xQueueHandle xQueue, const void * pvBuffer, portTickType xTicksToWait );
portBASE_TYPE xQueuePeek( xQueueHandle xQueue, const void * pvBuffer, portTickType xTicksToWait );
2.4.查询队列
uxQueueMessagesWaiting()用于查询队列中当前有效数据单元个数。
unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue );
2.5.其他
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION;
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
3.中断管理
只有以”FromISR”或”FROM_ISR”结束的 API 函数或宏才可以在中断服务例程中。
3.1.二值信号量
二值信号量可以用于延迟中断处理
void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );
需要说明的是 vSemaphoreCreateBinary()在实现上是一个宏,所以信号量变量应当直接传入
3.2.获取信号量
portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait );
3.3.给出信号量
portBASE_TYPE xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken );
3.4.计数信号量
计数信号量有2种用法:
1.事件计数
2.资源管理
xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount );
用于事件计数的计数信号量,在被创建时其计数值被初始化为 0。
用于资源管理的信号量,在创建时其计数值被初始化为可用资源总数。
3.5.中断嵌套
configKERNEL_INTERRUPT_PRIORITY 设置系统心跳时钟的中断优先级。
configMAX_SYSCALL_INTERRUPT_PRIORITY 设置中断安全版本 FreeRTOS API 可以运行的最高中断优先级。
3.6.其他
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )
#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
4.资源管理
4.1.资源一致性问题
1.访问外设
2.读改写操作
3.变量的非原子访问
4.函数重入
4.2.临界区与挂起调度器
基本临界区是指宏 taskENTER_CRITICAL()与 taskEXIT_CRITICAL()之间的代码区间
临界区是提供互斥功能的一种非常原始的实现方法。临界区的工作仅仅是简单地把中断全部关掉,或是关掉优先级在 configMAX_SYSCAL_INTERRUPT_PRIORITY 及以下的中断——依赖于具体使用的 FreeRTOS 移植。
由挂起调度器实现的临界区只可以保护一段代码区间不被其它任务打断
void vTaskSuspendAll( void );
portBASE_TYPE xTaskResumeAll( void );
4.3.互斥量
xSemaphoreHandle xSemaphoreCreateMutex( void );
优先级继承暂时地将互斥量持有者的优先级提升至所有等待此互斥量的任务所具有的最高优先级。
4.4.守护任务
守护任务是对某个资源具有唯一所有权的任务。只有守护任务才可以直接访问其守护的资源——其它任务要访问该资源只能间接地通过守护任务提供的服务。
4.5.其他
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
5.内存管理
FreeRTOS 自带有三种 pvPortMalloc()与 vPortFree()实现范例
这三个范例对应三个源文件: heap_1.c, heap_2.c, heap_3.c——这三个文件都放在目录 FreeRTOS\Source\Portable\MemMang 中。
5.1.范例1
Heap_1.c 实现了一个非常基本的 pvPortMalloc()版本,而且没有实现 vPortFree()。
这种分配方案是将 FreeRTOS 的内存堆空间看作一个简单的数组。
数组的总大小(字节为单位)在 FreeRTOSConfig.h 中由 configTOTAL_HEAP_SIZE定义。
5.2.范例2
Heap_2.c 也是使用了一个由 configTOTAL_HEAP_SIZE 定义大小的简单数组。 heap_2 采用了一个最佳匹配算法来分配内存,并且支持内存释放。
最佳匹配算法保证 pvPortMalloc()会使用最接近请求大小的空闲内存块。
Heap_2.c 并不会把相邻的空闲块合并成一个更大的内存块。
Heap_2.c 适合用于那些重复创建与删除具有相同栈空间任务的应用程序。
5.3.范例3
Heap_3.c 简单地调用了标准库函数 malloc()和 free(),但是通过暂时挂起调度器使得函数调用备线程安全特性。
此时的内存堆空间大小不受 configTOTAL_HEAP_SIZE 影响,而是由链接器配置决定。
6.错误排查
6.1.查询栈剩余空间
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask );
6.2.运行时栈侦测
FreeRTOS 包含两种运行时栈侦测机制,由 FreeRTOSConfig.h 中的配置常量configCHECK_FOR_STACK_OVERFLOW 进行控制。
栈溢出钩子函数(或称回调函数)由内核在侦测到栈溢出时调用。
void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName );
方法1:
当 configCHECK_FOR_STACK_OVERFLOW 设置为 1 时选用方法 1。
内核会在任务上下文保存后检查栈指针是否还指向有效栈空间。一旦检测到栈指针的指向已经超出任务栈的有效范围,栈溢出钩子函数就会被调用
方法2:
将 configCHECK_FOR_STACK_OVERFLOW 设为 2 就可以选用方法 2。
当创建任务时,任务栈空间中就预置了一个标记。方法 2 会检查任务栈的最后 20个字节,查看预置在这里的标记数据是否被覆盖。如果最后 20 个字节的标记数据与预设值不同,则栈溢出钩子函数就会被调用。
7.其他
7.1.延时函数
void vTaskDelay( portTickType xTicksToDelay );
void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );
常数 portTICK_RATE_MS 可以用来将以毫秒为单位的时间值转换为以心跳周期为单位的时间值。
7.2.调度算法
1.优先级抢占式调度
2.协作式调度
3.混合调度
7.3.启动调度器
void vTaskStartScheduler( void ) ;
7.4.执行调度
#define taskYIELD() portYIELD()
7.5.上下文切换
#define portSWITCH_CONTEXT() 汇编代码块
7.6.时间片
时间片的长度通过心跳中断的频率进行设定,心跳中断频率由FreeRTOSConfig.h 中的编译时配置常量 configTICK_RATE_HZ 进行配置。
FreeRTOS API 函数调用中指定的时间总是以心跳中断为单位(通常的提法为心跳”ticks”)。常量 portTICK_RATE_MS 用于将以心跳为单位的时间值转化为以毫秒为单位的时间值。
7.7.其他
TickType_t xTaskGetTickCount( void ) ;
TickType_t xTaskGetTickCountFromISR( void ) ;
8.总结
内核的接口比较多,但主要应用于任务、队列、二值信号量、计数信号量、互斥量等。
浙公网安备 33010602011771号