8.1 FreeRTOS 调度器开启过程分析
分析 过程 开启 调度 8.1 freeRTOS
2023-09-14 09:06:15 时间
8.1.1 任务调度器开启函数分析
main()函数中先创建一个开始任务 start_task,后面紧接着调用函数 vTaskStartScheduler(),在文件 tasks.c
中有定义。
void vTaskStartScheduler( void ) //开启任务调度器
{
BaseType_t xReturn;
/* Add the idle task at the lowest priority. */
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
//...
}
#else
{
/* The Idle task is being created using dynamically allocated RAM. */
//创建空闲任务,优先级为 tskIDLE_PRIORITY
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast
to all supported compilers. */
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask(); //创建定时器服务任务
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
if( xReturn == pdPASS )
{
/* Interrupts are turned off here, to ensure a tick does not occur
before or during the call to xPortStartScheduler(). The stacks of
the created tasks contain a status word with interrupts switched on
so interrupts will automatically get re-enabled when the first task
starts to run. */
portDISABLE_INTERRUPTS(); //关闭中断
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
/* Switch Newlib's _impure_ptr variable to point to the _reent
structure specific to the task that will run first. */
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif /* configUSE_NEWLIB_REENTRANT */
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE; //调度器开始运行
xTickCount = ( TickType_t ) 0U;
/* If configGENERATE_RUN_TIME_STATS is defined then the following
macro must be defined to configure the timer/counter used to generate
the run time counter time base. */
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); // 为1使能时间统计功能
/* Setting up the timer tick is hardware specific and thus in the
portable interface. */
if( xPortStartScheduler() != pdFALSE ) //初始化跟调度器启动有关的硬件
{
/* Should not reach here as if the scheduler is running the
function will not return. */
}
else
{
/* Should only reach here if a task calls xTaskEndScheduler(). */
}
}
else
{
/* This line will only be reached if the kernel could not be started,
because there was not enough FreeRTOS heap to create the idle task
or the timer task. */
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
}
/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
meaning xIdleTaskHandle is not used anywhere else. */
( void ) xIdleTaskHandle;
}
8.1.2 内核相关硬件初始化函数分析
FreeRTOS 系统时钟是由滴答定时器来提供,任务切换会用到 PendSV 中断,由函数 xPortStartScheduler()初始化。
在ARM_CM4F的port.c文件中。
/*
* See header file for description.
*/
BaseType_t xPortStartScheduler( void )
{
/* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY );
/* This port can be used on all revisions of the Cortex-M7 core other than
the r0p1 parts. r0p1 parts should use the port from the
/source/portable/GCC/ARM_CM7/r0p1 directory. */
configASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
configASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
#if( configASSERT_DEFINED == 1 )
{
//...
}
#endif /* conifgASSERT_DEFINED */
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; //设置 PendSV 为最低优先级
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; //设置滴答定时器为最低优先级
/* Start the timer that generates the tick ISR. Interrupts are disabled
here already. */
vPortSetupTimerInterrupt(); //设置滴答定时器的定时周期,使能滴答定时器的中断
/* Initialise the critical nesting count ready for the first task. */
uxCriticalNesting = 0; //初始化临界区嵌套计数器
/* Ensure the VFP is enabled - it should be anyway. */
prvEnableVFP(); //使能 VFP
/* Lazy save always. */
*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; //设置FPCCR bit31,bit30为1,异常入口和退出时会自动保存和恢复
/* Start the first task. */
prvStartFirstTask(); //启动第一个任务
/* Should not get here! */
return 0;
}
8.1.3 使能 FPU 函数分析
xPortStartScheduler()中会调用 prvEnableVFP()来使能 FPU,代码是汇编形式。
在ARM_CM4F的port.c文件中。
__asm void prvEnableVFP( void )
{
PRESERVE8
/* The FPU enable bits are in the CPACR. */
ldr.w r0, =0xE000ED88 //CPACR(bit20 ~ bit23)使能或禁止FPU
ldr r1, [r0] //读取CPACR 寄存器的值,保存在R1
/* Enable CP10 and CP11 coprocessors, then save back. */
orr r1, r1, #( 0xf << 20 ) //位或运算,bit20~bit23设为1
str r1, [r0] //R1的值写入CPACR开启FPU
bx r14 //bx 为间接跳转指令,跳转到链接寄存(LR),LR用函数或子程序调用时返回地址的保存
nop
}
8.1.4 启动第一个任务
prvStartFirstTask()启动第一个任务,代码是汇编。
在ARM_CM4F的port.c文件中。
__asm void prvStartFirstTask( void )
{
PRESERVE8
/* Use the NVIC offset register to locate the stack. */
ldr r0, =0xE000ED08 //向量表偏移寄存器(VTOR)为0XE000ED08 保存在R0
ldr r0, [r0] //读取VTOR的值保存R0
ldr r0, [r0] //读取0X08000000的值保存R0,R0 存 MSP 的初始值
/* Set the msp back to the start of the stack. */
msr msp, r0 //复位 MSP
/* Globally enable interrupts. */
cpsie i //使能中断(清除 PRIMASK)
cpsie f //使能中断(清除 FAULTMASK)
dsb //数据同步屏障
isb //指令同步屏障
/* Call SVC to start the first task. */
svc 0 //触发请求管理调用(SVC) 中断,SVC 异常由 SVC 指令触发,启动第一个任务
nop
nop
}
8.1.5 SVC 中断服务函数
prvStartFirstTask()调用 SVC 指令触发 SVC 中断,第一个任务的启动是在 SVC 中断服务函数中完成的, SVC 中断函数为 SVC_Handler()。
FreeRTOS Config.h 中通过#define 的方式重新定义为了 xPortPendSVHandler()。
#define xPortPendSVHandler PendSV_Handler
在ARM_CM4F的port.c文件中。代码是汇编。
__asm void vPortSVCHandler( void )
{
PRESERVE8
/* Get the location of the current TCB. */
ldr r3, =pxCurrentTCB //获取pxCurrentTCB指针的值地址,指向 TCB_t 的指针,永远指向正在运行的任务
ldr r1, [r3] //获取当前任务的任务控制块的值地址
ldr r0, [r1] //任务控制块的第一个字段是任务堆栈的栈顶指针 pxTopOfStack 所指向的位置
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14} //LDMIA 指令是多加载/存储指令
msr psp, r0 //设置进程栈指针(PSP)为任务的堆栈
isb //指令同步屏障
mov r0, #0 //R0 为 0
msr basepri, r0 // BASEPRI 为 R0,打开中断
bx r14 //堆栈使用进程栈 PSP,任务调度器开始运行
}
8.1.6 空闲任务
空闲任务是空闲的时候运行的任务,空闲任务是 FreeRTOS 系统自动创建,空闲任务的任务优先级是最低的,为 0。
空闲任务
- 判断系统是否有任务删除
- 运行用户设置的空闲任务钩子函数
- 判断是否开启低功耗 tickless 模式
相关文章
- MongoDB索引顺序导致慢SQL分析过程
- 记一次bug分析过程,并随之引发的思考
- 2021中国大学排名分析数据分析项目
- R语言和Python用泊松过程扩展:霍克斯过程Hawkes Processes分析比特币交易数据订单到达自激过程时间序列|附代码数据
- 跟着Nature Biotechnology学作图:R语言pca分析并使用ggplot2可视化结果
- 【ES三周年】ELK日志分析平台 | 记一次基于鲲鹏麒麟操作系统分析平台搭建过程
- MySQL源码分析之SQL函数执行
- 5. 「snabbdom@3.5.1 源码分析」Thunks 函数
- 【Android 逆向】整体加固脱壳 ( 脱壳起点 : 整体加固脱壳 | Dalvik 脱壳机制 : 利用 DexClassLoader 加载过程进行脱壳 | 相关源码分析 )
- 从进货、制造到出货所有的过程都需要进行PFMEA分析吗?
- ORACLE 段 segement 的HEADER_BLOCK结构分析详解
- Hive学习之路 (十七)Hive分析窗口函数(五) GROUPING SETS、GROUPING__ID、CUBE和ROLLUP详解大数据
- word2vec 源码分析word2vec.c详解大数据
- 『Oracle存储过程跟踪分析』(oracle跟踪存储过程)
- Wireshark系列之3 路由过程抓包分析
- Wireshark系列之2 路由过程理论分析
- 分析SQL Server 差值分析技术探索(sqlserver 差值)
- Oracle实现亿级数据的快速分析(oracle亿级数据分析)
- Oracle1一对多关系执行错误分析(oracle1对多报错)
- ASP下操作Excel技术总结分析
- MySQL性能分析及explain的使用说明
- 使用Log4j为项目配置日志输出应用详解以及示例演示的实现分析
- 基于mysql事务、视图、存储过程、触发器的应用分析
- 通过Androidtrace文件分析死锁ANR实例过程
- php调用mysql存储过程实例分析
- c++11新增的便利算法实例分析