调度器是 FreeRTOS 操作系统的核心,主要负责任务切换,即找出最高优先级的就绪任务,并使之获得 CPU 运行权。调度器并非自动运行的,需要人为启动它。
使用 FreeRTOS,一个最基本的程序架构如下所示:
int main(void)
{
必要的初始化工作;
创建任务1;
创建任务2;
...
vTaskStartScheduler(); /*启动调度器*/
while(1);
}
API 函数 vTaskStartScheduler()
用于启动调度器,它会创建一个空闲任务、初始化一些静态变量,最主要的,它会初始化系统节拍定时器并设置好相应的中断,然后启动第一个任务
过程分析:
(1)创建空闲任务,使用最低优先级
(2)使能软件定时器,并创建定时器任务(可选)
(3)portDISABLE_INTERRUPTS();
关闭中断,确保节拍定时器中断不会在调用xPortStartScheduler()
时或之前发生.当第一个任务启动时,会重新启动中断
(4)xSchedulerRunning = pdTRUE;
表示调度器已开启
(5)调用函数 xPortStartScheduler()
来启动系统节拍定时器并启动第一个任务(硬件相关)
xPortStartScheduler()
相关硬件初始化函数分析(1)将PendSV和SysTick中断设置为最低优先级
portNVIC_SYSPRI2_REG |=portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI;
(2)启动系统节拍定时器,即SysTick定时器,初始化中断周期并使能定时器vPortSetupTimerInterrupt();
(3)启动第一个任务prvStartFirstTask();
prvStartFirstTask();
启动第一个任务__asm void prvStartFirstTask( void )
{
PRESERVE8
/* Cortext-M3硬件中,0xE000ED08地址处为VTOR(向量表偏移量)寄存器,存储向量表起始地址*/
ldr r0, =0xE000ED08
ldr r0, [r0]
/* 取出向量表中的第一项,向量表第一项存储主堆栈指针MSP的初始值*/
ldr r0, [r0]
/* 将堆栈地址存入主堆栈指针 */
msr msp, r0
/* 使能全局中断*/
cpsie i
cpsie f
dsb
isb
/* 调用SVC启动第一个任务 */
svc 0
nop
nop
}
汇编指令 svc 0 触发 SVC 中断,完成启动第一个任务的工作
在SVC中断函数中正式开启第一个任务
__asm void vPortSVCHandler( void )
{
PRESERVE8
ldr r3, =pxCurrentTCB /* pxCurrentTCB指向处于最高优先级的就绪任务TCB */
ldr r1, [r3] /* 获取任务TCB地址 */
ldr r0, [r1] /* 获取任务TCB的第一个成员,即当前堆栈栈顶pxTopOfStack */
ldmia r0!, {r4-r11} /* 出栈,将寄存器r4~r11出栈 */
msr psp, r0 /* 最新的栈顶指针赋给线程堆栈指针PSP */
isb
mov r0, #0
msr basepri, r0
orrr14, #0xd /* 这里0x0d表示:返回后进入线程模式,从进程堆栈中做出栈操作,返回Thumb状态*/
bx r14
}
参考文献:https://blog.csdn.net/qq_27114397/article/details/83017422