找回密码
 立即注册
首页 业界区 安全 FreeRTOS启动任务调度器函数解释

FreeRTOS启动任务调度器函数解释

各卧唯 2025-6-8 12:45:55
目录

  • vTaskStartScheduler() 函数

    • xPortStartScheduler() 函数
    • prvStartFirstTask() 函数
    • vPortSVCHandler() 函数


FreeRTOS的任务开始运行的前提是调用了启动调度器函数 vTaskStartScheduler() ,只有调用了该函数任务才会被调度并运行。下面以FreeRTOS v9.0.0版本的源码进行分析FreeRTOS任务调度的启动流程。
vTaskStartScheduler() 函数
  1. void vTaskStartScheduler(void)
  2. {
  3.     BaseType_t xReturn;
  4. /* 静态方法创建空闲任务 */
  5. #if (configSUPPORT_STATIC_ALLOCATION == 1)
  6.     {
  7.         StaticTask_t *pxIdleTaskTCBBuffer = NULL;
  8.         StackType_t *pxIdleTaskStackBuffer = NULL;
  9.         uint32_t ulIdleTaskStackSize;
  10.         /* 以静态方式创建任务用户需自定义空闲任务的内存分配函数 */
  11.         vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize);
  12.         xIdleTaskHandle = xTaskCreateStatic(prvIdleTask,
  13.                                             "IDLE",
  14.                                             ulIdleTaskStackSize,
  15.                                             (void *)NULL,
  16.                                             (tskIDLE_PRIORITY | portPRIVILEGE_BIT),
  17.                                             pxIdleTaskStackBuffer,
  18.                                             pxIdleTaskTCBBuffer);
  19.         if (xIdleTaskHandle != NULL)
  20.         {
  21.             xReturn = pdPASS;
  22.         }
  23.         else
  24.         {
  25.             xReturn = pdFAIL;
  26.         }
  27.     }
  28. #else
  29.     { /* 动态方法创建空闲任务 */
  30.         xReturn = xTaskCreate(prvIdleTask,
  31.                               "IDLE", configMINIMAL_STACK_SIZE,
  32.                               (void *)NULL,
  33.                               (tskIDLE_PRIORITY | portPRIVILEGE_BIT),
  34.                               &xIdleTaskHandle);
  35.     }
  36. #endif /* configSUPPORT_STATIC_ALLOCATION */
  37. /* 如果启用软件定时器 */
  38. #if (configUSE_TIMERS == 1)
  39.     {
  40.         if (xReturn == pdPASS)
  41.         {
  42.             /* 创建空闲任务成功,且启用软件定时器,就创建定时器任务 */
  43.             xReturn = xTimerCreateTimerTask();
  44.         }
  45.         else
  46.         {
  47.             mtCOVERAGE_TEST_MARKER();
  48.         }
  49.     }
  50. #endif /* configUSE_TIMERS */
  51.     /* 任务创建成功或定时器任务创建成功(如果使能定时器任务创建的话)*/
  52.     if (xReturn == pdPASS)
  53.     {
  54.         /* 关中断,确保开启调度器之前或过程中,SysTick 不会产生中断
  55.            在第一个任务开始运行时,会重新打开中断 */
  56.         portDISABLE_INTERRUPTS();
  57. #if (configUSE_NEWLIB_REENTRANT == 1)
  58.         {
  59.             _impure_ptr = &(pxCurrentTCB->xNewLib_reent);
  60.         }
  61. #endif /* configUSE_NEWLIB_REENTRANT */
  62.         /* 设置下一个任务的解锁时间为最大,这样可以避免在启动调度器之前不会因为任务解锁而引起任务调度 */
  63.         xNextTaskUnblockTime = portMAX_DELAY;
  64.         /* 置 xSchedulerRunning 标志为真,这指示这调度器即将进行运行 */
  65.         xSchedulerRunning = pdTRUE;
  66.         /* 将计数值初始化为0,确保在启动调度器时开始计数,使所有任务的时钟节拍一致 */
  67.         xTickCount = (TickType_t)0U;
  68.         /* 如果定义了configGENERATE_RUN_TIME_STATS,
  69.         则必须定义以下宏来配置用于生成运行时计数器时基的定时器/计数器。
  70.         运行时间统计功能 */
  71.         portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
  72.         /* 用于完成启动任务调度器中与硬件架构相关的配置部分,以及启动第一个任务
  73.         调用这个函数以后,就不会再回来了*/
  74.         if (xPortStartScheduler() != pdFALSE)
  75.         {
  76.             /* 调度器正在运行函数不会返回到这里执行 */
  77.         }
  78.         else
  79.         {
  80.             /* 只有当任务调用 xTaskEndScheduler() 时才会到达这里
  81.             如果调度器没有成功启动,或者某个任务调用了 xTaskEndScheduler() 函数以结束调度器的运行,
  82.             则程序将会执行到这里。因此,在这里放置的代码可能会处理一些特殊情况或进行清理工作 */
  83.         }
  84.     }
  85.     /* 可能是因为没有足够的堆空间来创建空闲任务或定时器任务
  86.     通过断言来检查内核是否能成功分配所需的内存 */
  87.     else
  88.     {
  89.         configASSERT(xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY);
  90.     }
  91.     /* 防止编译器警告 */
  92.     (void)xIdleTaskHandle;
  93. }
