0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式4-freertos

jf_49463572 来源:27熊熊嵌入式 作者:27熊熊嵌入式 2026-05-07 15:43 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

▼关注公众号:27熊熊嵌入式

嵌入式开发领域,RTOS(实时操作系统)应用广泛——无人机飞行控制、汽车电子ECU、医疗设备、智能家居、智能手表,背后都有它的身影。

很多开发者入门RTOS时,会先接触FreeRTOSRT-Thread等内核,却容易陷入“会用API,不懂原理”的困境:为什么它能保证实时响应?多任务如何“同时”运行?上下文切换到底在做什么?

先搞懂:RTOS内核到底是什么?

核心认知:RTOS的核心是“内核”,它不是完整的操作系统(无图形界面、无复杂文件系统),而是一套轻量级任务调度与资源管理框架——相当于嵌入式系统的“大脑”,负责分配CPU资源、调度任务执行、管理系统资源,核心使命是在严格时间约束下,确定性响应外部事件

和Windows、Linux等通用操作系统相比,RTOS内核有三个核心优势:

轻量性:代码体积通常低于10KB,ROM/RAM占用可控,支持裁剪,适配单片机等资源有限硬件(典型任务栈仅需0.5KB~4KB);

实时性:分为硬实时和软实时,硬实时要求任务必须在截止期限前完成(否则系统失效,如汽车ABS制动),软实时允许偶发超时(如音视频同步);

确定性:任务从就绪到执行的最大延迟(最坏情况响应时间)可计算、可验证,这是通用操作系统无法实现的核心特性。

核心拆解1:任务管理——RTOS的“最小执行单元”

RTOS的核心能力,是将复杂系统功能拆分为多个独立任务,每个任务是可独立运行、被调度的最小单元。

任务的3个核心组成(缺一不可)

每个可运行的RTOS任务,包含三个核心部分:

任务函数:核心执行逻辑,通常为无限循环(for(;;)或while(1)),避免任务执行完毕后进入未知状态,“传感器数据采集”“串口指令处理”等业务逻辑均在此编写;

任务堆栈:每个任务的独立工作空间,用于保存上下文数据、局部变量、函数调用返回地址,记录任务运行状态;

任务控制块(TCB):RTOS管理任务的数据结构,包含任务优先级、运行状态、堆栈指针、任务句柄等信息,内核通过TCB定位和管理任务。

FreeRTOS核心代码分析(任务管理)

FreeRTOS中,任务控制块(TCB)的核心定义代码如下(简化版,去除冗余配置),对应上述任务核心组成:

// FreeRTOS任务控制块(TCB)完整定义(来自 tasks.c)typedefstructtskTaskControlBlock{ volatileStackType_t *pxTopOfStack;     // 当前栈顶指针(任务切换时保存/恢复)  ListItem_t xStateListItem;         // 任务状态链表节点(挂载到就绪/阻塞/挂起链表)  ListItem_t xEventListItem;         // 事件链表节点(用于信号量、队列等待)  UBaseType_t uxPriority;           // 任务优先级(数值越大优先级越高)  StackType_t *pxStack;            // 任务堆栈起始地址 charpcTaskName[ configMAX_TASK_NAME_LEN ]; // 任务名称(便于调试) // ... 其他可选成员(如栈溢出检测标记、任务通知值等)} tskTCB;

创建任务的核心API(xTaskCreate),本质是初始化TCB、分配任务堆栈、将任务加入就绪链表

源码:

