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
|