RTOS启动与同步机制RTOS启动任务切换场景任务同步机制队列信号量事件组任务通知任务延时RTOS启动FreeRTOS在任务创建完成后调用函数vTaskStartScheduler()启动任务调度器。vTaskStartScheduler()任务启动函数详解voidvTaskStartScheduler(void){BaseType_t xReturn;xReturnxTaskCreate(prvIdleTask,IDLE,configMINIMAL_STACK_SIZE,(void*)NULL,(tskIDLE_PRIORITY|portPRIVILEGE_BIT),xIdleTaskHandle);//创建空闲任务空闲任务优先级最低#if(configUSE_TIMERS1)//是否使用软件定时器{if(xReturnpdPASS){xReturnxTimerCreateTimerTask();//创建软件定时器任务}else{mtCOVERAGE_TEST_MARKER();}}#endifif(xReturnpdPASS){portDISABLE_INTERRUPTS();//关闭中断#if(configUSE_NEWLIB_REENTRANT1){_impure_ptr(pxCurrentTCB-xNewLib_reent);}#endifxNextTaskUnblockTimeportMAX_DELAY;xSchedulerRunningpdTRUE;//调度器开始运行xTickCount(TickType_t)0U;portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();//实现任务统计功能具体由用户实现if(xPortStartScheduler()!pdFALSE)//初始化任务硬件相关的功能Systick/PendSV/FPU{//如果调度器启动成功xPortStartScheduler()没有返回值不会运行到这里}else{//不会运行到这里除非调用xTaskEndScheduler()}}else{//创建空闲任务或定时器任务失败内存不足configASSERT(xReturn!errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY);}//防止编译器报错INCLUDE_xTaskGetIdleTaskHandle定义为0,编译器会提示(void)xIdleTaskHandle;}xPortStartScheduler()任务硬件相关函数详解BaseType_txPortStartScheduler(void){portNVIC_SYSPRI2_REG|portNVIC_PENDSV_PRI;//设置PendSV中断为最低优先级portNVIC_SYSPRI2_REG|portNVIC_SYSTICK_PRI;//设置Systick中断为最低优先级vPortSetupTimerInterrupt();//设置Systick周期uxCriticalNesting0;//初始化临界区嵌套计数prvEnableVFP();//使能FPU浮点运算*(portFPCCR)|portASPEN_AND_LSPEN_BITS;//采用惰性压栈的特性自动保存寄存器/* 惰性压栈猜测惰性压栈指的是异常产生时只将通用寄存器的值进行压栈浮点寄存器值不压栈 除非异常处理函数中存在浮点运算才会将浮点寄存器压栈。 具有浮点运算的处理器总线错误和MemManage错误等异常可能会在惰性压栈期间产生。惰性压栈 特性推迟了浮点寄存器的压栈只有当异常处理中存在浮点运算时才会将浮点寄存器的值先压栈 后执行异常处理中的浮点运算。 */prvStartFirstTask();//在SVC异常中启动第一个任务return0;}prvStartFirstTask()启动第一个任务函数详解__asmvoidprvStartFirstTask(void){PRESERVE8 ldr r0,0xE000ED08//向量表偏移寄存器VTOR的地址存储栈顶地址对该寄存器VTOR的值进行解引用获得栈顶地址ldr r0,[r0]//取R0地址处的值赋给R0R0地址处的值是一个地址ldr r0,[r0]//取R0地址处的值赋给R0, R0地址处的值是栈顶msr msp,r0//复位栈顶MSPcpsie i//使能中断清除PRIMASKcpsie f//使能中断清除FAULTMASKdsb//数据同步屏障isb//指令同步屏障svc0//触发SVC异常nop nop}vPortSVCHandler()SVC异常处理函数详解__asmvoidvPortSVCHandler(void){PRESERVE8 ldr r3,pxCurrentTCB//获取pxCurrentTCB指针的地址赋值R3ldr r1,[r3]//获取pxCurrentTCB指针指向的值赋值R1ldr r0,[r1]//获取R1地址处的值赋值给R0,获取任务栈栈顶指针即任务控制块的第一个数据就是任务栈顶指针ldmia r0!,{r4-r11,r14}//R4~R11,R14寄存器出栈栈顶移动364x9个字节用户手动出栈msr psp,r0//设置进程栈指针PSPR0isb//指令同步屏障mov r0,#0//R00msr basepri,r0//basepri寄存器0开中断bx r14//硬件自动恢复R0~R3,R12,LR,PC,xPSP值PC指向下一条指令}任务切换场景任务切换场景1.执行系统调用接口含有taskYIELD()的API2.Systick定时器中断任务在哪里切换任务在PendSV的异常中切换__asmvoidxPortPendSVHandler(void){externuxCriticalNesting;externpxCurrentTCB;externvTaskSwitchContext;PRESERVE8 mrs r0,psp//读取进程栈指针保存在R0寄存器中isb ldr r3,pxCurrentTCB//获取当前任务控制块的指针ldr r2,[r3]//将当前任务控制块的地址保存在R2寄存器中tst r14,#0x10it eq//判断是否使用FPU使用的话在进行任务切换的时候要手动保存FPU寄存器到任务栈中vstmdbeq r0!,{s16-s31}//保存S16~S31这16个FPU寄存器stmdb r0!,{r4-r11,r14}//保存R4~R11,R14寄存器str r0,[r2]//将R0的值保存到R2所保存的地址中去R2保存当前任务控制块的地址等于更新任务块栈顶指针stmdb sp!,{r3}//将R3的值入栈mov r0,#configMAX_SYSCALL_INTERRUPT_PRIORITY msr basepri,r0//关闭中断进入临界区dsb isb bl vTaskSwitchContext//调用vTaskSwitchContext()获取下一个运行的任务mov r0,#0msr basepri,r0//打开中断退出临界区ldmia sp!,{r3}//R3的值出栈新的任务控制块ldr r1,[r3]ldr r0,[r1]//获取新的任务的栈顶指针保存到R0中ldmia r0!,{r4-r11,r14}//R4~R11,R14出栈tst r14,#0x10it eq vldmiaeq r0!,{s16-s31}//判断当前任务是否使用FPU,是的话手动恢复FPU寄存器msr psp,r0//跟新进程栈指针isb#ifdefWORKAROUND_PMU_CM001/* XMC4000 specific errata */#ifWORKAROUND_PMU_CM0011push{r14}pop{pc}nop#endif#endifbx r14/* 硬件自动恢复R0~R3,R12,LR,PC,xPSP值,确定异常返回后进入处理器模式还是进程模式 使用主栈指针MSP还是进程栈指针PSP。后续进入进程模式使用进程栈指针PC的值指向 新任务的函数新任务开始运行 */}任务同步机制队列信号量二值/计数/互斥事件组任务通知消息邮箱队列队列传输数据的两种方式拷贝把数据复制进队列引用把数据地址复制进队列数据要做保护多任务发送队列怎么区别数据来源结构体ID数据形式位拼接高位表示ID,低位表示数据QueueHandle_txQueueCreate(UBaseType_t uxQueueLength,UBaseType_t uxItemSize);BaseType_txQueueReset(QueueHandle_t pxQueue);voidvQueueDelete(QueueHandle_t xQueue);BaseType_txQueueSend(QueueHandle_t xQueue,constvoid*pvItemToQueue,TickType_t xTicksToWait);BaseType_txQueueSendToBackFromISR(QueueHandle_t xQueue,constvoid*pvItemToQueue,BaseType_t*pxHigherPriorityTaskWoken);BaseType_txQueueReceive(QueueHandle_t xQueue,void*constpvBuffer,TickType_t xTicksToWait);BaseType_txQueueReceiveFromISR(QueueHandle_t xQueue,void*pvBuffer,BaseType_t*pxTaskWoken);UBaseType_tuxQueueMessagesWaiting(constQueueHandle_t xQueue);//返回队列已有数据空间个数UBaseType_tuxQueueSpacesAvailable(constQueueHandle_t xQueue);//返回队列剩余数据空间个数BaseType_txQueueOverwrite(QueueHandle_t xQueue,constvoid*pvItemToQueue);//队列覆盖写BaseType_txQueueOverwriteFromISR(QueueHandle_t xQueue,constvoid*pvItemToQueue,BaseType_t*pxHigherPriorityTaskWoken);BaseType_txQueuePeek(QueueHandle_t xQueue,void*constpvBuffer,TickType_t xTicksToWait);//复制队列中的数据不删除数据队列中无数据时阻塞BaseType_txQueuePeekFromISR(QueueHandle_t xQueue,void*pvBuffer,);信号量通知作用计数型信号信号初值自定义发送加1接收减1二进制信号信号初值是0发送加1接收减1互斥信号防止同时访问共享资源共享资源全局变量静态变量公共函数互斥信号禁止中断中使用SemaphoreHandle_txSemaphoreCreateCounting(UBaseType_t uxMaxCount,UBaseType_t uxInitialCount);SemaphoreHandle_txSemaphoreCreateBinary(void);SemaphoreHandle_txSemaphoreCreateMutex(void);//互斥锁他人也能解锁SemaphoreHandle_txSemaphoreCreateRecursiveMutex(void);//递归锁自己上锁自己解锁他人无法解锁voidvSemaphoreDelete(SemaphoreHandle_t xSemaphore);BaseType_txSemaphoreGive(SemaphoreHandle_t xSemaphore);BaseType_txSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t*pxHigherPriorityTaskWoken);BaseType_txSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);BaseType_txSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t*pxHigherPriorityTaskWoken);事件组事件发生广播所有任务事件可以与或组合多任务可以同步EventGroupHandle_txEventGroupCreate(void);voidvEventGroupDelete(EventGroupHandle_t xEventGroup);EventBits_txEventGroupSetBits(EventGroupHandle_t xEventGroup,constEventBits_t uxBitsToSet);EventBits_txEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,constEventBits_t uxBitsToSet,BaseType_t*pxHigherPriorityTaskWoken);EventBits_txEventGroupWaitBits(EventGroupHandle_t xEventGroup,constEventBits_t uxBitsToWaitFor,constBaseType_t xClearOnExit,constBaseType_t xWaitForAllBits,TickType_t xTicksToWait);EventBits_txEventGroupSync(EventGroupHandle_t xEventGroup,constEventBits_t uxBitsToSet,constEventBits_t uxBitsToWaitFor,TickType_t xTicksToWait);任务通知明确通知的任务数据任务独享不额外开辟内存效率更高中断中只能发送数据给任务/*任务通知计数功能函数*/BaseType_txTaskNotifyGive(TaskHandle_t xTaskToNotify);voidvTaskNotifyGiveFromISR(TaskHandle_t xTaskHandle,BaseType_t*pxHigherPriorityTaskWoken);uint32_tulTaskNotifyTake(BaseType_t xClearCountOnExit,TickType_t xTicksToWait);/*任务通知复杂功能函数*/BaseType_txTaskNotify(TaskHandle_t xTaskToNotify,uint32_tulValue,eNotifyActioneAction);BaseType_txTaskNotifyFromISR(TaskHandle_t xTaskToNotify,uint32_tulValue,eNotifyAction eAction,BaseType_t*pxHigherPriorityTaskWoken);BaseType_txTaskNotifyWait(uint32_tulBitsToClearOnEntry,uint32_tulBitsToClearOnExit,uint32_t*pulNotificationValue,TickType_t xTicksToWait);任务延时vTaskDelayn进入vTaskDelay到退出间隔至少n个Tick中断xTaskDelayUntilPre, n两次退出 xTaskDelayUntil 的时间至少是n个Tick中断也就是任务运行时的TickDelay的Tick