FreeRTOS的启动流程通常分为硬件初始化和操作系统初始化两个阶段。以下是典型启动流程:
硬件初始化阶段由用户完成,包括时钟配置、外设初始化、堆栈设置等。通常需要在main()
函数中调用硬件相关的初始化函数。
操作系统初始化阶段由FreeRTOS完成。调用vTaskStartScheduler()
启动调度器,此函数会初始化内核组件(如任务列表、空闲任务、定时器任务等),并启动第一个任务。
/*************************************************************************/
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
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();
}
}
此函数就干两个事:
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;
}
/*-----------------------------------------------------------*/
调度器启动后会执行以下操作:
FreeRTOS采用优先级抢占式调度算法,支持时间片轮转调度。调度行为主要基于任务优先级和状态:
任务状态包括:
调度触发条件:
taskYIELD()
或portYIELD()函数(这俩是同一个一个东西),进程间通信会用到
调度策略:
configUSE_TIME_SLICING
为1)关键调度函数:
// 查找最高优先级就绪任务
taskSELECT_HIGHEST_PRIORITY_TASK();
// 上下文切换
vTaskSwitchContext(); //通过赋予xYieldPending的值完成调度的触发
// 触发调度的宏
portYIELD();
调度器类型:
配置选项(FreeRTOSConfig.h相关):
#define configUSE_PREEMPTION 1 // 启用抢占
#define configUSE_TIME_SLICING 1 // 启用时间片