基于STM32的心电采集系统设计

# 基于STM32的心电采集系统设计

## 一、系统概述

心电采集系统通过**电极阵列**采集人体心电信号(ECG),经**信号调理**(放大、滤波)后,由**STM32微控制器**进行**模数转换(ADC)**、**数字滤波**、**特征提取**(如心率、QRS波),最终实现**实时显示**、**数据存储**或**无线传输**。系统需满足**高增益(1000倍以上)**、**低噪声(<1μV)**、**抗干扰(工频/肌电噪声)** 要求,适用于**便携式监护仪**、**远程医疗**、**运动健康**等场景。 ## 二、系统架构与硬件设计 ### 1. 系统架构 ```mermaid graph TD A[心电电极] -->|微弱信号 mV级| B[前端调理电路]
B –>|放大滤波后信号| C[STM32 ADC]
C –>|数字信号| D[STM32主控]
D –>|处理/分析| E[OLED显示]
D –>|存储| F[SD卡]
D –>|传输| G[蓝牙/Wi-Fi]
H[电源管理] –>|3.3V| B
H –>|3.3V| C
H –>|3.3V| D
H –>|3.3V| E
H –>|3.3V| F
H –>|3.3V| G
“`

### 2. 关键硬件组件

| **模块** | **型号/参数** | **功能** |
| ———— | ——————————– | ———————————- |
| **主控** | STM32F103C8T6(72MHz,12位ADC) | 信号采集、滤波、心率计算、控制外设 |
| **前端调理** | AD8232(仪表放大器,增益1000) | 心电放大、带通滤波(0.5-40Hz) |
| **ADC** | STM32内置12位ADC(1μs转换时间) | 模拟信号→数字信号(采样率250Hz) |
| **显示** | 0.96寸OLED(128×64,I2C) | 实时显示ECG波形、心率值 |
| **存储** | MicroSD卡(SPI接口,8GB) | 存储ECG数据(CSV格式) |
| **通信** | HC-05蓝牙模块(UART,2.4GHz) | 无线传输数据至手机APP |
| **电源** | 锂电池(3.7V/500mAh)+TP4056充电 | 系统供电,支持充电与低功耗管理 |

### 3. 核心电路设计

#### (1)前端调理电路(AD8232)

– **功能**:将心电电极采集的**0.5-5mV微弱信号**放大至**0-3.3V**(STM32 ADC输入范围),同时滤除**工频干扰(50Hz)** 和**肌电噪声(>100Hz)**。

– **电路连接**:
– 电极输入:右臂(RA)、左臂(LA)、右腿(RL,参考地)三导联;

– 输出:AD8232的`OUTPUT`引脚接STM32的`PA0`(ADC1通道0);

– 配置:增益=1000(通过`GAIN`引脚设置),带宽=0.5-40Hz(内部带通滤波)。

#### (2)STM32 ADC采集电路

– **采样率**:250Hz(心电信号最高频率100Hz,按Nyquist定理需≥200Hz);

– **分辨率**:12位(4096级量化,满足心电精度需求);

– **触发方式**:定时器TIM2触发(每4ms触发一次ADC转换,对应250Hz);

– **数据传输**:DMA(直接内存访问)将ADC数据自动传输至数组,减少CPU干预。

## 三、软件设计(C语言实现)

### 1. 主程序流程

“`c
#include “stm32f10x.h”
#include “ad8232.h”
#include “adc_dma.h”
#include “filter.h”
#include “heart_rate.h”
#include “oled.h”
#include “sd_card.h”
#include “bluetooth.h”

// 系统状态
typedef struct {
uint8_t is_sampling; // 采样标志
uint16_t adc_buffer[250]; // 1秒ADC数据(250Hz×1s)
float ecg_signal[250]; // 滤波后心电信号
uint8_t heart_rate; // 心率值(bpm)
} SystemState;

