微信公众号可以做微网站,seo排名推广,近一周新闻热点事件,淘宝上那些做网站seo的管用吗1.开启任务调度器
vTaskStartScheduler()
作用#xff1a;用于启动任务调度器#xff0c;任务调度器启动后#xff0c; FreeRTOS 便会开始进行任务调度【动态创建任务为例】
创建空闲任务如果使能软件定时器#xff0c;则创建定时器任务关闭中断#xff0c;防止调度器开…1.开启任务调度器
vTaskStartScheduler()
作用用于启动任务调度器任务调度器启动后 FreeRTOS 便会开始进行任务调度【动态创建任务为例】
创建空闲任务如果使能软件定时器则创建定时器任务关闭中断防止调度器开启之前或过程中受中断干扰会在运行第一个任务时打开中断初始化全局变量并将任务调度器的运行标志设置为已运行初始化任务运行时间统计功能的时基定时器 【可选】调用函数 xPortStartScheduler()
xPortStartScheduler()
作用该函数用于完成启动任务调度器中与硬件架构相关的配置部分以及启动第一个任务
检测用户在 FreeRTOSConfig.h 文件中对中断的相关配置是否有误配置 PendSV 和 SysTick 的中断优先级为最低优先级调用函数 vPortSetupTimerInterrupt()配置 SysTick清空计数值、配置节拍频率、重装载值、启动计数与中断初始化临界区嵌套计数器为 0调用函数 prvEnableVFP()使能 FPU将FPCCR寄存器的[31:30]置l这样在进出异常时FPU的相关寄存器就会自动地保存和恢复M4/M7调用函数prvStartFirstTask() 启动第一个任务
2.启动第一个任务
prvStartFirstTask()
__asm void prvStartFirstTask( void ) { /* 8字节对齐 */ PRESERVE8 ldr r0, 0xE000ED08 /* 0xE000ED08为VTOR地址 */ ldr r0, [ r0 ] /* 获取VTOR的值 */ ldr r0, [ r0 ] /* 获取MSP的初始值 */ /* 初始化MSP */ msr msp, r0/* 使能全局中断 */ cpsie i cpsie f dsb isb /* 调用SVC启动第一个任务 */ svc 0 nop nop
}执行过程为
获取MSP的初始值栈顶地址将MSP重新赋值为栈底指针让MSP回到原点启动任务一去不复返使能全局中断使用SVC指令传入系统调用信号出发SVC中断vPortSVCHandler () 关于MSP指针 程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时MCU 会自动更新 SP 指针ARM Cortex-M 内核提供了两个栈空间 主堆栈指针MSP它由 OS 内核、异常服务例程以及所有需要特权访问的应用程序代码来使用。 进程堆栈指针PSP用于常规的应用程序代码不处于异常服务例程中时。 在裸机中程序全部使用MSP在FreeRTOS中中断使用MSP主堆栈中断以外使用PSP进程堆栈关于0xE000ED08 0xE000ED08是VTOR中断向量表的地址向量表的第一个是 MSP 指针取 MSP 的初始值的思路是先根据向量表的位置寄存器 VTOR (0xE000ED08) 来获取向量表存储的地址在根据向量表存储的地址来访问第一个元素也就是初始的 MSP。 vPortSVCHandler ()
__asm void vPortSVCHandler( void )
{ /* 8字节对齐 */ PRESERVE8 /* 获取任务栈地址 */ ldr r3, pxCurrentTCB /* r3指向优先级最高的就绪态任务的任务控制块 */ ldr r1, [ r3 ] /* r1为任务控制块地址 */ ldr r0, [ r1 ] /* r0为任务控制块的第一个元素栈顶 */ /* 模拟出栈并设置PSP */ ldmia r0 !, { r4 - r11 } /* 任务栈弹出到CPU寄存器 */ msr psp, r0 /* 设置PSP为任务栈指针 */ isb /* 使能所有中断 */ mov r0, # 0 msr basepri, /* 使用PSP指针并跳转到任务函数 */ orr r14, # 0xd bx r14 }运行过程为
获取优先级最高的就绪任务的TCB并取其栈顶元素pxTopOfStack模拟出栈将寄存器值出栈至CPU寄存器并设置PSP指针开启中断线与0xd将r14设置为线程模式并使用PSP跳转到任务的任务函数中运行CPU自动出栈R0-xPSR等寄存器M4若EXC_RETURN使用FPU则恢复浮点单元 M4的vPortSVCHandler () 除了手动出栈r4-r11外还有r14这是因为M4等系列支持FPU需要该变量进行判别 M4的vPortSVCHandler () 不需要线与0xd因为在初始化时已经对EXC_RETURN进行赋值了不需要再线与 一般情况下使用动态创建任务第一个启动的任务是软件定时器任务 注意SVC中断只在启动第一次任务时会调用一次以后均不调用 开启任务调度器及启动第一个任务总结 3.任务切换
任务切换的本质就是CPU寄存器的切换又称上下文切换在PendSV中断服务函数中完成 主要分为两步
需暂停任务A的执行并将此时任务A的寄存器保存到任务堆栈这个过程叫做保存现场将任务B的各个寄存器值被存于任务堆栈中恢复到CPU寄存器中这个过程叫做恢复现场
触发PendSV中断方式
滴答定时器中断调用执行FreeRTOS提供的相关API函数portYIELD()本质通过向中断控制和状态寄存器 ICSR 的bit28 写入 1 挂起 PendSV 来启动 PendSV 中断 PendSV中断服务函数xPortPendSVHandler()
进入中断使用PSP自动压栈当前的psp是正在运行的任务的栈指针读取当前PSP进程指针存入r0M4还要考虑FPU压栈手动压栈并将最终结果封存至pxTopOfStack方便下次恢复屏蔽中断调用vTaskSeitchContext()获取当前最高优先级任务的任务控制块使能中断从最高优先级的TCB中获取pxTopOfStack并手动出栈更新切换后的任务的的栈指针给PSPPSP负责自动出栈bx r14 执行新任务函数
查找最优先级任务vTaskSwitchContext( )
通过这个函数完成taskSELECT_HIGHEST_PRIORITY_TASK( )
使用硬件方式本文使用使用软件方式 #define taskSELECT_HIGHEST_PRIORITY_TASK()
{UBaseType_t uxTopPriority;portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );configASSERT( listCURRENT_LIST_LENGTH( ( pxReadyTasksLists[ uxTopPriority ] ) ) 0);listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, ( pxReadyTasksLists[ uxTopPriority ] ) );
}前导置零指令 所谓的前导置零指令大家可以简单理解为计算一个 32位数头部 0 的个数。通过前导置零指令获得最高优先级
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )获取最高优先级任务的任务控制块 通过该函数获取当前最高优先级任务的任务控制块
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
{List_t * const pxConstList ( pxList );( pxConstList )-pxIndex ( pxConstList )-pxIndex-pxNext;if( ( void * ) ( pxConstList )-pxIndex ( void * ) ( ( pxConstList )-xListEnd ) ){(pxConstList )-pxIndex ( pxConstList )-pxIndex-pxNext;}( pxTCB ) ( pxConstList )-pxIndex-pvOwner;
}