FreeRTOS
目录
- 0. FreeRTOS配置
- 1. void prvStartFirstTask( void )
- 2. void vPortSVCHandler( void )
- 3. void xPortPendSVHandler( void )
- 4. StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
- 5. void xPortSysTickHandler( void )
正文
0. FreeRTOS配置
- (1) FreeRTOS版本v8.1.2,以下介绍的函数与当前较新的10.1.1版本并无太多变化。
- (2) 未开启FPU
1. prvStartFirstTask()
__asm void prvStartFirstTask( void )
{PRESERVE8 /* 堆栈的8字节对齐,参考8_DUI0473Marmasm_user_guide.pdf及AAPCS *//* Use the NVIC offset register to locate the stack. */ldr r0, =0xE000ED08 /* SCB->VTOR,向量表偏移地址寄存器*/ldr r0, [r0] /* 从向量表偏移地址寄存器中取出起始地址(0x08000000) */ldr r0, [r0] /* 取出STACK起始地址并初始化到MSP *//* Set the msp back to the start of the stack. */msr msp, r0 /* Globally enable interrupts. */cpsie i /* 使能中断 */cpsie fdsb /* 数据和指令同步隔离,将流水线中的数据和指令全部执行完毕 */isb/* Call SVC to start the first task. */svc 0 /* 生成svc中断 */nopnop
}
2. vPortSVCHandler()
__asm void vPortSVCHandler( void )
{PRESERVE8/* Get the location of the current TCB. */ldr r3, =pxCurrentTCB /* 获取当前任务的句柄,并获取任务的堆栈地址给r0 */ldr r1, [r3]ldr r0, [r1]/* Pop the core registers. */ldmia r0!, {r4-r11, r14} /* 从CurrentTCB的任务栈中恢复出各个寄存器,这里的r14出栈后就被恢复为了0xFFFFFFFD(详见pxPortInitialiseStack()),即当退出SVC中断后返回到ThreadMode且使用PSP */msr psp, r0isbmov r0, #0 /* 放开中断限制 */msr basepri, r0bx r14
}
3. xPortPendSVHandler()
__asm void xPortPendSVHandler( void )
{extern uxCriticalNesting;extern pxCurrentTCB;extern vTaskSwitchContext;PRESERVE8mrs r0, psp /* 获取当前任务栈指针PSP到r0 */isb /* 指令同步隔离,将流水线中的指令全部执行完毕 *//* Get the location of the current TCB. */ldr r3, =pxCurrentTCB /* 获取pxCurrentTCB的地址到r3 */ldr r2, [r3] /* 获取pxCurrentTCB到r2 *//* Is the task using the FPU context? If so, push high vfp registers. */tst r14, #0x10it eqvstmdbeq r0!, {s16-s31}/* Save the core registers. */stmdb r0!, {r4-r11, r14} /* 将r14,r11-r4依次入任务栈,栈指针为r0指向的地址(保存现场) *//* Save the new top of stack into the first member of the TCB. */str r0, [r2] /* 更新当前任务TCB的栈顶指针pxTopOfStack */stmdb sp!, {r0, r3} /* 将r3(pxCurrentTCB的地址)和r0(当前任务栈指针栈顶)的内容保存至主任务堆栈 */mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITYmsr basepri, r0 /* 屏蔽优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断,防止产生意外 */dsb /* 数据同步隔离,将流水线中的数据全部执行完毕 */isb /* 指令同步隔离,将流水线中的指令全部执行完毕 */bl vTaskSwitchContext /* 调用上下文切换函数,找出最高优先级的任务并将指针放入pxCurrentTCB */mov r0, #0 /* 取消屏蔽优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断 */msr basepri, r0ldmia sp!, {r0, r3} /* 从主栈恢复出TCB的任务栈栈顶和&pCurrentTCB分别到r0,r3 *//* The first item in pxCurrentTCB is the task top of stack. */ldr r1, [r3] /* 加载pxCurrentTCB到r1 */ldr r0, [r1] /* 加载pxCurrentTCB任务的栈顶到r0 *//* Pop the core registers. */ldmia r0!, {r4-r11, r14} /* 从新的任务栈恢复进入PendSV中断前的现场 *//* Is the task using the FPU context? If so, pop the high vfp registerstoo. */tst r14, #0x10it eqvldmiaeq r0!, {s16-s31}msr psp, r0 /* 更新PSP */isb#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */#if WORKAROUND_PMU_CM001 == 1push { r14 }pop { pc }nop#endif#endifbx r14 /* 退出 */
}
4. pxPortInitialiseStack()
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{/* Simulate the stack frame as it would be created by a context switchinterrupt. */ /* 模拟中断后堆栈的栈帧内容,初始化各个任务的任务栈 *//* Offset added to account for the way the MCU uses the stack on entry/exitof interrupts, and to ensure alignment. */pxTopOfStack--;*pxTopOfStack = portINITIAL_XPSR; /* xPSR */pxTopOfStack--;*pxTopOfStack = ( StackType_t ) pxCode; /* PC */pxTopOfStack--;*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ /* 无处返回,出错 *//* Save code space by skipping register initialisation. */pxTopOfStack -= 5; /* R12, R3, R2 and R1. */*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ /* 传递的参数放入r0中 *//* A save method is being used that requires each task to maintain itsown exec return value. */pxTopOfStack--;*pxTopOfStack = portINITIAL_EXEC_RETURN; /* 0xFFFFFFFD 返回到Thread & 使用PSP */pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */return pxTopOfStack;
}
5. xPortSysTickHandler()
void xPortSysTickHandler( void )/* */
{/* The SysTick runs at the lowest interrupt priority, so when this interruptexecutes all interrupts must be unmasked. There is therefore no need tosave and then restore the interrupt mask value as its value is alreadyknown. *//*SysTick中断的优先级是系统中最低的,因此如果已经进入了这个中断中,那么就可以判断当前系统中的所有中断都是未屏蔽的。因此没有必要保存中断屏蔽值,因为其已知。 */( void ) portSET_INTERRUPT_MASK_FROM_ISR(); /* 屏蔽优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断,防止使用FreeRTOS API的程序对下面产生影响 */{/* Increment the RTOS tick. */if( xTaskIncrementTick() != pdFALSE ) /* 检查等待列表pxDelayedTaskList看是否有到期的、比当前执行的任务优先级更高的Task */{/* A context switch is required. Context switching is performed inthe PendSV interrupt. Pend the PendSV interrupt. */portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;}}portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); /* 已完成关键操作,放开中断屏蔽 */
}