2、嵌入式系统基础回顾:MCU架构(ARM Cortex-M系列)、RTOS选型(FreeRTOS vs ThreadX)、外设驱动安全要点

各位同学,咱们今天聊点实在的。做医疗设备嵌入式开发,说白了就是跟MCU、RTOS和外设驱动打交道。这三样东西,就像盖房子的地基、框架和门窗。地基不稳,房子就塌;框架选错,后面改起来要命;门窗没装好,小偷就进来了。嗯,咱们一个一个说。

2.1 ARM Cortex-M系列:医疗设备的“心脏”

我个人习惯,选MCU先看架构。ARM Cortex-M系列,尤其是M3、M4、M7,在医疗领域用得最多。为什么?因为它的安全特性和实时性,是专门为这类场景设计的。

先说说M3。它是个“老实人”,没有MMU(内存管理单元),没有MPU(内存保护单元)。说白了,就是裸奔。但裸奔不代表不安全。我在一个血糖仪项目里用过M3,只要把外设寄存器访问权限管好,中断优先级配好,照样能过IEC 62304的Class B认证。M3的优势是功耗低、成本低,适合电池供电的便携设备。

M4就不一样了。它带了FPU(浮点运算单元),做信号处理快很多。比如心电信号(ECG)的滤波算法,用M4的硬件浮点,比M3的软件模拟快3-5倍。但要注意,FPU用不好会出问题。我曾经遇到一个坑:FPU的上下文切换没处理好,导致中断里算出来的数据全是NaN。后来查了ARM的文档才发现,FPU寄存器在中断里必须手动保存和恢复。嗯,这个细节,RTOS一般不会帮你做。

M7是性能怪兽。它支持双发射、分支预测,主频能跑到400MHz以上。但性能越强,安全风险越大。M7的缓存(Cache)和写缓冲(Write Buffer)会导致数据一致性问题。比如DMA和外设同时访问同一块内存,如果Cache没刷新,读到的就是旧数据。这在医疗设备里是致命的。我建议,凡是涉及DMA的数据缓冲区,一律用非缓存(Non-cacheable)内存区域。

最后提一下M23和M33。这两个是ARMv8-M架构的,支持TrustZone。TrustZone可以把系统分成安全区和非安全区。比如,把加密密钥、患者隐私数据放在安全区,普通应用代码放在非安全区。即使非安全区的代码被攻击了,也拿不到安全区的数据。这个特性,在联网的医疗设备里越来越重要。

核心要点:

  • M3:低功耗、低成本,适合简单控制类设备
  • M4:带FPU,适合信号处理类设备
  • M7:高性能,注意Cache一致性问题
  • M23/M33:支持TrustZone,适合安全敏感设备

2.2 RTOS选型:FreeRTOS vs ThreadX

RTOS选型,说白了就是选“调度器”。但医疗设备里,RTOS不只是调度任务,还要管安全、管认证。我见过太多项目,因为RTOS选错了,后面补安全补丁补到崩溃。

先说说FreeRTOS。它是开源的,用的人多,资料也多。但开源不等于免费。你要过IEC 62304认证,FreeRTOS的代码你得自己审计。我记得有个项目,FreeRTOS的队列操作里有个潜在的优先级反转问题。虽然概率很低,但在医疗设备里,这种“低概率”是不能接受的。后来我们不得不加了一个优先级继承协议,改了不少代码。

FreeRTOS的优势是灵活。你可以裁剪到只剩几个文件,适合资源受限的MCU。但它的安全特性基本为零。没有MPU支持,没有栈溢出检测,没有任务级看门狗。这些都得你自己加。我建议,如果项目预算紧张,用FreeRTOS可以,但一定要做充分的测试和代码审查。

再说说ThreadX。这是微软的商用RTOS,现在开源了。但它的核心价值不在开源,而在安全认证。ThreadX通过了IEC 62304 Class C、IEC 61508 SIL 4、ISO 26262 ASIL D等一堆认证。说白了,你拿ThreadX去做医疗设备,认证机构会少很多麻烦。

