一、系统概述
心电采集系统通过电极阵列 采集人体心电信号(ECG),经信号调理 (放大、滤波)后,由STM32微控制器 进行模数转换(ADC) 、数字滤波 、特征提取 (如心率、QRS波),最终实现实时显示 、数据存储 或无线传输 。系统需满足高增益(1000倍以上) 、低噪声(<1μV) 、抗干扰(工频/肌电噪声) 要求,适用于便携式监护仪 、远程医疗 、运动健康等场景。
二、系统架构与硬件设计
1. 系统架构
微弱信号 mV级
放大滤波后信号
数字信号
处理/分析
存储
传输
3.3V
3.3V
3.3V
3.3V
3.3V
3.3V
心电电极
前端调理电路
STM32 ADC
STM32主控
OLED显示
SD卡
蓝牙/Wi-Fi
电源管理
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<N-1; i++) {
if (signal[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<peak_count; i++) {
float interval = (peak_indices[i] - peak_indices[i-1]) / FS; // 间隔(秒)
if (interval > 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/contentcss/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实现了心电信号的采集-滤波-分析-显示全流程,核心亮点:
-
前端调理:采用AD8232集成放大滤波,简化电路设计;
-
高效采集:ADC+DMA实现250Hz无丢失采样;
-
实时处理:数字滤波(陷波+移动平均)与QRS波检测算法实时计算心率;
-
便携扩展:支持OLED显示、SD卡存储、蓝牙传输,适用于便携式监护场景。
系统可进一步优化为多导联ECG (增加电极)、云端互联(通过ESP8266接入云平台),满足临床诊断需求。