基于STM32,cortex-M3架构源码进行分析。

1. 上电复位

从FLASH 0x08000000开始运行,首先初始化栈指针sp,执行Reset_Handler。

在Reset_Handler中执行两个函数:SystemInit、__main。

SystemInit可选,由STM官方提供,如果使用外部SRAM,需要在这里初始化;用户也可以在这里修改中断向量表地址。

__main函数由ARM编译器提供,自动链接进程序中,主要是初始化.data段,清空.bss段,调用main()函数。

2. main()函数

首先调用HAL_INIT():

使能FLASH的预取缓冲器,提高执行效率。设置NVIC优先级分组(此时应设置为4bit全部用作抢占优先级,无响应优先级)。初始化SysTick定时器并开始定时中断,默认使用TIM6且优先级最低,1ms中断一次。初始化底层外设(AFIO时钟、PWR电源控制时钟、设置PendSV中断优先级为最低)

配置时钟树。

初始化基本外设(GPIO、RTC、I2C....)

3. 进入FreeRTOS初始化

还是在main()函数中,调用osKernelInitialize():

初始化RTOS内核状态,根据堆管理等级配置堆内存分配器。

调用MX_FREERTOS_INIT():

用户自定义初始化代码段,比如需要用到的互斥锁、信号量、消息队列等工具。

调用osKernelStart()准备开始调度。

4.osKernelStart()

继续调用vTaskScheduler():

首先创建空闲线程任务IdleTask:

任务优先级设置为最低0,主要负责回收已删除的任务资源(TCB、堆栈)。

两种可选(通过宏定义选择)创建方式,静态创建则程序提供固定StackBuffer,并且用vApplicationGetIdleTaskMemory分配一个TCBBuffer,之后创建任务;动态则直接调用xTaskCreate创建任务。函数为prvIdleTask。

空闲任务还负责:在回收资源之后,检查是否需要让出处理器;

执行用户定义的构子函数vApplicationIdleHook,通过宏定义configUSE_IDLE_HOOK设置;

判断是否进入无节拍休眠(Tickless Idle),主要用于系统低功耗运行模式:首先估算剩余时间判断是否进入,通过比较当前Tick值和即将就绪任务的Tick值是否足够长,若可以进入则调用vTaskSuspendAll挂起调度器,调用portSUPPRESS_TICKS_AND_SLEEP暂时周期性地停止SysTick中断,并在醒来后修正系统时间,调用xTaskResumeAll恢复调度器。这里系统休眠前后都可以执行自定义钩子函数。

接着创建定时器任务TimerTask

与上述创建流程类似,函数为prvTimerTask,默认任务优先级为2。主要负责监视软件定时器列表及命令列表。

具体来说,先执行用户定义的钩子函数;

随后在死循环中,检查是否已有定时器并获得它们的到期时间;先暂停调度,判断是否有超时定时器,若无,则设置阻塞至下个到期时间(没有定时器则无限期阻塞),然后恢复调度器;若有,则先恢复调度器,然后处理所有超时定时器。

最后处理增删改定时器的命令列表。

先关闭中断

设置当前为无任务阻塞状态

设置调度器状态为Running,TickCount为0。

可选步骤:设置统计任务运行时长的定时器、调用任务切换的钩子函数

调用xPortStartScheduler启动调度

检查、修正中断优先级,不能使用最高优先级调用FreeRTOS的API;

设置PendSV(任务切换)和SysTick中断优先级为最低,防止抢占其他中断如xxxFromISR;

配置、启动产生Tick的硬件中断vPortSetupTimerInterrupt();

设置临界区嵌套值uxCriticalNesting为0;

调用vPortStartFirstTask启动第一个任务,真正由调度器接管。

vPortStartFirstTask是汇编代码,主要工作是触发svc系统调用异常,在中断处理函数中跳转到第一个任务中运行,从此由调度器接管。

具体来说,先初始化栈指针,这里复用了中断向量表的代码,去VTOR(0xE000ED08)寄存器找到向量表起始地址,将主栈指针MSP恢复为启动时的初始值。

使能IRQ、FIQ中断;

触发SVC系统异常调用,进入SVC中断处理函数。

vPortSVCHandler函数:调度器入口

加载pxCurrentTCB到r3,取出其首地址的内容(即该任务的进程栈顶地址)到r0,从进程栈中恢复r4-r11的值,更新进程栈地址到psp进程栈寄存器。清楚中断标志位,修改此时的r14(lr)寄存器为0xd,执行bx r14表示异常返回,开始执行第一个任务。

补充一下cortex-M架构的机制:M架构下进硬件中断会自动保存r0-r13、r12、lr、pc、xPSR寄存器,而r4-r11寄存器通常需要在中断中手动保存\恢复。在lr值为0xd时执行bx lr表示异常返回,此时硬件会自动从psp中弹出r0-r3、r12、lr、xPSR,然后去对应的pc地址处执行。

至此,调度系统开始运行。

调度过程的核心主要是SysTick中断和PendSV中断的处理,这些内容在下一节分析

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