FreeRTOS启动流程以及任务调度

FreeRTOS启动流程

概述

FreeRTOS的启动流程通常分为硬件初始化和操作系统初始化两个阶段。以下是典型启动流程:

硬件初始化阶段由用户完成,包括时钟配置、外设初始化、堆栈设置等。通常需要在main()函数中调用硬件相关的初始化函数。

操作系统初始化阶段由FreeRTOS完成。调用vTaskStartScheduler()启动调度器,此函数会初始化内核组件(如任务列表、空闲任务、定时器任务等),并启动第一个任务。

启动流程示例代码:
1.startup.s
  • 程序开始时pc指向0x0,从startup.s文件开始,先分配栈大小和堆大小
  • 定义中断向量表
  • 从Reset_Handler开始执行,进入main函数
/*************************************************************************/
Stack_Size		EQU     0x1000
Heap_Size      EQU     0x200


/*************************************************************************/
; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
    
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ......................这里不一一列举了
                

/*************************************************************************/
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
2.main函数
int main(void)
{
  /*********** STM32系统初始化 **********/
  HAL_Init();
  SystemClock_Config();
  
  /** 外设初始化,包括开启时钟,配置寄存器 **/
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_I2C1_Init();   
  
  /************* 进入freertos **********/  
  freertos_start();               //从此进入rtos
  
  while(1)
  {         }

}          


 HAL_Init(void)
{
  
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);  //NVIC分组设置
  HAL_InitTick(TICK_INT_PRIORITY);                     //开启systick,1ms一次(HSI)
  HAL_MspInit();
  return HAL_OK;
}   


void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  //电源相关配置
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  //初始化rcc,配置成HSE提供时基,锁相环相关配置达到想要的主频(就是通过结构体操作rcc寄存器)
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /***** systick,AHB,APB1,APB2时钟配置(系统时钟的分频)
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}     
3.freertos_start()

此函数就干两个事:

  • 创建任务
  • 启动调度器
 void freertos_start(void)
{
    xTaskCreate(Start_task, "task0", 128, "task0", 1, &task0); 
    vTaskStartScheduler();
}
void vTaskStartScheduler( void )
{
    BaseType_t xReturn;
/**************************** 动态创建idle任务 ******************************/
    #else /* configSUPPORT_STATIC_ALLOCATION == 1  */
    {
        xReturn = xTaskCreate( prvIdleTask,
                               configIDLE_TASK_NAME,
                               configMINIMAL_STACK_SIZE,
                               ( void * ) NULL,
                               portPRIVILEGE_BIT,  //优先级为0,最低
                               &xIdleTaskHandle ); 
    }
    #endif /* configSUPPORT_STATIC_ALLOCATION */
/*------------------------------------------------------------------------*/
   

     
/**************************** 动态创建timer任务 ******************************/
    #if ( configUSE_TIMERS == 1 )
    {
        if( xReturn == pdPASS )
        {
            xReturn = xTimerCreateTimerTask();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* configUSE_TIMERS */
/*------------------------------------------------------------------------*/


    if( xReturn == pdPASS )
    {
        

    
        portDISABLE_INTERRUPTS();                //关中断

     

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;                            //设置调度器运行标志位
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;  //全局时间统计值

        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
        traceTASK_SWITCHED_IN();

        xPortStartScheduler();                                 //真正开始调度
        
    }
    else
    {
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }

   
    ( void ) xIdleTaskHandle;

   
    ( void ) uxTopUsedPriority;
}
/*-----------------------------------------------------------*/

调度器启动后会执行以下操作:

  • 创建空闲任务(优先级为0)
  • 如果启用软件定时器则创建定时器服务任务
  • 开始执行最高优先级的就绪任务

FreeRTOS任务调度机制

FreeRTOS采用优先级抢占式调度算法,支持时间片轮转调度。调度行为主要基于任务优先级和状态:

任务状态包括:

  • 就绪态(Ready):任务准备运行
  • 运行态(Running):当前正在执行的任务
  • 阻塞态(Blocked):任务等待事件或延时
  • 挂起态(Suspended):任务被主动挂起

调度触发条件:

  • 主动调用taskYIELD()portYIELD()函数(这俩是同一个一个东西),进程间通信会用到
  • 系统节拍时钟中断(tick中断)
  • 任务状态变化(如解除阻塞、优先级变化)

调度策略:

  • 总是运行最高优先级的就绪任务
  • 相同优先级任务采用时间片轮转(需配置configUSE_TIME_SLICING为1)
  • 可嵌套的中断处理机制

关键调度函数:

// 查找最高优先级就绪任务
taskSELECT_HIGHEST_PRIORITY_TASK();

// 上下文切换
vTaskSwitchContext();            //通过赋予xYieldPending的值完成调度的触发

// 触发调度的宏
portYIELD();

调度器类型:

  • 合作式调度器(已弃用)
  • 抢占式调度器(默认)
  • 带时间片轮转的抢占式调度器

配置选项(FreeRTOSConfig.h相关):

#define configUSE_PREEMPTION        1    // 启用抢占
#define configUSE_TIME_SLICING      1    // 启用时间片


你可能感兴趣的:(单片机,嵌入式硬件)