#if( configSUPPORT_DYNAMIC_ALLOCATION ==1) BaseType_txTaskCreate( TaskFunction_t pxTaskCode,             constchar*constpcName,             constconfigSTACK_DEPTH_TYPE usStackDepth,             void*constpvParameters,              UBaseType_t uxPriority,              TaskHandle_t *constpxCreatedTask )  {    TCB_t *pxNewTCB;     // 新任务控制块指针    BaseType_t xReturn;    // 返回状态   /* 根据堆栈生长方向,决定先分配TCB还是堆栈,避免堆栈覆盖TCB */   #if( portSTACK_GROWTH >0) /* 堆栈向上生长:先TCB后堆栈 */      pxNewTCB = ( TCB_t * )pvPortMalloc(sizeof( TCB_t ) );     if( pxNewTCB !=NULL)      {        pxNewTCB->pxStack = ( StackType_t * )pvPortMallocStack( ( ( (size_t) usStackDepth ) *sizeof( StackType_t ) ) );       if( pxNewTCB->pxStack ==NULL)        {         vPortFree( pxNewTCB );          pxNewTCB =NULL;        }      }   #else/* portSTACK_GROWTH <= 0 — 堆栈向下生长:先堆栈后TCB */            {                StackType_t *pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );                if( pxStack != NULL )                {                    pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );                    if( pxNewTCB != NULL )                    {                        pxNewTCB->pxStack = pxStack; // 将堆栈地址存入TCB          }         else          {           vPortFreeStack( pxStack );          }        }       else        {          pxNewTCB =NULL;        }      }   #endif/* portSTACK_GROWTH */   if( pxNewTCB !=NULL)    {     /* 标记任务为动态分配(便于后续删除时释放内存) */     #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE !=0)        pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;     #endif     /* 初始化任务TCB:任务名、优先级、堆栈布局、入口函数等 */     prvInitialiseNewTask( pxTaskCode, pcName, (uint32_t) usStackDepth,                 pvParameters, uxPriority, pxCreatedTask, pxNewTCB,NULL);     /* 将任务加入就绪链表(根据优先级挂载到对应的 pxReadyTasksLists[ ]) */     prvAddNewTaskToReadyList( pxNewTCB );      xReturn = pdPASS;    }   else    {      xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;    }   returnxReturn;  }#endif/* configSUPPORT_DYNAMIC_ALLOCATION */

关键说明:FreeRTOS中,所有就绪任务按优先级挂载到对应就绪链表,调度器从该链表选择最高优先级任务执行,这是抢占式调度的基础。

任务的4种核心状态(动态切换)

任务并非一直处于运行状态,而是在4种状态间动态切换,这是理解调度器的基础:

运行态:任务占用CPU,执行核心逻辑(单核CPU同一时刻,仅一个任务处于运行态);

就绪态:任务准备就绪,具备运行条件,但CPU被更高优先级任务占用,无法运行;

阻塞态:任务因等待事件(延时、信号量、消息队列)暂停,即使CPU空闲也无法运行(如任务延时1秒,期间处于阻塞态);

挂起态:任务被手动暂停(通过RTOS API调用),即使等待事件满足,也无法进入就绪态,需手动唤醒。

示例:温湿度采集任务每隔1秒采集一次数据——采集完成后进入阻塞态(等待1秒);1秒延时结束后进入就绪态;无更高优先级任务时,调度器让其进入运行态,开始下一次采集。

核心拆解2:调度器

调度器负责决定“哪个任务优先占用CPU”“何时切换任务”,核心目标是保证高优先级任务优先执行,确保系统实时性

主流RTOS(FreeRTOS、RT-Thread)采用“抢占式+时间片轮转”混合调度策略,兼顾实时性和公平性:

1. 抢占式调度

这是RTOS实现实时性的关键——更高优先级任务从阻塞态变为就绪态时,调度器立即暂停当前运行的低优先级任务,切换到高优先级任务执行,无需等待低优先级任务完成。

示例:故障报警任务(高优先级)和日志记录任务(低优先级)——日志任务正常运行,检测到故障后,报警任务进入就绪态,调度器立即暂停日志任务,优先执行报警任务,确保故障及时响应。

2. 时间片轮转调度(核心:同优先级任务公平执行)

多个任务优先级相同时,调度器给每个任务分配时间片(如10ms),任务执行完时间片后,主动放弃CPU,切换到下一个同优先级任务,实现“看似同时运行”的效果。

示例:两个同优先级的数据流显示任务和串口接收任务,时间片设为10ms——两任务轮流占用CPU,各执行10ms,从用户角度看,两个功能同步进行。

调度器的调度时机固定,主要在两个场景:一是任务主动放弃CPU(延时、等待事件);二是中断服务程序执行完毕后,调度器检查是否有更高优先级任务就绪,有则触发任务切换。

