前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >M-Arch(14)兼容FreeRTOS:FreeRTOS任务管理

M-Arch(14)兼容FreeRTOS:FreeRTOS任务管理

作者头像
滚神大人
发布2022-01-24 11:10:08
7270
发布2022-01-24 11:10:08
举报
文章被收录于专栏:趣Python趣Python

前言

回顾下之前的章节:我们在一个简单的定时器OS基础上实现了cortex-M系列架构的兼容,并基于单片机的基本资源实现了很多实例。

从这个章节开始,我们把FreeRTOS移植进来,同时还考虑兼容性。

上一节我们介绍了移植FreeRTOS的方法,并基于此实现一个最基本的例子:串口定时打印数据。

这一节我们分析下FreeRTOS的任务管理。

开发板是GD32的开发板。

关键字:FreeRTOS,STM32,GD32,任务管理

关于FreeRTOS的官方文档,里面实际上也讲的很详细,参见:FreeRTOS官方文档[1]

任务

如果非要给任务下个定义的话,在嵌入式系统中,应该叫做具象业务的具象表达(独家定义,如有雷同,对方是假货)。

比如:采样是具象业务,对应的具象表达就是sample()。

任务需要占用一定的资源,比如内存,CPU。

不同任务都想要执行,那么存在资源的冲突问题。

任务需要解决资源和资源冲突的问题。

写过单片机程序的同学应该都写过裸机程序,官方叫法叫前后台系统。

具体做法就是:

  1. 定义一个时基,比如:10ms。
  2. 给不同的任务定义不同的运行周期。
  3. main函数中一个while(1),时基计数,到了某个任务的运行周期就运行某个任务。
  4. 如有中断,优先运行中断。

这种做法的缺点也很明显,就是一大锅饭,大家一起等着,到点了才能领饭,管你是不是老弱病残孕。

在FreeRTOS中,任务被赋予了优先级,高优先级的任务可优先运行;当然,它占用ram资源多一些。

这个跟软件优化有点像,要么是用时间换空间,要么是空间换时间

FreeRTOS的任务已经具备了进程的特性,类似于Linux中没有线程的进程。

任务状态机

创建任务

代码语言:javascript
复制
void ATaskFunction( void *pvParameters )
{

 for( ;; )
 {
    /* The code to implement the task functionality will go here. */
 }
 
 /* 如果要删除任务,调用vTaskDelete函数 */
 vTaskDelete( NULL );
}

任务由xTaskCreate函数创建(详细可查看手册的3.4 Creating Tasks):

代码语言:javascript
复制
BaseType_t xTaskCreate(  TaskFunction_t pvTaskCode, 
                         const char * const pcName, 
                         uint16_t usStackDepth, 
                         void *pvParameters, 
                         UBaseType_t uxPriority, 
                         TaskHandle_t *pxCreatedTask );
  • 参数
    • pvTaskCode:任务函数指针:typedef void (*TaskFunction_t)( void * )
    • pcName:任务名称字符串,该字符串的最大长度由FreeRTOSConfig.h中的宏configMAX_TASK_NAME_LEN指定。
    • usStackDepth:任务堆栈大小,单位是堆栈位宽的数量。在32位宽度的堆栈下,usStackDepth定义为100,则实际使用100*4字节堆栈存储空间。
    • pvParameters:任务的指针参数,任务创建时,作为一个参数传递给任务。
    • uxPriority:任务的优先级,最大值由configMAX_PRIORITIES指定。
    • pvCreatedTask:当前任务指针,可以通过它来修改任务优先级或者删除任务;如果不用,置为NULL。
  • 返回值
    • pdPASS:成功。
    • pdFAIL:失败,堆栈或者ram不够。

删除任务

函数比较简单。

代码语言:javascript
复制
void vTaskDelete( TaskHandle_t pxTaskToDelete );
  • 参数
    • pxTaskToDelete:任务指针,如果为NULL,表示删除当前任务。

注:使用本函数需要在FreeRTOSConfig.h中设置INCLUDE_vTaskDelete宏为1

其他任务相关函数

vTaskStartScheduler 任务调度主函数

代码语言:javascript
复制
void vTaskStartScheduler( void );

vTaskSuspend 挂起指定任务

代码语言:javascript
复制
void vTaskSuspend( TaskHandle_t xTaskToSuspend );

