389 lines
11 KiB
Markdown
389 lines
11 KiB
Markdown
# ECG十二导联信号生成系统 - 工作交接文档
|
||
|
||
## 项目概述
|
||
|
||
本项目是一个基于ESP32-S3的ECG(心电图)十二导联信号生成系统,能够同时生成并输出I、II、III导联以及V1-V6胸导联的ECG信号,通过DAC芯片输出到8个独立通道。
|
||
|
||
### 主要功能
|
||
- **十二导联ECG信号生成**:I、II、III、V1-V6导联
|
||
- **多通道DAC输出**:8个独立通道同时输出
|
||
- **高精度波形生成**:20kHz采样率,支持10,000点查找表
|
||
- **内存优化**:PSRAM存储,防止DRAM溢出
|
||
- **实时信号处理**:50μs定时器中断
|
||
|
||
---
|
||
|
||
## 系统架构
|
||
|
||
### 硬件平台
|
||
- **主控芯片**:ESP32-S3
|
||
- **DAC芯片**:AD5328BRUZ(8通道12位DAC)
|
||
- **通信接口**:SPI2(DAC控制)、I2C0(触摸屏)
|
||
- **显示**:ST7789 LCD + FT5x06触摸屏
|
||
|
||
### 软件架构
|
||
```
|
||
main/
|
||
├── main.c # 主程序入口
|
||
├── esp32_s3_szp.c # 硬件驱动(SPI、I2C、LCD)
|
||
├── esp32_s3_szp.h # 硬件定义
|
||
└── app_ui.c # UI界面
|
||
|
||
components/signal_generators/
|
||
├── include/
|
||
│ ├── ecg_generator.h # ECG生成器接口
|
||
│ └── sine_generator.h # 正弦波生成器接口
|
||
└── src/
|
||
├── ecg_generator.c # ECG生成器实现
|
||
└── sine_generator.c # 正弦波生成器实现
|
||
```
|
||
|
||
---
|
||
|
||
## 核心代码功能详解
|
||
|
||
### 1. ECG信号生成器 (`ecg_generator.c`)
|
||
|
||
#### 1.1 导联类型定义
|
||
```c
|
||
typedef enum {
|
||
ECG_LEAD_I = 0, // I导联:右手到左手
|
||
ECG_LEAD_II = 1, // II导联:右手到左腿
|
||
ECG_LEAD_III = 2, // III导联:左手到左腿
|
||
ECG_LEAD_V1 = 3, // V1导联:胸骨右缘第四肋间
|
||
ECG_LEAD_V2 = 4, // V2导联
|
||
ECG_LEAD_V3 = 5, // V3导联:过渡区
|
||
ECG_LEAD_V4 = 6, // V4导联:高R波
|
||
ECG_LEAD_V5 = 7, // V5导联
|
||
ECG_LEAD_V6 = 8 // V6导联
|
||
} ecg_lead_t;
|
||
```
|
||
|
||
#### 1.2 ECG生成器结构体
|
||
```c
|
||
typedef struct {
|
||
// 用户参数
|
||
float heart_rate; // 心率 (次/分)
|
||
float amplitude; // 幅值 (V)
|
||
ecg_lead_t lead; // 当前导联
|
||
|
||
// 内部状态
|
||
uint32_t sample_count; // 样本计数器
|
||
float sample_rate; // 采样率 (Hz)
|
||
bool is_running; // 运行状态
|
||
|
||
// 导联特定参数
|
||
float p_amp_ratio; // P波幅度比例
|
||
float q_amp_ratio; // Q波幅度比例
|
||
float r_amp_ratio; // R波幅度比例
|
||
float s_amp_ratio; // S波幅度比例
|
||
float t_amp_ratio; // T波幅度比例
|
||
bool p_bidirectional; // P波是否双向(V1导联特有)
|
||
|
||
// 查找表相关
|
||
float* ecg_lookup_table; // ECG查找表
|
||
uint32_t table_size; // 查找表大小
|
||
uint32_t table_index; // 当前索引
|
||
|
||
// 按比例重复采样(防止相位失真)
|
||
uint32_t ideal_table_size; // 理想表大小
|
||
uint32_t samples_per_point; // 每个表点对应的样本数
|
||
uint32_t sample_counter; // 当前表点的样本计数器
|
||
} ecg_generator_t;
|
||
```
|
||
|
||
#### 1.3 关键算法:按比例重复采样
|
||
|
||
**问题**:当理想表大小超过10,000点时,需要限制表大小防止内存溢出,但会导致相位失真。
|
||
|
||
**解决方案**:按比例重复采样
|
||
```c
|
||
// 计算理想表大小和实际表大小
|
||
generator->ideal_table_size = (uint32_t)(generator->sample_rate / generator->frequency);
|
||
const uint32_t MAX_TABLE_SIZE = 10000;
|
||
generator->table_size = generator->ideal_table_size;
|
||
if (generator->table_size > MAX_TABLE_SIZE) {
|
||
generator->table_size = MAX_TABLE_SIZE;
|
||
}
|
||
|
||
// 计算每个表点需要重复的样本数
|
||
generator->samples_per_point = generator->ideal_table_size / generator->table_size;
|
||
|
||
// 在get_next_sample中实现重复采样
|
||
if (generator->sample_counter >= generator->samples_per_point) {
|
||
generator->sample_counter = 0;
|
||
generator->table_index = (generator->table_index + 1) % generator->table_size;
|
||
}
|
||
```
|
||
|
||
#### 1.4 导联特定参数设置
|
||
|
||
每个导联都有独特的波形特征:
|
||
|
||
```c
|
||
void ecg_generator_set_lead_parameters(ecg_generator_t *generator, ecg_lead_t lead) {
|
||
switch (lead) {
|
||
case ECG_LEAD_I:
|
||
generator->p_amp_ratio = 0.12; // P波较小
|
||
generator->q_amp_ratio = 0.25;
|
||
generator->r_amp_ratio = 0.7; // R波中等
|
||
generator->s_amp_ratio = 0.3;
|
||
generator->t_amp_ratio = 0.3;
|
||
generator->p_bidirectional = false;
|
||
break;
|
||
|
||
case ECG_LEAD_V1:
|
||
generator->p_amp_ratio = 0.08; // P波双向
|
||
generator->q_amp_ratio = 0.0; // 无Q波
|
||
generator->r_amp_ratio = 0.25; // 小r波
|
||
generator->s_amp_ratio = 1.0; // 深S波
|
||
generator->t_amp_ratio = -0.15; // T波倒置
|
||
generator->p_bidirectional = true; // 双向P波
|
||
break;
|
||
|
||
// ... 其他导联
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 1.5 双向P波实现(V1导联特有)
|
||
|
||
```c
|
||
// 在波形计算中
|
||
if (generator->p_bidirectional) {
|
||
// 双向P波:先正后负
|
||
float p_positive = p_amp * expf(-(t_norm - p_time_ratio) * (t_norm - p_time_ratio) /
|
||
(2.0f * p_width_ratio * p_width_ratio));
|
||
float p_negative = -p_amp * 0.6f * expf(-(t_norm - (p_time_ratio + 0.02f)) *
|
||
(t_norm - (p_time_ratio + 0.02f)) /
|
||
(2.0f * p_width_ratio * p_width_ratio));
|
||
p = p_positive + p_negative;
|
||
} else {
|
||
// 单向P波
|
||
p = p_amp * expf(-(t_norm - p_time_ratio) * (t_norm - p_time_ratio) /
|
||
(2.0f * p_width_ratio * p_width_ratio));
|
||
}
|
||
```
|
||
|
||
### 2. 十二导联系统封装
|
||
|
||
#### 2.1 系统初始化函数
|
||
```c
|
||
int ecg_generator_init_twelve_leads_system(float sample_rate, float amplitude, float heart_rate) {
|
||
// 初始化所有9个导联生成器
|
||
ecg_generator_init(&g_ecg_gen_i, sample_rate, ECG_LEAD_I);
|
||
ecg_param_set(&g_ecg_gen_i, amplitude, heart_rate);
|
||
ecg_generator_generate_lookup_table(&g_ecg_gen_i);
|
||
ecg_generator_start(&g_ecg_gen_i);
|
||
|
||
// ... 重复其他8个导联
|
||
|
||
return 0; // 成功
|
||
}
|
||
```
|
||
|
||
#### 2.2 多通道输出函数
|
||
```c
|
||
void ecg_generator_output_twelve_leads(void (*set_channel_voltage)(uint8_t channel, float voltage)) {
|
||
float voltage;
|
||
|
||
// 通道映射:0通道输出I导联,1通道输出II导联,2-7通道输出V1-V6导联
|
||
voltage = ecg_generator_get_next_sample(&g_ecg_gen_i);
|
||
set_channel_voltage(0, voltage); // 通道0:I导联
|
||
|
||
voltage = ecg_generator_get_next_sample(&g_ecg_gen_ii);
|
||
set_channel_voltage(1, voltage); // 通道1:II导联
|
||
|
||
voltage = ecg_generator_get_next_sample(&g_ecg_gen_v1);
|
||
set_channel_voltage(2, voltage); // 通道2:V1导联
|
||
|
||
// ... 其他通道
|
||
}
|
||
```
|
||
|
||
### 3. 硬件驱动 (`esp32_s3_szp.c`)
|
||
|
||
#### 3.1 SPI初始化
|
||
```c
|
||
// SPI总线配置
|
||
spi_bus_config_t bus_config = {
|
||
.miso_io_num = GPIO_NUM_NC, // 不使用MISO
|
||
.mosi_io_num = PIN_MOSI, // GPIO12
|
||
.sclk_io_num = PIN_SCK, // GPIO13
|
||
.quadhd_io_num = GPIO_NUM_NC,
|
||
.quadwp_io_num = GPIO_NUM_NC,
|
||
};
|
||
|
||
// SPI设备配置
|
||
spi_device_interface_config_t dev_config = {
|
||
.clock_speed_hz = 1000000, // 1 MHz
|
||
.mode = 0, // SPI模式0
|
||
.spics_io_num = PIN_CS, // GPIO10
|
||
.queue_size = 1,
|
||
.flags = SPI_DEVICE_HALFDUPLEX, // 半双工
|
||
};
|
||
```
|
||
|
||
#### 3.2 DAC数据发送
|
||
```c
|
||
void set_channel_voltage(uint8_t channel, float voltage) {
|
||
// 电压转数字值(12位)
|
||
uint16_t dac_value = (uint16_t)((voltage / V_REF) * 4095);
|
||
|
||
// 组装SPI数据包(16位)
|
||
uint8_t tx_data[2] = {
|
||
((channel & 0x07) << 4) | ((dac_value >> 8) & 0x0F), // 高字节
|
||
(dac_value & 0xFF) // 低字节
|
||
};
|
||
|
||
// SPI事务
|
||
spi_transaction_t t = {
|
||
.length = 16,
|
||
.tx_buffer = tx_data,
|
||
.rx_buffer = NULL
|
||
};
|
||
|
||
esp_err_t ret = spi_device_transmit(handle, &t);
|
||
}
|
||
```
|
||
|
||
### 4. 主程序 (`main.c`)
|
||
|
||
#### 4.1 系统初始化流程
|
||
```c
|
||
void app_main(void) {
|
||
int led_count = 0;
|
||
|
||
// 1. 硬件初始化
|
||
init_hardware(); // I2C、LED、DAC初始化
|
||
|
||
// 2. ECG系统初始化
|
||
init_ecg_system(); // 初始化十二导联系统
|
||
|
||
// 3. 主循环
|
||
while (true) {
|
||
// LED状态指示
|
||
gpio_set_level(LED_PIN, 1);
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
gpio_set_level(LED_PIN, 0);
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
|
||
// 内存监控
|
||
if (++led_count % 10 == 0) {
|
||
displayMemoryUsage();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4.2 定时器中断处理
|
||
```c
|
||
static void signal_timer_callback(void* arg) {
|
||
// ECG十二导联模式:使用十二导联输出函数
|
||
ecg_generator_output_twelve_leads(set_channel_voltage);
|
||
|
||
signal_sample_count++;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 系统运行机制
|
||
|
||
### 1. 启动流程
|
||
1. **硬件初始化**:I2C、SPI、GPIO配置
|
||
2. **DAC初始化**:SPI总线、设备、控制引脚
|
||
3. **ECG系统初始化**:9个导联生成器、查找表生成
|
||
4. **定时器启动**:50μs周期(20kHz)
|
||
5. **主循环**:LED指示、内存监控
|
||
|
||
### 2. 实时信号生成
|
||
1. **定时器中断**:每50μs触发一次
|
||
2. **信号生成**:各导联生成器产生下一个样本
|
||
3. **DAC输出**:8个通道同时更新电压
|
||
4. **相位同步**:所有导联保持同步
|
||
|
||
### 3. 内存管理
|
||
- **查找表存储**:PSRAM(heap_caps_malloc with MALLOC_CAP_SPIRAM)
|
||
- **表大小限制**:最大10,000点防止DRAM溢出
|
||
- **按比例采样**:保持波形形状和相位连续性
|
||
|
||
### 4. 通道映射
|
||
```
|
||
DAC通道 → ECG导联
|
||
通道0 → I导联
|
||
通道1 → II导联
|
||
通道2 → V1导联
|
||
通道3 → V2导联
|
||
通道4 → V3导联
|
||
通道5 → V4导联
|
||
通道6 → V5导联
|
||
通道7 → V6导联
|
||
```
|
||
|
||
---
|
||
|
||
## 技术参数
|
||
|
||
### 性能指标
|
||
- **采样率**:20kHz
|
||
- **心率范围**:30-200 BPM
|
||
- **电压范围**:0-2.048V(12位分辨率)
|
||
- **查找表大小**:最大10,000点
|
||
- **内存使用**:PSRAM存储,避免DRAM溢出
|
||
|
||
### 硬件接口
|
||
- **SPI2**:DAC控制(MOSI:GPIO12, SCK:GPIO13, CS:GPIO10)
|
||
- **GPIO控制**:LDAC(GPIO11), DAC_EN(GPIO17)
|
||
- **I2C0**:触摸屏通信(SDA:GPIO1, SCL:GPIO2)
|
||
|
||
### 波形特征
|
||
- **I导联**:R波中等,P波较小
|
||
- **II导联**:R波最大,P波明显
|
||
- **III导联**:波形介于I和II之间
|
||
- **V1导联**:双向P波,深S波,T波倒置
|
||
- **V2-V6导联**:过渡区到高R波区域
|
||
|
||
---
|
||
|
||
## 维护和调试
|
||
|
||
### 1. 常见问题排查
|
||
- **内存不足**:检查PSRAM配置,调整查找表大小
|
||
- **SPI通信失败**:检查引脚配置和时钟频率
|
||
- **波形失真**:验证按比例采样算法
|
||
- **相位不同步**:检查定时器配置
|
||
|
||
### 2. 性能优化
|
||
- **内存优化**:使用PSRAM存储大查找表
|
||
- **实时性**:50μs定时器确保20kHz输出
|
||
- **精度优化**:12位DAC提供高精度输出
|
||
|
||
### 3. 扩展功能
|
||
- **参数调节**:心率、幅度、导联类型
|
||
- **波形存储**:支持不同ECG模式
|
||
- **多设备支持**:可扩展更多DAC通道
|
||
|
||
---
|
||
|
||
## 开发环境
|
||
|
||
### 编译环境
|
||
- **ESP-IDF**:v5.0+
|
||
- **编译器**:GCC for RISC-V
|
||
- **调试工具**:ESP-IDF Monitor
|
||
|
||
### 依赖组件
|
||
- **esp_lcd_port**:LCD显示
|
||
- **lvgl**:图形界面
|
||
- **esp_lcd_touch_ft5x06**:触摸屏
|
||
|
||
### 构建命令
|
||
```bash
|
||
idf.py build
|
||
idf.py flash monitor
|
||
---
|
||
|
||
|
||
**交接完成日期**:2025年10月9日
|
||
**系统版本**:v1.0 |