news 2026/8/16 13:53:21

RTOS随笔之FreeRTOS第二篇

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RTOS随笔之FreeRTOS第二篇

RTOS启动与同步机制

  • RTOS启动
  • 任务切换场景
  • 任务同步机制
    • 队列
    • 信号量
    • 事件组
    • 任务通知
  • 任务延时

RTOS启动

FreeRTOS在任务创建完成后调用函数vTaskStartScheduler()启动任务调度器。
vTaskStartScheduler()任务启动函数详解

voidvTaskStartScheduler(void){BaseType_t xReturn;xReturn=xTaskCreate(prvIdleTask,"IDLE",configMINIMAL_STACK_SIZE,(void*)NULL,(tskIDLE_PRIORITY|portPRIVILEGE_BIT),&xIdleTaskHandle);//创建空闲任务,空闲任务优先级最低#if(configUSE_TIMERS==1)//是否使用软件定时器{if(xReturn==pdPASS){xReturn=xTimerCreateTimerTask();//创建软件定时器任务}else{mtCOVERAGE_TEST_MARKER();}}#endifif(xReturn==pdPASS){portDISABLE_INTERRUPTS();//关闭中断#if(configUSE_NEWLIB_REENTRANT==1){_impure_ptr=&(pxCurrentTCB->xNewLib_reent);}#endifxNextTaskUnblockTime=portMAX_DELAY;xSchedulerRunning=pdTRUE;//调度器开始运行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周期uxCriticalNesting=0;//初始化临界区嵌套计数prvEnableVFP();//使能FPU浮点运算*(portFPCCR)|=portASPEN_AND_LSPEN_BITS;//采用惰性压栈的特性自动保存寄存器/* 惰性压栈猜测:惰性压栈指的是异常产生时只将通用寄存器的值进行压栈,浮点寄存器值不压栈, 除非异常处理函数中存在浮点运算才会将浮点寄存器压栈。 具有浮点运算的处理器,总线错误和MemManage错误等异常可能会在惰性压栈期间产生。惰性压栈 特性推迟了浮点寄存器的压栈,只有当异常处理中存在浮点运算时才会将浮点寄存器的值先压栈, 后执行异常处理中的浮点运算。 */prvStartFirstTask();//在SVC异常中启动第一个任务return0;}

prvStartFirstTask()启动第一个任务函数详解

__asmvoidprvStartFirstTask(void){PRESERVE8 ldr r0,=0xE000ED08//向量表偏移寄存器VTOR的地址,存储栈顶地址,对该寄存器VTOR的值进行解引用获得栈顶地址ldr r0,[r0]//取R0地址处的值赋给R0,R0地址处的值是一个地址ldr r0,[r0]//取R0地址处的值赋给R0, R0地址处的值是栈顶msr msp,r0//复位栈顶MSPcpsie i//使能中断(清除PRIMASK)cpsie f//使能中断(清除FAULTMASK)dsb//数据同步屏障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寄存器出栈,栈顶移动36=4x9个字节,用户手动出栈msr psp,r0//设置进程栈指针,PSP=R0isb//指令同步屏障mov r0,#0//R0=0msr basepri,r0//basepri寄存器=0,开中断bx r14//硬件自动恢复R0~R3,R12,LR,PC,xPSP值,PC指向下一条指令}

任务切换场景

任务切换场景:
1.执行系统调用接口(含有taskYIELD()的API)
2.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_CM001==1push{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);

任务延时

vTaskDelay(n):进入vTaskDelay到退出间隔至少n个Tick中断
xTaskDelayUntil(&Pre, n):两次退出 xTaskDelayUntil 的时间至少是n个Tick中断,也就是任务运行时的Tick+Delay的Tick

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/16 13:52:46

震惊!各类考试适用的答题卡大揭秘,河北文瀚技术表现如何?

在各类考试、测评场景中,答题卡的质量至关重要,它不仅影响着考生填涂的体验,更关系到机器识别的准确性。为了帮助对答题卡质量有需求的人群更好地了解相关产品,本次对包括河北文瀚技术等厂家的答题卡进行了测评。本次参与测评的产…

作者头像 李华
网站建设 2026/8/16 13:38:55

商业模式画布:9个模块+6个模板,从0到1构建清晰商业逻辑

1. 从“一团乱麻”到“一张画布”:为什么你需要商业模式画布 如果你正在创业,或者在公司里负责一个新项目、新产品的规划,大概率经历过这样的场景:脑子里想法很多,跟团队讨论时七嘴八舌,每个人关注的点都不…

作者头像 李华
网站建设 2026/8/16 13:36:48

BIOS/UEFI完全指南:从开机进BIOS到核心设置与实战优化

1. 从“黑屏”到“蓝屏”:BIOS究竟是什么,为什么你绕不开它? 每次电脑开机,那个一闪而过的品牌Logo画面,对很多人来说,可能只是等待进入Windows或macOS前一个无关紧要的瞬间。但就在这短短几秒里&#xff0…

作者头像 李华
网站建设 2026/8/16 13:36:39

Android Studio无法识别设备:从驱动到ADB的完整排查指南

1. 问题场景:当Android Studio对设备“视而不见”时作为一名Android开发者,最让人血压飙升的场景之一,莫过于你信心满满地连接上手机,打开Android Studio,准备大展身手时,却发现设备列表里空空如也&#xf…

作者头像 李华