回顾下之前的章节:我们在一个简单的定时器OS基础上实现了cortex-M系列架构的兼容,并基于单片机的基本资源实现了很多实例。
从这个章节开始,我们把FreeRTOS移植进来,同时还考虑兼容性。
上一节我们介绍了移植FreeRTOS的方法,并基于此实现一个最基本的例子:串口定时打印数据。
这一节我们分析下FreeRTOS的任务管理。
开发板是GD32的开发板。
关键字:FreeRTOS,STM32,GD32,任务管理
关于FreeRTOS的官方文档,里面实际上也讲的很详细,参见:FreeRTOS官方文档[1]
如果非要给任务下个定义的话,在嵌入式系统中,应该叫做具象业务的具象表达(独家定义,如有雷同,对方是假货)。
比如:采样是具象业务,对应的具象表达就是sample()。
任务需要占用一定的资源,比如内存,CPU。
不同任务都想要执行,那么存在资源的冲突问题。
任务需要解决资源和资源冲突的问题。
写过单片机程序的同学应该都写过裸机程序,官方叫法叫前后台系统。
具体做法就是:
这种做法的缺点也很明显,就是一大锅饭,大家一起等着,到点了才能领饭,管你是不是老弱病残孕。
在FreeRTOS中,任务被赋予了优先级,高优先级的任务可优先运行;当然,它占用ram资源多一些。
这个跟软件优化有点像,要么是用时间换空间,要么是空间换时间。
FreeRTOS的任务已经具备了进程的特性,类似于Linux中没有线程的进程。
void ATaskFunction( void *pvParameters )
{
for( ;; )
{
/* The code to implement the task functionality will go here. */
}
/* 如果要删除任务,调用vTaskDelete函数 */
vTaskDelete( NULL );
}
任务由xTaskCreate函数创建(详细可查看手册的3.4 Creating Tasks
):
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
typedef void (*TaskFunction_t)( void * )
。configMAX_PRIORITIES
指定。函数比较简单。
void vTaskDelete( TaskHandle_t pxTaskToDelete );
注:使用本函数需要在FreeRTOSConfig.h
中设置INCLUDE_vTaskDelete
宏为1
void vTaskStartScheduler( void );
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
注:使用本函数需要在FreeRTOSConfig.h
中设置INCLUDE_vTaskSuspend
宏为1
void vTaskSuspendAll( void );
void vTaskResume( TaskHandle_t xTaskToResume );
注:使用本函数需要在FreeRTOSConfig.h
中设置INCLUDE_vTaskSuspend
宏为1
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );
注:使用本函数需要在FreeRTOSConfig.h
中设置INCLUDE_vTaskSuspend
和INCLUDE_xTaskResumeFromISR
宏为1
BaseType_t xTaskResumeAll( void );
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );
注:使用本函数需要在FreeRTOSConfig.h
中设置INCLUDE_uxTaskPriorityGet
宏为1
void vTaskPrioritySet( TaskHandle_txTask, UBaseType_tuxNewPriority );
注:使用本函数需要在FreeRTOSConfig.h
中设置INCLUDE_vTaskPrioritySet
宏为1
3.8 The Idle Task and the Idle Task Hook There must always be at least one task that can enter the Running state1. To ensure this is the case, an Idle task is automatically created by the scheduler when vTaskStartScheduler() is called. The idle task has the lowest possible priority (priority zero), to ensure it never prevents a higher priority application task from entering the Running state—although there is nothing to prevent application designers creating tasks at, and therefore sharing, the idle task priority, if desired. Running at the lowest priority ensures the Idle task is transitioned out of the Running state as soon as a higher priority task enters the Ready state. Note: If an application uses the vTaskDelete() API function then it is essential that the Idle task is not starved of processing time. This is because the Idle task is responsible for cleaning up kernel resources after a task has been deleted.
总结一下:
空闲任务的堆栈大小由configMINIMAL_STACK_SIZE
指定。
Hook一般翻译为钩子,在软件设计中,通常是一个函数指针。
空闲任务Hook的函数原型如下:
void vApplicationIdleHook( void );
如果需要使用它,需要在FreeRTOSConfig.h
中设置configUSE_IDLE_HOOK
宏的值为1。
运行时,它将在每一个空闲任务周期被调用一次。
由于空闲任务钩子的特殊性,vApplicationIdleHook中不可以调用可能引起空闲任务阻塞的API函数(比如vTaskDelay())。
Common uses for the Idle task hook include: Executing low priority, background, or continuous processing functionality. Measuring the amount of spare processing capacity. (The idle task will run only when all higher priority application tasks have no work to perform; so measuring the amount of processing time allocated to the idle task provides a clear indication of how much processing time is spare.) Placing the processor into a low power mode, providing an easy and automatic method of saving power whenever there is no application processing to be performed (although the power saving that can be achieved using this method is less than can be achieved by using the tick-less idle mode described in Chapter 10, Low Power Support).
通常,使用这个空闲钩子函数来测试性能或者设置CPU进入低功耗模式。
当系统运行异常时,首先应该要想到是否任务堆栈溢出
比如进入HardFault_Handler查不到错误信息
或者
正常的数据被修改
FreeRTOS可以通过配置configCHECK_FOR_STACK_OVERFLOW
为1或2来检查是否溢出。
configCHECK_FOR_STACK_OVERFLOW = 2
相对configCHECK_FOR_STACK_OVERFLOW = 1
而言,会在创建任务时把堆栈初始化为0xA5:
#define tskSTACK_FILL_BYTE ( 0xa5U )
建议调试时,设置configCHECK_FOR_STACK_OVERFLOW为2。
当然,当堆栈溢出时,可能已经造成了内存错误从而无法检查,所以如果系统进入了HardFault_Handler却查不到错误信息,要想到可能是堆栈出了问题。
我们测试2个任务,进行任务运行,优先级打印,任务暂停,任务恢复,任务删除。
任务优先级max设置为5
vTask1:设置优先级为3
vTask2:设置优先级为2,task2_count=10->0
主代码:
TaskHandle_t task1 = NULL;
TaskHandle_t task2 = NULL;
int task2_count = 10;
void vTask1(void *pvParameters)
{
while(1)
{
#ifdef STM32
printf("[STM32] hello, this is freertos!\r\n");
#endif
#ifdef GD32
printf("[ GD32] vTask1 running, priority = %ld!\r\n", uxTaskPriorityGet(NULL));
#endif
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vTask2(void *pvParameters)
{
while(task2_count)
{
task2_count--;
printf("[ GD32] vTask2 running of %d!\r\n", task2_count);
if (task2_count == 6)
{
printf("[ GD32] vTask1 priority %ld -> 4\r\n", uxTaskPriorityGet(task1));
vTaskPrioritySet(task1, 4);
}
if (task2_count == 4)
{
printf("[ GD32] vTask1 Suspend\r\n");
vTaskSuspend(task1);
}
if (task2_count == 2)
{
printf("[ GD32] vTask1 Resume\r\n");
vTaskResume(task1);
}
if (task2_count == 0)
{
vTaskDelete(task1);
printf("[ GD32] vTask1 Delete\r\n");
}
vTaskDelay(pdMS_TO_TICKS(2000));
}
printf("[ GD32] vTask2 Delete\r\n");
vTaskDelete(NULL);
}
xTaskCreate(vTask1, "Task1", 1024, NULL, 3, &task1);
xTaskCreate(vTask2, "Task2", 1024, NULL, 2, &task2);
运行结果:
测试结果ok
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask2 running of 9!
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask2 running of 8!
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask2 running of 7!
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask1 running, priority = 3!
[ GD32] vTask2 running of 6!
[ GD32] vTask1 priority 3 -> 4
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask2 running of 5!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask2 running of 4!
[ GD32] vTask1 Suspend
[ GD32] vTask2 running of 3!
[ GD32] vTask2 running of 2!
[ GD32] vTask1 Resume
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask2 running of 1!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask1 running, priority = 4!
[ GD32] vTask2 running of 0!
[ GD32] vTask1 Delete
[ GD32] vTask2 Delete
代码:
xTaskCreate(vTask1, "Task1", 50, NULL, 3, &task1);
xTaskCreate(vTask2, "Task2", 50, NULL, 2, &task2);
增加溢出检查配置:
#define configCHECK_FOR_STACK_OVERFLOW 2
增加溢出检查Hook:
void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName )
{
printf("StackOverflow: %s\r\n", pcTaskName);
}
运行结果:
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask2 running of 9!
StackOverflow: Task2
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask2 running of 8!
StackOverflow: Task2
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask2 running of 7!
StackOverflow: Task2
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask1 running, priority = 3!
StackOverflow: Task1
[ GD32] vTask2 running of 6!
[ GD32] vTask1 priority 3 -> 4
StackOverflow: Task2
[ GD32] vTask1 running, priority = 4!
StackOverflow: €
[ GD32] vTask1 running, priority = 4!
StackOverflow: €
[ GD32] vTask2 running of 5!
StackOverflow: Task2
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
[ GD32] vTask2 running of 4!
[ GD32] vTask1 Suspend
StackOverflow: Task2
[ GD32] vTask2 running of 3!
StackOverflow: Task2
[ GD32] vTask2 running of 2!
[ GD32] vTask1 Resume
StackOverflow: Task2
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
StackOverflow: Task2
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
[ GD32] vTask2 running of 1!
StackOverflow: Task2
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
[ GD32] vTask1 running, priority = 7!
StackOverflow: €
[ GD32] vTask2 running of 0!
[ GD32] vTask1 Delete
StackOverflow: Task2
[ GD32] vTask2 Delete
StackOverflow: Task2
[1]FreeRTOS官方文档: https://www.freertos.org/Documentation/RTOS_book.html