FreeRTOS核心代码分析(调度器)

FreeRTOS调度器核心函数是vTaskSwitchContext(),作用是“选择下一个执行任务”,核心逻辑是从就绪链表找到最高优先级任务


voidvTaskSwitchContext(void){ if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )  {   /* 调度器挂起,暂不切换,仅标记需要挂起的任务切换 */    xYieldPending = pdTRUE;  } else  {    xYieldPending = pdFALSE;    traceTASK_SWITCHED_OUT();   #if( configGENERATE_RUN_TIME_STATS ==1)     /* 记录当前任务的运行时间统计(略) */     // ......   #endif   /* 栈溢出检测(若使能) */    taskCHECK_FOR_STACK_OVERFLOW();   /* 选择下一个最高优先级的就绪任务 */    taskSELECT_HIGHEST_PRIORITY_TASK(); /* 核心宏:遍历就绪链表,更新 pxCurrentTCB */    traceTASK_SWITCHED_IN();   /* 更新 Newlib 可重入结构(若使能) */   #if( configUSE_NEWLIB_REENTRANT ==1)      _impure_ptr = &( pxCurrentTCB->xNewLib_reent );   #endif  }}

补充说明:FreeRTOS中,时间片轮转依赖系统滴答定时器(SysTick)中断,每次中断检查当前任务时间片是否用完,用完则触发调度器切换任务,核心代码在xPortSysTickHandler()中,简化如下:

voidxPortSysTickHandler(void){ /* 进入临界区:提高 BASEPRI 以屏蔽部分中断 */ vPortRaiseBASEPRI();  {   /* 增加系统节拍计数,并检查是否需要触发 PendSV 进行任务切换 */   if(xTaskIncrementTick() != pdFALSE )    {     /* 触发 PendSV 异常,在最低优先级执行实际上下文切换 */      portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;    }  } vPortClearBASEPRIFromISR();}BaseType_txTaskIncrementTick(void){  TCB_t * pxTCB;  TickType_t xItemValue;  BaseType_t xSwitchRequired = pdFALSE; /* 如果调度器未挂起,正常处理节拍 */ if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )  {   constTickType_t xConstTickCount = xTickCount + ( TickType_t )1;    xTickCount = xConstTickCount;   /* 节拍溢出处理:交换延迟链表和溢出延迟链表 */   if( xConstTickCount == ( TickType_t )0U)    {     taskSWITCH_DELAYED_LISTS();    }   /* 检查是否有任务因延时或超时而解除阻塞 */   if( xConstTickCount >= xNextTaskUnblockTime )    {     for( ;; )      {       if(listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )        {          xNextTaskUnblockTime = portMAX_DELAY;         break;        }       else        {          pxTCB =listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );          xItemValue =listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );         if( xConstTickCount < xItemValue )                    {                        xNextTaskUnblockTime = xItemValue;                        break;                    }                    /* 将任务从延迟链表中移除,并加入就绪链表 */                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );         if(listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) !=NULL)          {           listREMOVE_ITEM( &( pxTCB->xEventListItem ) );          }         prvAddTaskToReadyList( pxTCB );         #if( configUSE_PREEMPTION ==1)           /* 如果解除阻塞的任务优先级 >= 当前任务,则标记需要切换 */           if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )            {              xSwitchRequired = pdTRUE;            }         #endif        }      }    }   /* 时间片轮转:同优先级就绪任务数 > 1 则触发切换 */   #if( ( configUSE_PREEMPTION ==1) && ( configUSE_TIME_SLICING ==1) )     if(listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t )1)      {        xSwitchRequired = pdTRUE;      }   #endif   /* 处理挂起的任务切换请求 */   #if( configUSE_PREEMPTION ==1)     if( xYieldPending != pdFALSE )      {        xSwitchRequired = pdTRUE;      }   #endif  } else  {   /* 调度器挂起时,累加挂起的节拍数,稍后补处理 */    ++xPendedTicks;  } returnxSwitchRequired;}

调度器切换任务时,关键操作是上下文切换

上下文,即任务运行时的全部状态,包括CPU寄存器(PC、SP、通用寄存器)、任务堆栈数据等——即任务“当前运行到哪一步”的所有信息。

上下文切换过程分两步,由内核自动完成,无需开发者干预:

保存上下文:任务被暂停时,内核将其上下文信息(寄存器、堆栈数据)保存到自身任务堆栈,即“保存工作进度”;

恢复上下文:任务再次被调度时,内核从其堆栈中恢复上下文信息到CPU寄存器,即“恢复工作进度”,任务从暂停处继续执行。

FreeRTOS核心代码分析(上下文切换)

FreeRTOS上下文切换分“保存当前任务上下文”和“恢复下一个任务上下文”,核心代码为汇编实现,简化后如下:

__asmvoidxPortPendSVHandler(void){ externuxCriticalNesting; externpxCurrentTCB; externvTaskSwitchContext;  PRESERVE8 /* 获取当前任务的进程栈指针 (PSP) */  mrs r0, psp  isb /* 获取当前任务控制块的指针 */  ldr r3, =pxCurrentTCB  ldr r2, [r3] /* 如果使用了 FPU,保存 FPU 高寄存器 */  tst r14,#0x10  it eq  vstmdbeq r0!, {s16-s31} /* 保存核心寄存器 (r4-r11 以及 r14) */  stmdb r0!, {r4-r11, r14} /* 将新的栈顶指针保存到 TCB 的第一个成员 */  str r0, [r2] /* 屏蔽中断,调用调度器选择下一个任务 */  stmdb sp!, {r0, r3}  mov r0,#configMAX_SYSCALL_INTERRUPT_PRIORITY  msr basepri, r0  dsb  isb  bl vTaskSwitchContext  mov r0,#0  msr basepri, r0  ldmia sp!, {r0, r3} /* 获取新任务的 TCB 和栈顶指针 */  ldr r1, [r3]  ldr r0, [r1] /* 恢复新任务的寄存器 */  ldmia r0!, {r4-r11, r14} /* 恢复 FPU 寄存器(如果之前保存过) */  tst r14,#0x10  it eq  vldmiaeq r0!, {s16-s31} /* 更新 PSP 并异常返回 */  msr psp, r0  isb  bx r14}

补充:不同RTOS内核(FreeRTOS、RT-Thread、uC/OS)实现细节有差异(如优先级数值规则:FreeRTOS数值越大优先级越高,uC/OS则相反),但核心原理一致——掌握这些逻辑,切换任何RTOS都能快速上手。

文章参考:

FreeRTOS源码仓库(可下载完整源码,对照本文代码分析学习):https://github.com/FreeRTOS/FreeRTOS-Kernel

FreeRTOS中文官方文档(适配国内开发者,简化易懂):https://www.freertos.org/(含内核原理、任务管理、调度器等核心模块详解)

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5210

    文章

    20692

    浏览量

    337601
  • FreeRTOS
    +关注

    关注

    14

    文章

    501

    浏览量

    67316
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    飞凌嵌入式亮相德国纽伦堡embedded world 2026,深耕全球嵌入式AI市场!

    2026年3月10日,全球嵌入式技术领域的年度盛会——embedded world 2026国际嵌入式展览会,在德国纽伦堡会展中心盛大启幕。飞凌嵌入式携多款重磅新品及全场景解决方案再一次隆重参展
    的头像 发表于 03-11 16:37 1.1w次阅读
    飞凌<b class='flag-5'>嵌入式</b>亮相德国纽伦堡embedded world 2026,深耕全球<b class='flag-5'>嵌入式</b>AI市场!

    嵌入式单片机开发学习路径

    项目 通过完成一些简单的项目来巩固所学知识。例如,制作一个简单的LED闪烁电路或温度传感器测量系统。 4. 高级进阶 4.1 操作系统学习 学习嵌入式操作系统(如FreeRTOS),理解多任务
    发表于 02-09 15:42

    STM32开发中的五大嵌入式系统

    STM32开发中常见的嵌入式系统有:FReeRTOS、μC/OS-II和μC/OS-II、RT-Thread、 eCos、uClinux,下面我们一起看下这五种嵌入式系统的的特点: FreeR
    发表于 01-21 10:48

    什么是嵌入式应用开发?

    概述 所谓的嵌入式应用开发就是在嵌入式操作系统下进行开发、软硬件综合开发 ‌嵌入式应用开发‌是指在嵌入式操作系统下进行开发,包括系统化设计指导下的硬件和软件综合研发。
    发表于 01-12 16:13

    系统嵌入式的学习路线

    嵌入式技术是各种电子产品的核心技术,也是工业4.0、远程医疗、3D打印等新兴产业的核心技术,具有广阔的发展前景。很多计算机、电子信息类专业的学生都想把嵌入式开发作为自己的职业目标,但是因为嵌入式涉及
    发表于 12-16 07:49

    什么是嵌入式操作系统?

    FreeRTOS/UCOS?,核心差异在资源适配和实时性: 举个嵌入式开发中的实际例子: 用 STM32F4 做一个智能小车,需要同时做 3 件事:① 读取超声波传感器数据(避障)、② 接收蓝牙
    发表于 12-09 10:33

    分享一个嵌入式开发学习路线

    拓展期(3-4个月) 学习嵌入式操作系统(RTOS)和物联网通信技术,能开发“多任务、联网”的复杂项目,理解企业级嵌入式开发的“架构思维”。这一阶段的学习需要一定的自律和毅力,但与学历无关。
    发表于 12-04 11:01

    CW32嵌入式软件开发的必备知识

    的数据手册及用户手册,查找所需外设工作原理。 4嵌入式操作系统(深入知识点) 掌握常用的嵌入式操作系统,如μC/OS、FREERTOS等,了解其内核、进程管理、内存管理、设备管理和
    发表于 11-28 07:48

    嵌入式实时操作系统的特点

    。 低功耗和小尺寸:实时嵌入式操作系统通常要求运行时的资源占用较少,以适应嵌入式系统对功耗和尺寸的限制。 常见的实时嵌入式操作系统包括 FreeRTOS、ThreadX、Micriu
    发表于 11-13 06:30

    嵌入式需要掌握哪些核心技能?

    /网络设备)、中断底半部处理,适用于高端嵌入式设备。 企业需求:RTOS是60%岗位的核心要求,Linux开发需求随项目复杂度增长。 4)通信协议与接口 基础协议:UART、SPI、I2C、CAN总线
    发表于 10-21 16:25

    嵌入式达到什么水平才能就业?

    、LoRa、NB-IoT,能实现嵌入式设备与云端平台的数据交互了解 RTOS 实时操作系统:如 FreeRTOS、RT-Thread,能进行任务创建、信号量管理、内存分配
    发表于 09-15 10:20

    嵌入式从入门到进阶,怎么学?

    嵌入式从入门到进阶,怎么学? 嵌入式学习的核心是 “软硬结合的技术壁垒”,科学分层才能高效突破。以下是从入门到高阶的精简路线,帮你避开弯路: 1、基础奠基层:构建技术底座 C 语言聚焦 指针、结构体
    发表于 09-02 09:44

    Linux嵌入式和单片机嵌入式的区别?

    ,开发工具包括GCC、Makefile等。 3.操作系统 : 单片机嵌入式 :一般不使用完整的操作系统,或者使用简单的实时操作系统(RTOS),如FreeRTOS。 Linux嵌入式
    发表于 06-20 09:46

    OPENRTOS为FreeRTOS提供商业许可证

    嵌入式操作系统是嵌入式系统的基石,是工业软件的基础。在市场占有率上,Eclipse基金会2024年物联网开发者调查表明,资源受限设备上的开发人员使用的嵌入式操作系统系统中FreeRTOS
    的头像 发表于 06-06 09:43 1076次阅读

    嵌入式开发入门指南:从零开始学习嵌入式

    基础 3. 学习路径推荐第一阶段:熟悉开发环境(如Keil、IAR、STM32)第二阶段:掌握裸机编程与驱动开发第三阶段:学习RTOS(实时操作系统)如FreeRTOS第四阶段:深入理解Linux嵌入式
    发表于 05-15 09:29