FreeRTOS解析
FreeRTOS源码解析
基本配置
1 | #define portSTACK_GROWTH ( -1 ) |
任务管理
任务控制块TCB
1 | /* |
动态创建任务
1 | // tasks.c |
portSTACK_GROWTH < 0,栈向下增长;为避免栈增长到TCB中,需要先申请栈再申请TCB结构。
pxNewTCB->pxStack = pxStack;将栈赋值到TCB中。
prvInitialiseNewTask初始化TCB结构。
prvAddNewTaskToReadyList将任务添加到就绪列表中。1
static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
taskENTER_CRITICAL进入临界区,关闭中断。
prvAddTaskToReadyList将任务按优先级加入就绪队列中。
taskEXIT_CRITICAL退出临界区,开中断。
开始任务调度
1 | // tasks.c |
xTaskCreate创建idle任务。
xTimerCreateTimerTask创建计时器任务。
portDISABLE_INTERRUPTS禁用中断。
xPortStartScheduler调用开启硬件调度接口,一般是启动tick计时器。1
2// port.c
BaseType_t xPortStartScheduler( void )
portDISABLE_INTERRUPTS禁用中断。
configSETUP_TICK_INTERRUPT配置tick计时器。
vPortRestoreTaskContext开始执行第一个任务。
汇编程序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// portASM.S
/******************************************************************************
* vPortRestoreTaskContext is used to start the scheduler.
*****************************************************************************/
.align 8
.type vPortRestoreTaskContext, %function
vPortRestoreTaskContext:
.set freertos_vector_base, _freertos_vector_table
/* Install the FreeRTOS interrupt handlers. */
LDR X1, =freertos_vector_base
#if defined( GUEST )
MSR VBAR_EL1, X1
#else
MSR VBAR_EL3, X1
#endif
DSB SY
ISB SY
/* Start the first task. */
portRESTORE_CONTEXT
任务切换
MPS2_QEMU会调用汇编处理程序xPortPendSVHandler1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35void xPortPendSVHandler( void )
{
/* This is a naked function. */
__asm volatile
(
" mrs r0, psp \n"
" isb \n"
" \n"
" ldr r3, pxCurrentTCBConst \n"/* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" stmdb r0!, {r4-r11} \n"/* Save the remaining registers. */
" str r0, [r2] \n"/* Save the new top of stack into the first member of the TCB. */
" \n"
" stmdb sp!, {r3, r14} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r3, r14} \n"
" \n"/* Restore the context, including the critical nesting count. */
" ldr r1, [r3] \n"
" ldr r0, [r1] \n"/* The first item in pxCurrentTCB is the task top of stack. */
" ldmia r0!, {r4-r11} \n"/* Pop the registers. */
" msr psp, r0 \n"
" isb \n"
" bx r14 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
);
}
OpenAI辅助理解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23void PendSV_Handler(void) {
/* 保存当前任务的上下文 */
__asm volatile (
"MRS R0, PSP \n" /* 获取进程堆栈指针 */
"STMDB R0!, {R4-R11} \n" /* 保存R4-R11寄存器到堆栈 */
"LDR R1, =pxCurrentTCB \n" /* 加载当前TCB指针地址 */
"LDR R2, [R1] \n" /* 加载当前TCB指针 */
"STR R0, [R2] \n" /* 保存堆栈指针到当前TCB */
);
/* 切换到下一个任务 */
vTaskSwitchContext();
/* 恢复下一个任务的上下文 */
__asm volatile (
"LDR R1, =pxCurrentTCB \n" /* 加载当前TCB指针地址 */
"LDR R2, [R1] \n" /* 加载当前TCB指针 */
"LDR R0, [R2] \n" /* 加载堆栈指针 */
"LDMIA R0!, {R4-R11} \n" /* 恢复R4-R11寄存器 */
"MSR PSP, R0 \n" /* 恢复进程堆栈指针 */
"BX LR \n" /* 返回任务 */
);
}
然后调用切换上下文函数1
2// task.c
void vTaskSwitchContext( void )
taskSELECT_HIGHEST_PRIORITY_TASK选择最高优先级任务
时钟管理
时钟节拍
MPS2_QEMU会调用处理程序xPortSysTickHandler1
2// port.c
void xPortSysTickHandler( void )
portDISABLE_INTERRUPTS关闭中断。
xTaskIncrementTick尝试增加tick值,成功后触发PendSV中断,强行进行一次调度。
portENABLE_INTERRUPTS打开中断。1
2// tasks.c
BaseType_t xTaskIncrementTick( void )
taskSWITCH_DELAYED_LISTS系统tick值反转时,将延时任务队列与反转反转队列互换。
判断是否有超时的任务,并将其移入就绪队列中。
任务延时
1 | void vTaskDelay( const TickType_t xTicksToDelay ) |
vTaskSuspendAll关闭调度器。
prvAddCurrentTaskToDelayedList将当前任务加到延时列表中。
xTaskResumeAll恢复调度器。
portYIELD_WITHIN_API当恢复调度时未进行再次调度,则强制进行调度并让任务进入睡眠状态。1
2
3static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
const BaseType_t xCanBlockIndefinitely )
uxListRemove将当前任务移除队列中。
xTimeToWake = xConstTickCount + xTicksToWait计算下次唤醒的时刻。需要考虑时间反转的场景。