复制代码
可以看到该函数主要做了几件事。
1.创建空闲任务,根据配置以不同方式创建空闲任务,静态或者动态方式。
2.如果启动了软件定时器功能就创建软件定时器任务,根据配置以静态还是动态方式进行创建软件定时器任务。
3.关闭中断,使用 portDISABLE_INTERRUPTS() 关闭中断,这种方式只会关闭受FreeRTOS所管理的中断,主要是为了防止Systick中断在任务调度器开启之前或过程中产生中断,FreeRTOS会在开始运行第一个任务时重新打开中断。
4.初始化一些全局变量,并将调度器标记为正在运行。
5.初始化任务运行时间统计功能的时基定时器,任务运行时间统计功能需要一个硬件定时器提供高精度的计数,这个硬件定时器就在这里进行配置,如果配置不启用任务运行时间统计功能的,就无需进行这项硬件定时器的配置。
6.调用 xPortStartScheduler() 启动调度器。
xPortStartScheduler() 函数

[code]/* 代码确定了可以在中断服务例程中调用的 FreeRTOS API 函数的最高优先级。这样可以确保在中断上下文中仅调用安全的API函数,从而保持中断处理的效率和可靠性。*/BaseType_t xPortStartScheduler(void){#if (configASSERT_DEFINED == 1)    {        volatile uint32_t ulOriginalPriority; // 用于保存即将被覆盖的中断优先级值。        /* 指向第一个用户中断的优先级寄存器地址。这个寄存器存储了中断的优先级值 */        volatile uint8_t *const pucFirstUserPriorityRegister = (uint8_t *)(portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER);        /* 用于存储计算得到的最大优先级值 */        volatile uint8_t ucMaxPriorityValue;        /* 确定FreeRTOS ISR安全的API函数可以调用的最大优先级。ISR安全函数是以“FromISR”结尾的。        保存即将被覆盖的中断优先级值 */        ulOriginalPriority = *pucFirstUserPriorityRegister;        /* 确定可用的优先级位的数量。首先写入所有可能的位 */        *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;        ucMaxPriorityValue = *pucFirstUserPriorityRegister;        /* Use the same mask on the maximum system call priority.        对最大系统调用优先级使用相同的掩码。*/        ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;        /* Calculate the maximum acceptable priority group value for the number        of bits read back.*/        ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;              while ((ucMaxPriorityValue & portTOP_BIT_OF_BYTE) == portTOP_BIT_OF_BYTE)        {            ulMaxPRIGROUPValue--;            ucMaxPriorityValue
您需要登录后才可以回帖 登录 | 立即注册