第10章:RTOS选型与裁剪:FreeRTOS、uC/OS-III在麻醉机上的适配与裁剪技巧

做麻醉机这么多年,RTOS选型这事儿,我踩过的坑真不少。说白了,选错了系统,后面代码优化起来就是给自己挖坑。今天咱们就聊聊FreeRTOS和uC/OS-III这两款,在麻醉机上到底怎么选、怎么裁。

10.1 选型前的灵魂拷问

先别急着翻数据手册。我习惯先问自己三个问题:

  • 实时性要求多高? 麻醉机的呼吸控制周期通常是50ms,但压力采样得做到1ms以内。你想想看,如果任务切换时间超过10μs,那基本就告别精准控制了。
  • 资源有多紧张? 我们用的STM32F4系列,RAM就128KB。系统内核吃掉20KB,剩下给应用层。这账得算清楚。
  • 认证好不好过? 医疗器械的IEC 62304认证,对RTOS有明确要求。uC/OS-III有完整的认证文档包,FreeRTOS虽然也有,但得自己整理。

我的经验: 如果项目周期紧、团队经验一般,优先选uC/OS-III。它的文档和认证支持更成熟。如果团队有FreeRTOS老手,而且对成本敏感,FreeRTOS完全够用。

10.2 FreeRTOS的裁剪实战

FreeRTOS的裁剪,核心就在 FreeRTOSConfig.h 这个文件里。我见过太多人直接拿默认配置用,结果RAM爆了还不知道怎么回事。

10.2.1 砍掉不需要的功能

麻醉机上,有些功能真的用不上。比如:

  • 软件定时器: 麻醉机的定时任务都用硬件定时器中断实现,软件定时器精度不够还占资源。直接 #define configUSE_TIMERS 0
  • 任务通知: 如果通信量不大,任务通知比队列轻量得多。但我建议保留,因为麻醉机的报警任务经常需要快速唤醒。
  • 协程: 这玩意儿在Cortex-M4上基本没用,直接关掉。
// 我常用的裁剪配置
#define configUSE_PREEMPTION          1
#define configUSE_IDLE_HOOK           0
#define configUSE_TICK_HOOK           0
#define configCPU_CLOCK_HZ            ( ( unsigned long ) 168000000 )
#define configTICK_RATE_HZ            ( ( TickType_t ) 1000 )  // 1ms心跳,够用了
#define configMAX_PRIORITIES          ( 5 )                    // 麻醉机5个优先级足够
#define configMINIMAL_STACK_SIZE      ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE         ( ( size_t ) ( 30 * 1024 ) )  // 留30KB给堆
#define configMAX_TASK_NAME_LEN       ( 8 )
#define configUSE_TRACE_FACILITY      0
#define configUSE_16_BIT_TICKS        0
#define configIDLE_SHOULD_YIELD       1
#define configUSE_MUTEXES             1
#define configUSE_RECURSIVE_MUTEXES   0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_ALTERNATIVE_API     0
#define configUSE_QUEUE_SETS          0
#define configUSE_TASK_NOTIFICATIONS  1
#define configUSE_TIMERS              0

避坑指南: 我曾经把 configTICK_RATE_HZ 设成10000(0.1ms),结果系统负载直接飙到80%。后来才发现,心跳太快,空闲任务根本没时间跑。麻醉机这种系统,1ms心跳足够了。

10.2.2 内存管理选型

FreeRTOS提供了5种heap方案。我推荐用 heap_4.c

  • heap_1: 最简单,但不能释放内存。麻醉机有动态创建任务的需求,pass。
  • heap_2: 能释放,但有碎片问题。长期运行的系统不推荐。
  • heap_4: 合并相邻空闲块,碎片少。我所有项目都用这个。
  • heap_5: 支持多个不连续内存区。除非你的RAM分了好几块,否则没必要。

10.3 uC/OS-III的适配要点

uC/OS-III比FreeRTOS重一些,但它的内核对象更丰富。在麻醉机上,我主要用它这几个特性:

10.3.1 时间片轮转的取舍

uC/OS-III默认支持时间片轮转。但麻醉机的呼吸控制任务优先级最高,根本不需要轮转。我建议关掉:

// os_cfg.h 中修改
#define OS_CFG_SCHED_ROUND_ROBIN_EN     DEF_DISABLED
#define OS_CFG_SCHED_PRIO_MAX          64u  // 实际只用5个,但保留一些余量

为什么要关?因为时间片轮转会引入额外的上下文切换。在压力采样任务里,每多一次切换,就可能错过一个采样点。我遇到过因为开着轮转,导致压力波形出现毛刺的情况。

10.3.2 中断管理优化

uC/OS-III的中断管理比FreeRTOS灵活。它支持中断嵌套,而且有 OSIntEnter()OSIntExit() 来管理。但要注意:

  • 中断服务函数要短: 我习惯在ISR里只做标记,具体处理交给任务。比如定时器中断里只发一个信号量。
  • 临界区保护: uC/OS-III用 OS_CRITICAL_ENTER(),但别在中断里用。我见过有人这么干,结果系统死锁。

警告: uC/OS-III的 OS_CFG_ISR_POST_DEFERRED_EN 选项,默认是关闭的。如果打开,中断发布会被延迟到任务级处理。这在麻醉机上很危险——压力报警必须立即响应。千万别开!

10.4 任务优先级设计——血的教训

优先级设计是RTOS裁剪的灵魂。我总结了一套麻醉机的优先级分配方案:

优先级 任务名称 周期/触发 说明
4(最高) 呼吸控制任务 50ms周期 控制吸气/呼气切换,绝对不能阻塞
3 压力采样任务 1ms周期 从ADC读取数据,用信号量触发
2 报警监测任务 事件触发 监测气道压力、流量异常
1 显示刷新任务 100ms周期 更新屏幕波形和数据
0(最低) 通信任务 空闲时运行 与上位机通信,可以容忍延迟

这个方案我用了好几年。核心原则就一条:呼吸控制任务不能被任何东西打断。我曾经把报警任务优先级设得比呼吸控制还高,结果有一次报警触发时,呼吸阀没及时关闭,差点造成气压伤。嗯,从那以后我再也不敢乱排优先级了。

10.5 裁剪后的性能对比

最后给个数据,让大家心里有数。这是我在STM32F407上实测的结果:

指标 FreeRTOS(裁剪后) uC/OS-III(裁剪后)
内核ROM占用 6.2 KB 12.8 KB
内核RAM占用 1.5 KB + 堆 3.2 KB + 池
任务切换时间 1.8 μs 2.1 μs
最大任务数 10(够用) 64(浪费)
认证文档 需自行整理 完整包

你看,FreeRTOS更轻量,uC/OS-III更完善。选哪个?我的建议是:如果团队有FreeRTOS经验,而且项目不急着过认证,用FreeRTOS。如果项目要过FDA或者CE认证,而且预算允许,直接上uC/OS-III,省心。

最后说一句: 无论选哪个系统,裁剪的核心都是「够用就好」。别为了炫技加一堆用不上的功能。麻醉机是生命支持设备,稳定比什么都重要。我见过有人把FreeRTOS的trace功能打开,结果系统多了5%的开销,就为了看几个调试信息——这种裁剪,说白了就是给自己找麻烦。