ThreadX有个特性叫“安全栈”(Secure Stack)。它可以在运行时检测栈溢出,一旦发现,立即触发异常处理。这个功能,我在一个输液泵项目里用过。当时有个任务递归调用太深,栈溢出了。ThreadX直接捕获了,没有导致系统崩溃。如果是FreeRTOS,可能就静默覆盖了其他任务的数据,后果不堪设想。

但ThreadX也有缺点。它占用的ROM和RAM比FreeRTOS大。比如,一个最小配置的ThreadX内核,大概要8KB ROM和2KB RAM。而FreeRTOS可以做到4KB ROM和1KB RAM。所以,如果MCU资源紧张,ThreadX可能跑不起来。

特性 FreeRTOS ThreadX
许可证 开源(MIT) 开源(微软)
安全认证 无(需自行认证) IEC 62304 Class C
最小ROM占用 ~4KB ~8KB
MPU支持 需手动添加 原生支持
栈溢出检测 安全栈
任务级看门狗 内置

我的建议:

如果项目目标是过IEC 62304 Class B或C,预算允许,直接上ThreadX。认证省下的时间,比多花的钱值多了。如果只是原型验证或Class A设备,FreeRTOS完全够用。

2.3 外设驱动安全要点:别让“门”变成“洞”

外设驱动,说白了就是MCU和外部世界的接口。但接口也是攻击面。我见过太多驱动代码,只关心“能不能用”,不关心“安不安全”。结果呢?一个UART接收溢出,就能让整个系统崩溃。

先说GPIO。GPIO看似简单,但安全风险不小。比如,一个按键中断,如果没做去抖处理,可能会触发多次中断。如果中断服务函数里做了重量级操作,比如写Flash,那系统就卡死了。我建议,GPIO中断里只做标记,具体处理放到任务里。另外,所有未使用的GPIO引脚,一定要配置成输入上拉或输出低电平,防止悬空导致功耗异常或误触发。

再说UART。UART是医疗设备最常见的通信接口,比如和上位机通信、和传感器通信。UART驱动最容易出的问题是“接收溢出”。如果上位机发数据太快,MCU来不及处理,UART的接收缓冲区就满了。新数据会覆盖旧数据,或者直接丢失。这在医疗设备里是不能接受的。比如,一个输液泵的流速指令丢了,后果是什么?你想想看。

我建议,UART驱动一定要做“流控”。硬件流控(RTS/CTS)最好,不行的话,软件流控(XON/XOFF)也行。另外,接收缓冲区要设计成环形缓冲区,并且要有溢出保护。我曾经在一个项目里,用了一个简单的数组做缓冲区,结果数据一多就覆盖了。后来改成环形缓冲区,加上溢出标志,问题才解决。

最后说SPI和I2C。这两个是板级通信接口,比如和ADC、DAC、Flash通信。SPI和I2C的安全要点是“时序”和“错误处理”。SPI的时钟极性(CPOL)和相位(CPHA)一定要配对,否则数据全是错的。I2C的时钟拉伸(Clock Stretching)要处理好,否则从设备忙的时候,主设备会超时。

错误处理更重要。比如,SPI通信时,从设备突然掉电了,主设备读到的数据全是0xFF。如果驱动不做校验,系统就会把0xFF当成有效数据。我建议,所有外设通信都要加CRC校验。如果连续3次CRC错误,就认为外设故障,触发系统安全状态。

避坑指南:

我曾经在一个心电监护仪项目里,SPI驱动没做超时处理。结果有一次ADC芯片坏了,SPI一直忙,主设备就卡死在等待标志位里。整个系统死机,患者数据丢了10分钟。从那以后,我所有外设驱动都加了超时机制。超时时间一般是正常通信时间的3倍,超时后直接报错,不等待。

好了,这一章就聊这么多。MCU架构、RTOS选型、外设驱动,这三样东西,你吃透了,医疗设备的安全和可靠性就有了基础。下一章,咱们聊聊更具体的——如何用MPU做内存隔离。嗯,那个更有意思。