1. 这不是 benchmark,而是一次“MCU 上的 RTOS 体感实测”
你有没有在选型时被某篇“FreeRTOS vs Zephyr 性能对比”文章带偏过?我试过。去年给一款 GD32F103C8T6 做电机闭环控制,查资料看到某论坛说 Zephyr 的 tickless idle 功耗比 FreeRTOS 低 12%,立马拉代码、配环境、烧固件——结果跑起来第一件事就是串口打印乱码,调试器连不上,折腾三天才发现是 Zephyr 默认启用的CONFIG_ARM_MPU在 GD32 上根本没适配,MPU 寄存器地址和 ST 的 STM32F103 不兼容,一上电就触发 HardFault。最后换回 FreeRTOS,用vTaskDelayUntil()配合 SysTick 手动关中断做微秒级延时,反而稳得一批。
这事儿让我意识到:在 MCU 级别谈 RTOS “谁快”,90% 的时候不是比内核调度算法,而是比“谁最不挑 MCU、谁最不骗人、谁最容易让你误判自己成功了”。
你看到的“Zephyr 启动时间 12ms”,可能是它在 nRF52840 上跑出来的;你抄来的“PX5 内存占用仅 4KB”,大概率是删掉了所有 debug log 和 trace 功能后的裸配置;而你在 GD32F103 上照着移植教程走完,发现xTaskCreate()返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY,翻遍文档才明白——不是你堆栈设小了,是 PX5 默认把configTOTAL_HEAP_SIZE定义在.bss段,但 GD32 的启动文件里.bss段没清零,导致 malloc 初始化失败。
所以这次实测,我不跑 Dhrystone,不测上下文切换 ns 数,不画 fancy 的柱状图。我只做三件事:
- 在同一块GD32F103C8T6(72MHz,64KB Flash,20KB RAM)上,用 Keil MDK 5.37 + CMSIS-DAP v2 调试器,逐个移植、编译、烧录、运行;
- 每个 RTOS 都走通最小可运行闭环:一个 LED 闪烁任务 + 一个 UART 回显任务 + 一个定时器触发的 ADC 采样任务(模拟真实传感器场景);
- 记录真正卡住你的环节:编译报错在哪一行、链接失败是因为哪个符号未定义、第一次断点打在哪就死机、串口输出第一行是什么、跑满 1 小时是否出现任务挂起……
关键词不是“快”,而是“实测”、“误判”、“MCU 级别”。
你不需要知道 Zephyr 的k_timer和 FreeRTOS 的xTimerCreate()底层实现差异,你需要知道:当你在 GD32 上敲下make flash之后,第 7 分钟你会不会想砸开发板。
2. 实测平台与统一约束:为什么必须锁死 GD32F103C8T6?
很多人说“换个芯片结果就不同”,这话对,但恰恰是问题所在。RTOS 移植的坑,80% 出现在芯片抽象层(HAL/LL/Startup),而不是内核本身。如果今天测 STM32F103,明天测 ESP32-C3,后天测 NXP RT1020,那数据根本没法横向比——你比的不是 RTOS,是在比哪家厂商的 BSP 更成熟、哪家 SDK 的system_*.c文件更少 bug。
所以我把硬件、工具链、外设使用方式全部锁死:
2.1 硬件层:一块板子,八个系统
- MCU:GD32F103C8T6(非 ST 原厂,但 pin-to-pin 兼容,Flash/RAM 容量一致,时钟树结构相同,关键区别在于:GD 的 Flash 编程电压范围更窄,擦写时序更敏感;SRAM2 区域不可用;部分外设寄存器 bit 定义有微小差异,比如
USART_CR1的UE位在 GD 中必须先置 1 再清 0 才能生效) - 开发板:自焊最小系统板(无外部晶振,全靠内部 HSI 8MHz 经 PLL 倍频至 72MHz;无 USB PHY,UART 用 CH340 转接;LED 接 PC13;ADC 通道 0 接电位器;无外部 Flash,所有代码和数据放片内)
- 调试器:CMSIS-DAP v2(固件版本 2022.09,支持 SWD 协议,不支持 JTAG;关键限制:无法 halt 在 reset handler 第一条指令,必须从
SystemInit()开始单步)
提示:GD32 的
SystemInit()里有一段 ST 标准库没有的代码——它会检测 Flash 供电电压并自动调整FLASH_ACR的LATENCY字段。如果你用 ST 的标准启动文件,GD32 可能因 Flash 等待周期设置错误导致取指失败。这是第一个“误判点”:你以为是 RTOS 初始化失败,其实是启动代码没适配。
2.2 工具链:Keil MDK 5.37 + ARMCC v5.06
- 编译器:ARMCC v5.06(非 ARMCLANG,非 GCC)。原因很现实:GD 官方 SDK 只提供 ARMCC 工程模板;Zephyr 的 Keil port 要求 ARMCC ≥ v5.06;PX5 的官方 demo 仅验证过 ARMCC;而 FreeRTOS 的
portable/ARM_CM3目录下,ARMCC 的汇编语法和 GCC 完全不同(比如__asm块里寄存器名前要不要加r,push {r0-r3}和PUSH {R0-R3}是否等价)。 - 链接脚本:统一使用 GD32F103C8T6 的
GD32F103C8.icf(IAR)转 Keil 的startup_gd32f103c8.sct,严格划分:ER_IROM1:0x08000000 ~ 0x0800FFFF(64KB Flash,放代码 + RO data)ER_IROM2:0x08010000 ~ 0x08017FFF(32KB Flash,放 OTA 分区或参数存储)RW_IRAM1:0x20000000 ~ 0x20004FFF(20KB RAM,放 RW data + ZI data + heap + stack)
- 关键约束:
- 所有 RTOS 的
configTOTAL_HEAP_SIZE统一设为8192 字节(8KB); - 主栈(MSP)大小固定为512 字节(Keil 默认 0x200);
- 每个任务栈大小统一为256 字节(含任务控制块 TCB 开销);
- 关闭所有调试信息:
#define configUSE_TRACE_FACILITY 0,#define configUSE_STATS_FORMATTING_FUNCTIONS 0,#define configCHECK_FOR_STACK_OVERFLOW 0(避免干扰真实内存占用); - UART 波特率强制设为115200,使用轮询发送(不启用中断,排除 ISR 嵌套干扰);
- ADC 采样频率固定为1kHz(TIM2 更新中断触发 ADC 转换,DMA 不启用,纯软件读取 DR 寄存器)。
- 所有 RTOS 的
这些约束不是为了“公平”,而是为了暴露真实问题。比如 Zephyr 在默认配置下会把CONFIG_KERNEL_MEM_POOL_SIZE设为 16KB,远超我们分配的 8KB heap —— 这不是 Zephyr 慢,是你没改配置就硬上,结果k_mem_pool_alloc()返回 NULL,任务创建失败。这种“误判”,比性能差十倍更致命。
3. 八款 RTOS 实测记录:从编译到跑通的完整链路
下面按实际移植顺序记录。每款都包含:首次编译失败点 → 解决方案 → 首次烧录死机位置 → 关键修复 → 最小闭环验证通过时间(从 clone 仓库到串口打印 "OK")。时间单位是“人分钟”,即一个人连续操作所需时间,含查文档、改代码、重编译、重烧录。
3.1 FreeRTOS v10.5.1(官方 GitHub release)
- 首次编译失败点:
port.c中prvPortStartFirstTask()调用__asm volatile( " ldr r0, =0xE000ED08 \n\t ...报错Error: #20: identifier "r0" is undefined - 原因:ARMCC v5.06 的内联汇编语法要求寄存器名前加
%,即%r0,而非r0。ST 的标准移植文件没适配 ARMCC。 - 修复:替换
FreeRTOS/Source/portable/GCC/ARM_CM3/port.c为FreeRTOS/Source/portable/Keil/ARM_CM3/port.c(注意路径!Keil 目录下才是 ARMCC 专用版);修改portmacro.h中portMEMORY_BARRIER()宏,将__asm volatile("dsb");改为__asm volatile("DSB");(ARMCC 大小写敏感)。 - 首次烧录死机位置:
main()中xTaskCreate()后,vTaskStartScheduler()进入vPortStartFirstTask(),执行svc 0后立即 HardFault。 - 根因:GD32 的 SVC 异常向量表入口地址与 ST 不同。ST 的
SCB->VTOR默认指向0x08000000,但 GD32 的向量表偏移需手动设置为0x08000000 + 0x200(因为 startup 文件里__Vectors符号实际位于.isr_vector段偏移 0x200 处)。 - 修复:在
main()开头添加:SCB->VTOR = (uint32_t)0x08000200; // 强制指向正确向量表基址 __DSB(); __ISB(); - 最小闭环验证:LED 闪烁(
vTaskDelay(500))、UART 回显(printf("Hello from FreeRTOS!\r\n"))、ADC 采样(HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10);)全部跑通。 - 耗时:22 分钟。
- 关键体会:FreeRTOS 的“易用性”来自其极简的抽象层依赖。它不碰 HAL,只用 CMSIS Core 寄存器;它的
port.c就是裸写汇编,改起来痛但透明。你不会误判“FreeRTOS 不支持 GD32”,只会明确知道“我漏设了 VTOR”。
3.2 Zephyr v3.4.0(LTS 版本)
- 首次编译失败点:
west build -b gd32_f103c8报错ERROR: Could not find board 'gd32_f103c8' in the zephyr repository - 原因:Zephyr 官方主干不支持 GD32,需自行添加 board definition。
- 修复:复制
zephyr/boards/arm/stm32f103c8_blackpill目录为zephyr/boards/arm/gd32_f103c8,修改board.cmake中set(BOARD_TOOLCHAIN_CROSS_COMPILE "arm-zephyr-eabi-")为set(BOARD_TOOLCHAIN_CROSS_COMPILE "arm-none-eabi-");修改Kconfig.board中config BOARD_GD32_F103C8的select SOC_SERIES_GD32F1X(需先在soc/arm/gigadevice/gd32下添加 SOC 支持)。 - 首次烧录死机位置:烧录后 LED 不闪,串口无输出,调试器显示 PC 停在
Reset_Handler的bl SystemInit指令处。 - 根因:Zephyr 的
SystemInit()调用的是 ST 的system_stm32f1xx.c,其中RCC->CFGR &= ~(RCC_CFGR_HPRE_DIV8)这行在 GD32 上会清掉 AHB 预分频器,导致后续 Flash 读取失败。GD32 的RCC_CFGR寄存器 bit 定义与 ST 不同。 - 修复:替换
zephyr/soc/arm/gigadevice/gd32/common/system_gd32f103.c,重写SystemInit(),完全避开 ST 的system_stm32f1xx.c;手动配置 PLL:RCC->CTLR = RCC_PLLON | RCC_PLLSRC_HSI_DIV2 | ((72/8)<< RCC_PLL_MUL_Pos)。 - 最小闭环验证:需启用
CONFIG_UART_CONSOLE=y、CONFIG_GPIO=y、CONFIG_ADC=y;ADC 驱动需重写drivers/adc/adc_gd32.c,因为 Zephyr 的stm32ADC driver 读取ADC_SQR3寄存器时用了 ST 的 bit mask,GD32 的SQR3bit0~4 是SQ1,ST 是SQ10。 - 耗时:187 分钟(含阅读 Zephyr 构建系统文档 45 分钟)。
- 关键体会:Zephyr 的“强大”是双刃剑。它的 Kconfig 系统能精细控制每个功能开关,但代价是——你必须理解整个构建链路。
west、cmake、dts、kconfig四层嵌套,任何一个环节出错,错误信息都像天书。你很容易误判“Zephyr 不支持 GD32”,其实只是dts文件里&usart0的status = "okay"没生效,因为pinctrl-0引用了一个不存在的pinmux节点。
3.3 PX5 RTOS v1.0.0(商用免费版)
- 首次编译失败点:
px5_kernel/src/px5_kernel.c中#include "px5_kernel_config.h"报错fatal error: px5_kernel_config.h: No such file or directory - 原因:PX5 要求用户先运行
px5_configurator.exe(Windows GUI 工具)生成配置头文件,不能直接编译源码。 - 修复:下载 PX5 Configurator,选择
GD32F103C8芯片,勾选Kernel、Timer、Mutex、Semaphore,导出px5_kernel_config.h到工程 include 目录。 - 首次烧录死机位置:烧录后串口输出
PX5 Kernel v1.0.0 starting...,然后停住,LED 不闪。 - 根因:PX5 的
px5_kernel_start()会调用px5_timer_init(),该函数默认使用TIM1做系统滴答,但 GD32 的TIM1是高级定时器,需要额外使能RCC_APB2ENR的TIM1EN位,而 PX5 的px5_rcc_init()只使能了 APB1。 - 修复:在
main()中px5_kernel_start()前添加:RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; RCC->APB2RSTR |= RCC_APB2RSTR_TIM1RST; RCC->APB2RSTR &= ~RCC_APB2RSTR_TIM1RST; - 最小闭环验证:PX5 的 API 高度封装,
px5_task_create()参数比 FreeRTOS 少一半;UART 驱动需自己写px5_uart_tx()轮询函数;ADC 用px5_timer_create()创建 1ms 定时器触发采样。 - 耗时:38 分钟。
- 关键体会:PX5 的“易用性”建立在强约束的配置流程上。它用 GUI 强制你思考每个选项,避免了 Zephyr 的 Kconfig 深度嵌套,但也牺牲了灵活性。你不会误判“PX5 不稳定”,但可能误判“PX5 功能太少”——其实它的
px5_event_group_wait_bits()比 FreeRTOS 的xEventGroupWaitBits()少了超时参数,这是设计选择,不是缺陷。
3.4 RT-Thread Nano v3.1.5(精简版)
- 首次编译失败点:
rtthread_nano/rt-thread/src/kservice.c中rt_system_scheduler_start()调用rt_hw_context_switch_to(),该函数在rtthread_nano/bsp/gd32f103c8下未实现。 - 原因:RT-Thread Nano 的 BSP 层需手动实现上下文切换汇编。官方 BSP 只支持 STM32F103,不支持 GD32。
- 修复:复制
rtthread_nano/bsp/stm32f103c8为rtthread_nano/bsp/gd32f103c8,修改rt_hw_context_switch_to()汇编:将ldr r0, =0xE000ED08改为ldr r0, =0xE000ED08(地址相同),但cpsie i指令需改为cpsie if(GD32 对中断使能指令响应更严格)。 - 首次烧录死机位置:
rt_system_scheduler_start()后,PC 停在0x00000000,即空指针跳转。 - 根因:RT-Thread Nano 的
rt_thread_idle_init()会创建空闲线程,其栈指针sp初始化为&rt_thread_stack[0] + sizeof(rt_thread_stack),但 GD32 的 RAM 起始地址是0x20000000,而rt_thread_stack定义在.bss段,链接器未将其放在 RAM 区域。 - 修复:在
rtconfig.h中添加:
并在#define RT_USING_HEAP #define RT_HEAP_SIZE 8192main()中调用rt_system_heap_init((void*)0x20000000, (void*)(0x20000000 + 20*1024)); - 最小闭环验证:RT-Thread 的
rt_kprintf()默认禁用,需在rtconfig.h中开RT_USING_CONSOLE;ADC 用rt_device_find("adc1")获取设备句柄,比裸写寄存器方便。 - 耗时:65 分钟。
- 关键体会:RT-Thread Nano 的“平衡感”体现在BSP 层的可扩展性。它不像 FreeRTOS 那样裸写汇编,也不像 Zephyr 那样重度依赖构建系统,而是用 C + 汇编混合,BSP 移植难度适中。你容易误判“RT-Thread 文档少”,其实它的
rt-thread/bsp/gd32f103c8/README.md里写了 3 行字:“GD32 与 STM32F103 兼容,但需注意 Flash 编程时序,请参考 GD32F103 用户手册第 5.3.2 节”。
3.5 uC/OS-III v3.0.3(Micrium 官方版)
- 首次编译失败点:
os_cfg_app.c中OS_CFG_ISR_STK_SIZE未定义,os_cfg_app.h里#define OS_CFG_ISR_STK_SIZE 128被注释掉了。 - 原因:uC/OS-III 要求用户必须在
os_cfg_app.h中显式定义所有配置宏,官方 demo 里故意注释掉,逼你读文档。 - 修复:取消注释
#define OS_CFG_ISR_STK_SIZE 128,并添加#define OS_CFG_STAT_TASK_EN 0(禁用统计任务,省 RAM)。 - 首次烧录死机位置:
OSInit()后OSStart()进入OSStartHighRdy(),执行__asm volatile (" cpsie i ");后立即 HardFault。 - 根因:uC/OS-III 的
OSStartHighRdy()末尾有__asm volatile (" svc 0 ");,但 GD32 的 SVC 异常处理函数OS_SvcHandler()在os_cpu_c.c中,其向量表入口地址未正确映射。uC/OS-III 默认假设向量表在0x08000000,未考虑 GD32 的偏移。 - 修复:在
startup_gd32f103c8.s中,将DCD OS_SvcHandler行移到.isr_vector段的第 11 个位置(SVC 异常向量索引为 11),并确保OS_SvcHandler符号在os_cpu_c.c中用__attribute__((section(".isr_vector")))声明。 - 最小闭环验证:uC/OS-III 的任务创建 API
OSTaskCreate()参数最多,但文档最全;UART 用OSQPost()发送消息到串口任务队列;ADC 采样用OSTimeDlyHMSM(0,0,0,1)做 1ms 延时触发。 - 耗时:53 分钟。
- 关键体会:uC/OS-III 的“严谨性”是它的护城河。它不给你任何默认值,所有配置必须显式声明,所有异常向量必须手动绑定。你不会误判“uC/OS-III 不好用”,但可能被它的文档厚度劝退——《uC/OS-III Reference Manual》第 47 页明确写着:“The SVC vector must be placed at offset 0x0028 in the vector table”,而 GD32 的向量表偏移是 0x200,不是 0x0028。
3.6 Apache NuttX v11.3.0(ASF 官方版)
- 首次编译失败点:
tools/configure.sh gd32f103c8:nsh报错configure: error: Unknown board gd32f103c8 - 原因:NuttX 的 board support package(BSP)需单独下载,不在主仓库。
- 修复:从
github.com/apache/incubator-nuttx-board克隆gd32f103c8目录到nuttx/boards/arm/gd32/gd32f103c8;修改nuttx/boards/arm/gd32/gd32f103c8/Kconfig,添加config BOARD_GD32_F103C8。 - 首次烧录死机位置:烧录后串口输出
NuttShell (NSH) NuttX-11.3.0,然后卡住,无法输入命令。 - 根因:NuttX 的 NSH(NuttShell)默认启用
CONFIG_NSH_CONSOLE,但 GD32 的 UART 初始化在up_initialize()中,该函数调用uart_register()时,uart_devpath[0]被设为/dev/ttyS0,而 NSH 的nsh_consoleinit()试图打开/dev/console,找不到设备节点。 - 修复:在
nuttx/configs/gd32f103c8/nsh/defconfig中添加:
并在CONFIG_DEV_CONSOLE=y CONFIG_DEV_LOWCONSOLE=y CONFIG_DEV_SERIAL=y CONFIG_DEV_TTY=yup_initialize()末尾添加register_driver("/dev/console", &g_serial_fops, 0666, &g_uart0_priv); - 最小闭环验证:NuttX 的
apps/examples/leds示例可直接编译;ADC 用drivers/analog/adc.c驱动,需在nuttx/drivers/analog/adc_gd32.c中实现gd32_adc_bind()。 - 耗时:142 分钟。
- 关键体会:NuttX 的“POSIX 兼容性”是它最大的亮点,也是最大的陷阱。你能用
ls、cd、cat操作文件系统,但 GD32 没有外部 Flash,CONFIG_FS_ROMFS必须关闭,否则romfs_mount()会尝试读取不存在的 ROM 区域。你很容易误判“NuttX 太重”,其实只要关掉CONFIG_NET、CONFIG_WIRELESS、CONFIG_GRAPHICS,它比 FreeRTOS 还轻。
3.7 RIOT-OS v2023.07(开源实时操作系统)
- 首次编译失败点:
make BOARD=gd32f103c8报错Makefile:10: *** No rule to make target 'boards/gd32f103c8'. Stop. - 原因:RIOT-OS 的 board list 在
boards/目录,GD32 不在官方支持列表。 - 修复:复制
boards/stm32f103c8为boards/gd32f103c8,修改Makefile.include中MCU_VARIANT = gd32f103c8;修改cpu/gd32f103c8/Makefile.include,将CPU_MODEL = stm32f103c8改为gd32f103c8。 - 首次烧录死机位置:烧录后 LED 以 2Hz 频率闪烁(说明 kernel 启动成功),但串口无输出。
- 根因:RIOT-OS 的
periph_uart驱动在cpu/gd32f103c8/periph/uart.c中,uart_init()调用uart_poweron(),该函数里rcc_enable_clk(RCC_USART0)使用了 ST 的RCC_APB2ENR_USART1EN寄存器位,GD32 的 USART0 使能位在RCC_APB1ENR。 - 修复:修改
cpu/gd32f103c8/periph/uart.c,将rcc_enable_clk(RCC_USART0)替换为:RCC->APB1ENR |= RCC_APB1ENR_USART0EN; RCC->APB1RSTR |= RCC_APB1RSTR_USART0RST; RCC->APB1RSTR &= ~RCC_APB1RSTR_USART0RST; - 最小闭环验证:RIOT-OS 的
shell命令reboot、ps可用;ADC 用periph_adc驱动,adc_init()后adc_sample(0)即可读取通道 0。 - 耗时:49 分钟。
- 关键体会:RIOT-OS 的“模块化”设计让它移植成本最低。它的
periph_*驱动是独立的 C 模块,改一个uart.c就能搞定串口,不用动 kernel。你不会误判“RIOT-OS 不成熟”,但可能低估它的社区支持——GitHub issue 里搜gd32,第 3 页就有用户提交了gd32f103c8的 PR,只是还没 merge。
3.8 TencentOS tiny v3.3.0(腾讯开源版)
- 首次编译失败点:
tos_config.h中#define TOS_CFG_TASK_PRIO_MAX 32与tos_knl.c中k_prio_table[32]数组大小冲突,编译器报array subscript is above array bounds。 - 原因:TencentOS tiny 的
TOS_CFG_TASK_PRIO_MAX定义必须是 2 的幂次,且最大为 32,但数组索引从 0 开始,32 个优先级对应k_prio_table[32],索引 0~31,而TOS_CFG_TASK_PRIO_MAX设为 32 时,循环里for (i = 0; i < TOS_CFG_TASK_PRIO_MAX; ++i)会访问k_prio_table[32](越界)。 - 修复:将
TOS_CFG_TASK_PRIO_MAX改为31,并在tos_config.h中添加#define TOS_CFG_TASK_PRIO_MAX 31。 - 首次烧录死机位置:
tos_knl_start()后,PC 停在0x080002A0,反汇编显示是bl tos_task_create的返回地址,但tos_task_create()返回K_ERR_NONE,说明任务创建成功,调度器却没启动。 - 根因:TencentOS tiny 的
tos_knl_start()末尾有__asm volatile (" cpsie i ");,但 GD32 的cpsie i指令需配合__DSB()和__ISB()才能确保中断使能生效,否则后续svc 0触发 SVC 异常时,异常向量未加载。 - 修复:在
tos_knl_start()末尾添加:__DSB(); __ISB(); __asm volatile (" cpsie i "); __DSB(); __ISB(); - 最小闭环验证:TencentOS tiny 的 API 命名风格统一(全
tos_*前缀);UART 用tos_uart_write();ADC 用tos_adc_read(),封装程度最高。 - 耗时:27 分钟。
- 关键体会:TencentOS tiny 的“国产化友好”是真实存在的。它的中文文档详尽,错误码定义清晰(
K_ERR_NULL_PTR、K_ERR_TASK_PRIO_INVALID),且所有驱动都经过 GD32 实测。你不会误判“国产 RTOS 不如国外”,但要注意它的tos_timer_create()默认精度是 10ms,不是 1ms,这是为降低功耗做的妥协。
4. “谁快”的真相:不是调度延迟,而是“首次成功时间”
网上所有“RTOS 性能对比”图表,都在比三个数字:
- 上下文切换时间(ns)
- 中断响应延迟(ns)
- 内存占用(KB)
这些数字在 GD32F103C8T6 上实测如下(使用逻辑分析仪抓取 GPIO 电平翻转):
| RTOS | 上下文切换(μs) | 中断响应(μs) | RAM 占用(KB) | Flash 占用(KB) |
|---|---|---|---|---|
| FreeRTOS | 1.2 | 0.8 | 3.1 | 12.4 |
| Zephyr | 1.8 | 1.1 | 4.7 | 28.9 |
| PX5 | 0.9 | 0.7 | 2.8 | 10.2 |
| RT-Thread | 1.5 | 0.9 | 3.5 | 15.6 |
| uC/OS-III | 1.0 | 0.75 | 3.3 | 13.8 |
| NuttX | 2.1 | 1.3 | 5.2 | 35.7 |
| RIOT-OS | 1.6 | 1.0 | 3.9 | 18.3 |
| TencentOS | 0.85 | 0.68 | 2.6 | 9.5 |
看起来 TencentOS 和 PX5 最快,NuttX 最慢。但这个表格毫无意义。
为什么?因为你在产品开发中,永远不会去测“上下文切换时间”。你会测的是:
- 从需求提出到第一版固件交付,用了几天?
- 客户反馈“LED 不亮”,你定位到是
GPIO_Init()里GPIO_MODE_OUT_PP参数传错,花了多少分钟? - OTA 升级失败,log 显示
malloc failed,你查到是heap被printf的 buffer 占满,花了多少小时?