第2章:心电监护仪硬件平台介绍

好,咱们正式开始动手之前,得先把“战场”看清楚。做嵌入式开发,尤其是Bootloader这种跟硬件打交道的活儿,不了解硬件平台,那就是盲人摸象。

这一章,我就带你仔细看看咱们心电监护仪的核心硬件。说白了,就是主控芯片选型、Flash怎么分区、RAM怎么安排,还有那些外设接口长什么样。

2.1 主控芯片选型:为什么是STM32F4系列?

选芯片这事儿,我踩过不少坑。早些年做一款便携式监护仪,图便宜选了颗低端M3内核的片子。结果呢?心电信号处理算法跑起来,CPU占用率直接飙到90%,连刷个屏都卡顿。用户一活动,波形就失真。那次教训挺深刻的。

所以这次做心电监护仪,我毫不犹豫选了STM32F4系列。具体型号是STM32F407VGT6。为什么是它?

  • 性能够用:Cortex-M4内核,带FPU(浮点运算单元)。心电信号处理里有很多滤波算法,比如IIR、FIR,这些计算量不小。FPU能帮你硬件加速,省下的CPU时间可以干别的。
  • 存储空间充裕:1MB Flash + 192KB RAM。Bootloader、应用程序、参数存储,都能安排得明明白白。
  • 外设丰富:ADC、DAC、定时器、SPI、I2C、USART……该有的都有。心电信号采集、屏幕显示、通信,一个芯片全搞定。
  • 生态成熟:库函数、HAL库、各种例程,网上资料一抓一大把。遇到问题,基本都能找到答案。

我个人习惯:选型时,我会留出30%的性能余量。别把芯片用到极限,否则后期加功能、修bug,你会非常痛苦。

2.2 Flash分区规划:Bootloader和App怎么共存?

Flash分区,是Bootloader设计的核心。分区没规划好,后面升级、回滚都会出问题。

咱们这颗STM32F407,Flash总共1MB,起始地址0x08000000。我是这么分的:

分区名称 起始地址 大小 用途
Bootloader区 0x08000000 64KB 存放Bootloader固件
App参数区 0x08010000 16KB 存放App的校准参数、配置信息
App主程序区 0x08014000 896KB 存放应用程序固件
备份升级区 0x080F0000 48KB 存放升级固件包(OTA下载后暂存)

你可能会问:为什么Bootloader只给64KB?够用吗?

嗯,这里要注意。Bootloader的功能其实很单一:检查升级标志、擦写Flash、跳转到App。代码量不会太大。我做过一个项目,Bootloader只用了30KB。64KB已经留了很大余量了。

我曾经犯过一个错误:把Bootloader和App的参数区紧挨着放。结果App升级时,不小心把参数区给擦掉了。病人数据全丢了……从那以后,我坚持把参数区放在Bootloader和App之间,作为隔离带。

2.3 RAM布局:中断向量表重映射

RAM的规划,重点在于中断向量表。STM32上电后,默认从Flash起始地址读取中断向量表。但App运行后,需要把中断向量表重映射到App的起始地址。

咱们的RAM是192KB,起始地址0x20000000。我一般这样分配:

  • 0x20000000 - 0x2000003FF:这部分留给Bootloader的中断向量表(如果Bootloader需要中断的话)。
  • 0x20000400 - 0x2001FFFF:App的堆、栈、全局变量、DMA缓冲区等。

App启动时,需要做两件事:

  1. 设置向量表偏移寄存器(SCB->VTOR)为App的Flash起始地址。
  2. 重新初始化堆栈指针。

代码实现很简单,但容易忘:

// 在App的启动文件或main函数最前面
#define APP_FLASH_ADDR  0x08014000

void jump_to_app(void)
{
    // 关闭全局中断
    __disable_irq();
    
    // 设置主堆栈指针
    __set_MSP(*(uint32_t*)APP_FLASH_ADDR);
    
    // 设置中断向量表偏移
    SCB->VTOR = APP_FLASH_ADDR;
    
    // 跳转到App的复位向量
    void (*app_reset_handler)(void) = (void (*)(void))(*(uint32_t*)(APP_FLASH_ADDR + 4));
    app_reset_handler();
}

避坑指南:跳转前一定要关闭全局中断。否则跳转过程中,中断来了,但向量表还没更新,程序直接跑飞。我因为这个bug,调试了整整一个下午。

2.4 外设接口概览:心电监护仪需要哪些“手脚”?

心电监护仪不是一个人在战斗。它需要跟各种外设打交道。咱们来看看主要接口:

  • ADC(模数转换):心电信号是模拟量,必须转成数字量才能处理。STM32F4有3个12位ADC,我用了ADC1,采样率设为1kHz。为什么是1kHz?因为心电信号的频率范围是0.05Hz-100Hz,根据奈奎斯特定理,2倍就够了。1kHz留了10倍余量,够用。
  • SPI(串行外设接口):用来驱动LCD屏幕。心电波形要实时显示,SPI速度够快,刷屏不卡顿。
  • I2C(集成电路总线):连接传感器,比如加速度计(检测病人是否跌倒)、温度传感器(监测体温)。
  • USART(通用同步异步收发器):用于Wi-Fi模块或蓝牙模块通信。OTA升级就是通过这个接口接收固件包。
  • GPIO(通用输入输出):控制蜂鸣器(报警用)、LED指示灯(状态指示)、按键(用户交互)。

我记得有一次,SPI通信老是出错。排查了半天,发现是SCK时钟极性配置反了。硬件上要求空闲时时钟为高电平,我配成了低电平。这种细节,不看数据手册真的容易忽略。

我的建议:画原理图时,就把每个外设的引脚、时钟、中断优先级都列清楚。做成一张表,贴在工位上。调试时能省一半时间。

好了,硬件平台就介绍到这里。下一章,咱们开始搭建开发环境,写第一行Bootloader代码。你准备好了吗?