int main(void) {
// 1. 初始化硬件
System_Init(); // 时钟、GPIO、中断
AD8232_Init(); // 前端调理电路初始化
ADC_DMA_Init(250); // ADC+DMA初始化(250Hz采样)
OLED_Init(); // OLED显示初始化
SD_Card_Init(); // SD卡初始化
Bluetooth_Init(); // 蓝牙初始化

SystemState sys_state = {0};
sys_state.is_sampling = 1;

// 2. 主循环
while (1) {
if (sys_state.is_sampling) {
// 2.1 启动ADC采样(DMA传输)
ADC_Start_Conversion();

// 2.2 等待采样完成(1秒数据)
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);

// 2.3 数字滤波(去噪、基线校正)
for (int i=0; i<250; i++) { sys_state.ecg_signal[i] = Filter_Process((float)sys_state.adc_buffer[i]); } // 2.4 心率计算(QRS波检测) sys_state.heart_rate = HeartRate_Calculate(sys_state.ecg_signal, 250); // 2.5 显示与存储 OLED_Display_ECG(sys_state.ecg_signal, 250); // 显示ECG波形 OLED_Display_HR(sys_state.heart_rate); // 显示心率 SD_Card_Save_Data(sys_state.ecg_signal, 250); // 存储至SD卡 Bluetooth_Send_Data(sys_state.ecg_signal, 250); // 蓝牙传输 // 2.6 低功耗管理(空闲时休眠) if (should_enter_sleep()) { Enter_Sleep_Mode(1000); // 休眠1秒 } } } } ``` ### 2. 关键模块实现 #### (1)ADC+DMA配置(信号采集) ```c // ADC+DMA初始化(250Hz采样,12位分辨率) void ADC_DMA_Init(uint16_t sample_rate) { ADC_InitTypeDef ADC_InitStruct; DMA_InitTypeDef DMA_InitStruct; TIM_TimeBaseInitTypeDef TIM_InitStruct; // 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 2. 配置ADC通道(PA0,ADC1通道0) GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入 GPIO_Init(GPIOA, &GPIO_InitStruct); // 3. 配置ADC ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; ADC_InitStruct.ADC_ScanConvMode = DISABLE; // 单通道 ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // 单次转换(由TIM触发) ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; // TIM2触发 ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStruct); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_DMACmd(ADC1, ENABLE); // 使能ADC DMA // 4. 配置DMA(ADC1→内存) DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)sys_state.adc_buffer;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = 250; // 250个采样点(1秒)
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16位ADC数据
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel1, ENABLE);

// 5. 配置TIM2触发(4ms周期,250Hz)
TIM_InitStruct.TIM_Period = 72000/250 – 1; // 72MHz/250=288kHz,周期=288k/250=1152? 修正:72MHz/250Hz=288000,TIM分频后计数
TIM_InitStruct.TIM_Prescaler = 0;
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 1;
TIM_OC2Init(TIM2, &TIM_OCInitStruct);
TIM_Cmd(TIM2, ENABLE);
}
“`

#### (2)数字滤波(去噪与基线校正)

心电信号需滤除**50Hz工频干扰**和**基线漂移(<0.5Hz)**,采用**IIR陷波滤波+移动平均滤波**: ```c // 50Hz陷波滤波器(IIR) #define NOTCH_FREQ 50.0f #define FS 250.0f // 采样率 #define Q 30.0f // 品质因数 float notch_filter(float input) { static float x1=0, x2=0, y1=0, y2=0; float omega = 2 * 3.1415926f*NOTCH_FREQ/FS; float alpha = sin(omega)/(2*Q); float b0 = 1/(1+alpha); float b1 = -2*cos(omega)*b0; float b2 = b0; float a1 = -2*cos(omega)*b0; float a2 = (1-alpha)*b0; float output = b0*input + b1*x1 + b2*x2 - a1*y1 - a2*y2; x2 = x1; x1 = input; y2 = y1; y1 = output; return output; } // 移动平均滤波(窗口大小5) #define MA_WINDOW 5 float ma_filter(float input) { static float buffer[MA_WINDOW] = {0}; static uint8_t index=0; static float sum=0; sum -= buffer[index]; buffer[index] = input; sum += input; index = (index+1)%MA_WINDOW; return sum/MA_WINDOW; } // 总滤波函数 float Filter_Process(float adc_value) { // 1. ADC值→电压(0-3.3V) float voltage = (adc_value / 4095.0f) * 3.3f; // 2. 去直流偏移(AD8232输出含0.5V偏置) voltage -= 0.5f; // 3. 陷波滤波(去50Hz工频) float notched = notch_filter(voltage); // 4. 移动平均滤波(去高频噪声) float filtered = ma_filter(notched); return filtered; } ``` #### (3)心率计算(QRS波检测) 通过**阈值法**检测QRS波(心电图中幅度最大的波群),计算相邻QRS波时间间隔,换算为心率: ```c // 心率计算(输入:滤波后心电信号,长度N;输出:心率bpm) uint8_t HeartRate_Calculate(float* signal, uint16_t N) { #define THRESHOLD 0.5f // 检测阈值(根据实际信号调整) #define MIN_INTERVAL 0.3f // 最小RR间期(秒,对应200bpm) #define MAX_INTERVAL 2.0f // 最大RR间期(秒,对应30bpm) uint16_t peak_indices[10] = {0}; // 存储QRS波位置 uint8_t peak_count = 0; float max_val = 0; uint16_t max_index = 0; // 1. 寻找QRS波峰值(简单阈值法) for (int i=1; i signal[i-1] && signal[i] > signal[i+1] && signal[i] > THRESHOLD) {
if (peak_count < 10) { peak_indices[peak_count++] = i; } } } // 2. 计算平均RR间期(秒) if (peak_count < 2) return 0; // 无有效峰值 float rr_sum = 0; for (int i=1; i MIN_INTERVAL && interval < MAX_INTERVAL) { rr_sum += interval; } } float avg_rr = rr_sum / (peak_count - 1); // 3. 计算心率(bpm=60/avg_rr) uint8_t hr = (uint8_t)(60.0f / avg_rr); return (hr > 200) ? 0 : hr; // 限幅
}
“`

参考代码 基于STM32的心电采集系统(硬件+软件+上位机+设计报告等) www.youwenfan.com/contentcns/160880.html

## 四、系统测试与优化

### 1. 测试指标

| **参数** | **指标** | **测试方法** |
| ———— | ——————- | ————————————– |
| 输入信号范围 | 0.5-5mV(心电电极) | 函数发生器模拟心电信号(0.5-5mV@50Hz) |
| 放大倍数 | 1000倍(AD8232) | 输入1mV信号,测量输出电压(≈1V) |
| 采样率 | 250Hz | 示波器测量ADC触发信号周期(4ms) |
| 心率测量误差 | ±5bpm(静息状态) | 对比医用ECG仪测量结果 |
| 工频抑制比 | >40dB(50Hz) | 输入50Hz干扰信号,测量输出衰减 |

### 2. 优化方向

– **抗干扰**:使用**屏蔽线**连接电极,STM32金属外壳接地,软件增加**自适应滤波**(如LMS算法);

– **低功耗**:采样间隙关闭ADC、OLED背光,STM32进入**停止模式**(电流<10μA); - **算法优化**:采用**Pan-Tompkins算法**(更精确的QRS波检测),提升心率测量精度; - **数据存储**:压缩ECG数据(如差分编码),延长SD卡存储时间。 ## 五、总结 本设计基于STM32实现了心电信号的**采集-滤波-分析-显示**全流程,核心亮点: 1. **前端调理**:采用AD8232集成放大滤波,简化电路设计; 2. **高效采集**:ADC+DMA实现250Hz无丢失采样; 3. **实时处理**:数字滤波(陷波+移动平均)与QRS波检测算法实时计算心率; 4. **便携扩展**:支持OLED显示、SD卡存储、蓝牙传输,适用于便携式监护场景。 系统可进一步优化为**多导联ECG**(增加电极)、**云端互联**(通过ESP8266接入云平台),满足临床诊断需求。

专注于matlab/simulink,电子电路,编程