注:使用本函数需要在FreeRTOSConfig.h中设置INCLUDE_vTaskSuspend宏为1

vTaskSuspendAll 挂起所有任务

代码语言:javascript
复制
void vTaskSuspendAll( void );

vTaskResume 让挂起任务重新就绪

代码语言:javascript
复制
void vTaskResume( TaskHandle_t xTaskToResume );

注:使用本函数需要在FreeRTOSConfig.h中设置INCLUDE_vTaskSuspend宏为1

xTaskResumeFromISR 让挂起任务重新就绪(中断)

代码语言:javascript
复制
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );

注:使用本函数需要在FreeRTOSConfig.h中设置INCLUDE_vTaskSuspendINCLUDE_xTaskResumeFromISR宏为1

xTaskResumeAll 恢复所有任务

代码语言:javascript
复制
BaseType_t xTaskResumeAll( void );

uxTaskPriorityGet 任务优先级获取

代码语言:javascript
复制
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );

注:使用本函数需要在FreeRTOSConfig.h中设置INCLUDE_uxTaskPriorityGet宏为1

vTaskPrioritySet 任务优先级设置

代码语言:javascript
复制
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.

总结一下:

  1. 为了保证系统正常运行,必须有一个空闲任务。
  2. 空闲任务在vTaskStartScheduler被调用时由系统自动创建。
  3. 空闲任务为最低优先级0,保证其他任务正常运行。
  4. 当其他任务调用vTaskDelete时,空闲任务负责处理资源回收。

空闲任务的堆栈大小由configMINIMAL_STACK_SIZE指定。

空闲任务Hook

Hook一般翻译为钩子,在软件设计中,通常是一个函数指针。

空闲任务Hook的函数原型如下:

代码语言:javascript
复制
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:

代码语言:javascript
复制
#define tskSTACK_FILL_BYTE  ( 0xa5U )

建议调试时,设置configCHECK_FOR_STACK_OVERFLOW为2。

当然,当堆栈溢出时,可能已经造成了内存错误从而无法检查,所以如果系统进入了HardFault_Handler却查不到错误信息,要想到可能是堆栈出了问题。

测试

我们测试2个任务,进行任务运行,优先级打印,任务暂停,任务恢复,任务删除。

任务优先级max设置为5

vTask1:设置优先级为3

  • 运行并打印当前运行优先级

vTask2:设置优先级为2,task2_count=10->0

  • task2_count = 6时,修改task1优先级为4
  • task2_count = 4时,暂停task1
  • task2_count = 2时,恢复task1
  • task2_count = 0时,删除task1
  • 最后:删除task2

正常测试

主代码:

代码语言:javascript
复制
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);    
}
代码语言:javascript
复制
    xTaskCreate(vTask1, "Task1", 1024, NULL, 3, &task1);
    xTaskCreate(vTask2, "Task2", 1024, NULL, 2, &task2);

运行结果:

测试结果ok

代码语言:javascript
复制
[ 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

异常测试 - 堆栈溢出

代码:

代码语言:javascript
复制
    xTaskCreate(vTask1, "Task1", 50, NULL, 3, &task1);
    xTaskCreate(vTask2, "Task2", 50, NULL, 2, &task2);

增加溢出检查配置:

代码语言:javascript
复制
#define configCHECK_FOR_STACK_OVERFLOW 2

增加溢出检查Hook:

代码语言:javascript
复制
void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName )
{
    printf("StackOverflow: %s\r\n", pcTaskName);
}

运行结果:

代码语言:javascript
复制
[ 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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 趣Python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 任务
  • 任务状态机
  • 创建任务
  • 删除任务
  • 其他任务相关函数
    • vTaskStartScheduler 任务调度主函数
      • vTaskSuspend 挂起指定任务
        • vTaskSuspendAll 挂起所有任务
          • vTaskResume 让挂起任务重新就绪
            • xTaskResumeFromISR 让挂起任务重新就绪(中断)
              • xTaskResumeAll 恢复所有任务
                • uxTaskPriorityGet 任务优先级获取
                  • vTaskPrioritySet 任务优先级设置
                  • 空闲任务
                  • 空闲任务Hook
                  • 任务堆栈溢出
                  • 测试
                    • 正常测试
                      • 异常测试 - 堆栈溢出
                        • 参考资料
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档