Electricity/UART电刺激控制系统技术文档.md

14 KiB
Raw Blame History

UART电刺激控制系统技术文档

1. 系统概述

本系统基于ENS1芯片实现UART通信控制的电刺激(EMS)功能,通过串口接收控制指令,实时调整电刺激参数并控制输出波形。

1.1 主要功能

  • UART数据接收与解析
  • 电刺激参数配置
  • 实时波形控制
  • 数据回传确认

1.2 技术特点

  • 中断驱动的UART通信
  • CRC校验确保数据完整性
  • 实时参数更新
  • 渐进式波形控制

2. 系统架构

2.1 文件结构

USER/
├── mian.c              # 主程序文件
├── ENS001_CONFIG.h     # 配置文件
└── MY_HEADER.h         # 自定义头文件

FWLIB/
├── source/
│   ├── ENS1_UART.c     # UART驱动实现
│   ├── ENS1_TIMER.c    # 定时器控制
│   └── ENS1_WAVEGEN.c  # 波形生成
└── include/
    └── ENS1_UART.h     # UART驱动头文件

2.2 核心模块

  • UART通信模块: 负责数据接收、解析和回传
  • 电刺激控制模块: 负责参数配置和波形控制
  • 定时器模块: 提供系统时钟基准
  • 波形生成模块: 生成电刺激输出波形

3. UART通信协议

3.1 数据包格式

总长度: 19字节
┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
│ 功能码  │ 功能码  │ 数据长度│ 数据长度│ 开关类型│ 强度    │ 频率    │ 频率    │ 持续时间│ 持续时间│ 休息时间│ 休息时间│ 静默时间│ 静默时间│ 缓进时间│ 保持时间│ 缓出时间│ CRC16   │ CRC16   │
│ (低字节)│ (高字节)│ (低字节)│ (高字节)│         │         │ (低字节)│ (高字节)│ (低字节)│ (高字节)│ (低字节)│ (高字节)│ (低字节)│ (高字节)│         │         │         │ (低字节)│ (高字节)│
└─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘

3.2 字段说明

字段 字节位置 长度 说明 示例值
功能码 0-1 2字节 固定值0x0003 0x0003
数据长度 2-3 2字节 数据部分长度 0x000D
开关类型 4 1字节 0x00=关闭, 0x10~0x1F=开启 0x10
强度 5 1字节 电刺激强度(0-255) 50
频率 6-7 2字节 电刺激频率(Hz) 100
持续时间 8-9 2字节 总持续时间(ms) 5000
休息时间 10-11 2字节 休息间隔(ms) 1000
静默时间 12-13 2字节 静默间隔(ms) 244
缓进时间 14 1字节 渐入时间(ms) 1
保持时间 15 1字节 保持时间(ms) 1
缓出时间 16 1字节 渐出时间(ms) 3
CRC16 17-18 2字节 CRC-16-CCITT-FALSE校验 0x90B7

3.3 字节序说明

  • 多字节字段采用小端序(Little Endian)
  • 低字节在前,高字节在后

4. 工作流程

4.1 系统初始化流程

graph TD
    A[系统启动] --> B[MTP初始化]
    B --> C[GPIO配置]
    C --> D[UART初始化]
    D --> E[定时器初始化]
    E --> F[电刺激模块初始化]
    F --> G[进入主循环]

4.2 UART数据接收流程

graph TD
    A[UART中断触发] --> B[读取FIFO数据]
    B --> C[存储到接收缓冲区]
    C --> D[回传数据确认]
    D --> E{数据包完整?}
    E -->|否| F[继续接收]
    E -->|是| G[解析数据包]
    G --> H{解析成功?}
    H -->|否| I[丢弃数据包]
    H -->|是| J[更新电刺激配置]
    J --> K[启动/停止电刺激]
    K --> L[清除缓冲区]

4.3 电刺激控制流程

graph TD
    A[接收UART数据] --> B[解析数据包]
    B --> C{开关类型检查}
    C -->|0x00| D[停止电刺激]
    C -->|0x10~0x1F| E[参数有效性检查]
    E --> F[创建新配置]
    F --> G[应用配置]
    G --> H[启动电刺激]
    H --> I[波形生成]

5. 电刺激缓进缓出实现

5.1 缓进缓出原理

电刺激的缓进缓出功能通过三个阶段实现平滑的强度变化:

5.1.1 三个阶段

  1. 缓进阶段 (Ramp Up): 强度从0逐渐增加到目标值
  2. 保持阶段 (Hold): 强度保持在目标值
  3. 缓出阶段 (Ramp Down): 强度从目标值逐渐减少到0

5.1.2 时间控制

// 时间参数(单位:毫秒)
uint32_t ramp_up_ms = g_ems_config.ramp_up_time * 1000;    // 缓进时间
uint32_t hold_ms = g_ems_config.hold_time * 1000;           // 保持时间  
uint32_t ramp_down_ms = g_ems_config.ramp_down_time * 1000; // 缓出时间

5.1.3 强度计算算法

  • 缓进阶段: target_intensity = (time_count * max_intensity) / ramp_up_ms
  • 保持阶段: target_intensity = max_intensity
  • 缓出阶段: target_intensity = max_intensity - (ramp_down_elapsed * max_intensity) / ramp_down_ms

5.2 缓进缓出实现代码

// 缓进缓出处理函数(在电刺激处理中调用)
void EMS_Process_Ramp(void)
{
    if(ems_state)
    { 
        time_count++;
        if (!g_ems_config.enable_ramp || !g_ems_running)
        {
            return; // 如果未启用缓进缓出或未运行,直接返回
        }
        
        // 计算各阶段时间(毫秒)
        uint32_t ramp_up_ms = g_ems_config.ramp_up_time * 1000;
        uint32_t hold_ms = g_ems_config.hold_time * 1000;
        uint32_t ramp_down_ms = g_ems_config.ramp_down_time * 1000;
        
        switch (g_ramp_phase)
        {
            case 0: // 缓进阶段
            {
                if (time_count <= g_ems_config.ramp_up_time * 1000)
                {
                    // 线性递增强度
                    uint16_t target_intensity = (time_count * g_ems_config.intensity) / ramp_up_ms;
                    if (target_intensity > g_ems_config.intensity) {
                        target_intensity = g_ems_config.intensity;
                    }
                    g_current_intensity = target_intensity;
                }
                else
                {
                    // 缓进完成,进入保持阶段
                    g_ramp_phase = 1;
                    g_current_intensity = g_ems_config.intensity;
                }
                break;
            }
            
            case 1: // 保持阶段
            {
                if(time_count <= (ramp_up_ms + hold_ms))
                {
                    g_current_intensity = g_ems_config.intensity;
                }
                else 
                {
                    g_ramp_phase = 2; // 进入缓出阶段
                }
                break;
            }

            case 2: // 缓出阶段
            {
                if(time_count <= (ramp_up_ms + hold_ms + ramp_down_ms))
                {
                    // 计算缓出阶段的时间偏移
                    uint32_t ramp_down_start = ramp_up_ms + hold_ms;
                    uint32_t ramp_down_elapsed = time_count - ramp_down_start;
                    
                    // 线性递减强度
                    uint16_t target_intensity = g_ems_config.intensity - 
                        (ramp_down_elapsed * g_ems_config.intensity) / ramp_down_ms;
                    
                    if (target_intensity > g_ems_config.intensity) {
                        target_intensity = 0;
                    }
                    g_current_intensity = target_intensity;
                }
                else
                {
                    // 缓出完成,停止电刺激
                    g_current_intensity = 0;
                    g_ramp_phase = 0; // 重置为缓进阶段
                }
                break;
            }
        }
    }
}

5.3 波形强度更新

// 更新波形强度(不重新配置硬件)
void wavegen_UpdateIntensity(CMSDK_WAVE_GEN_TypeDef *CMSDK_WAVEGEN_DRVA, uint16_t intensity)
{
    // 只更新波形数据,不重新配置硬件
    for (int i = 0; i < 64; i++)
    {
        CMSDK_WAVEGEN_DRVA->WAVE_GEN_DRV_IN_WAVE_ADDR_REG = i;
        CMSDK_WAVEGEN_DRVA->WAVE_GEN_DRV_IN_WAVE_DATA_REG = intensity;
    }
}

5.4 状态管理变量

// 全局状态变量
uint8_t g_ramp_phase = 0;        // 渐进阶段0=缓进, 1=保持, 2=缓出
uint32_t time_count = 0;          // 时间计数器(毫秒)
uint16_t g_current_intensity = 0; // 当前强度值
uint8_t g_ems_running = 0;        // 电刺激运行状态

5.5 缓进缓出流程图

graph TD
    A[开始电刺激] --> B[缓进阶段]
    B --> C{时间 < 缓进时间?}
    C -->|是| D[线性增加强度]
    C -->|否| E[进入保持阶段]
    D --> F[更新波形强度]
    F --> C
    E --> G{时间 < 保持时间?}
    G -->|是| H[保持最大强度]
    G -->|否| I[进入缓出阶段]
    H --> J[更新波形强度]
    J --> G
    I --> K{时间 < 缓出时间?}
    K -->|是| L[线性减少强度]
    K -->|否| M[停止电刺激]
    L --> N[更新波形强度]
    N --> K
    M --> O[重置状态]
    O --> A

6. 核心代码实现

6.1 UART中断处理函数

void UART1_Handler(void) {
    uint8_t rev_data = 0;
    uint32_t ParamNumber = 0;
    
    // 清除NVIC中断挂起位
    NVIC_ClearPendingIRQ(UART1_IRQn);
    
    // 检查中断类型
    uint8_t int_type = UART_INT_TYPE(CMSDK_UART1);
    
    // 数据就绪中断处理
    if((int_type == INT_RCV_DATA_AVAILABLE) || (int_type == INT_CHAR_TIMEOUT_INDICATION)) {
        ParamNumber = (CMSDK_UART1->FSR >> 16) & 0x1f;
        
        // 读取FIFO中的所有数据
        for(uint32_t i = 0; i < ParamNumber; i++) {
            rev_data = CMSDK_UART1->RBR;
            
            // 存储到缓冲区
            if(uart_rx_count < sizeof(uart_rx_buffer)) {
                uart_rx_buffer[uart_rx_count] = rev_data;
                uart_rx_count++;
            }
            
            // 回传数据确认
            UartPutc(CMSDK_UART1, rev_data);
        }
        
        // 检查完整数据包
        if(uart_rx_count >= 19) {
            UART_EMS_Packet_t ems_packet;
            if(ParseUART_EMS_Packet(uart_rx_buffer, uart_rx_count, &ems_packet)) {
                UpdateEMS_ConfigFromUART(&ems_packet);
            }
            uart_rx_count = 0; // 清除缓冲区
        }
    }
}

6.2 数据包解析函数

uint8_t ParseUART_EMS_Packet(uint8_t *data, uint16_t length, UART_EMS_Packet_t *packet) {
    // 解析数据包(小端序)
    packet->function_code = (data[1] << 8) | data[0];
    packet->data_length = (data[3] << 8) | data[2];
    packet->switch_type = data[4];
    packet->intensity = data[5];
    packet->frequency = (data[7] << 8) | data[6];
    packet->duration = (data[9] << 8) | data[8];
    packet->rest_time = (data[11] << 8) | data[10];
    packet->silent_time = (data[13] << 8) | data[12];
    packet->ramp_up_time = data[14];
    packet->hold_time = data[15];
    packet->ramp_down_time = data[16];
    packet->crc16 = (data[18] << 8) | data[17];
    
    // CRC校验当前已注释
    // uint16_t calculated_crc = CalculateCRC16_CCITT_FALSE(data, 17);
    // if(calculated_crc != packet->crc16) return 0;
    
    return 1; // 解析成功
}

6.3 电刺激配置更新函数

void UpdateEMS_ConfigFromUART(UART_EMS_Packet_t *packet) {
    // 检查开关状态
    if(packet->switch_type == 0x00) {
        EMS_Stop();
        return;
    }
    
    // 检查电刺激类型有效性
    if(packet->switch_type < 0x10 || packet->switch_type > 0x1F) {
        return;
    }
    
    // 创建新配置
    EMS_Config_TypeDef new_config = {
        .frequency = packet->frequency,
        .duration = packet->duration,
        .intensity = packet->intensity,
        .rest_time = packet->rest_time,
        .silent_time = packet->silent_time,
        .ramp_up_time = packet->ramp_up_time,
        .hold_time = packet->hold_time,
        .ramp_down_time = packet->ramp_down_time,
        .enable_ramp = 1
    };
    
    // 应用配置并启动
    EMS_Configure(&new_config);
    EMS_Start();
}

7. 关键数据结构

7.1 UART数据包结构

typedef struct {
    uint16_t function_code;    // 功能码 (0x0003)
    uint16_t data_length;      // 数据长度 (0x000D)
    uint8_t switch_type;       // 开关状态以及电刺激类型
    uint8_t intensity;         // 强度值
    uint16_t frequency;        // 频率值
    uint16_t duration;         // 总持续时间 (ms)
    uint16_t rest_time;        // 休息时间 (ms)
    uint16_t silent_time;      // 静默时间 (ms)
    uint8_t ramp_up_time;      // 缓进时间
    uint8_t hold_time;         // 保持时间
    uint8_t ramp_down_time;    // 缓出时间
    uint16_t crc16;            // CRC16校验
} UART_EMS_Packet_t;

7.2 电刺激配置结构

typedef struct {
    uint16_t frequency;        // 频率
    uint16_t duration;        // 持续时间
    uint8_t intensity;        // 强度
    uint16_t rest_time;       // 休息时间
    uint16_t silent_time;     // 静默时间
    uint8_t ramp_up_time;     // 缓进时间
    uint8_t hold_time;        // 保持时间
    uint8_t ramp_down_time;   // 缓出时间
    uint8_t enable_ramp;      // 启用渐进控制
} EMS_Config_TypeDef;

8. 调试与测试

8.1 调试输出

系统提供详细的调试输出,包括:

  • 接收到的原始数据
  • 解析后的参数值
  • 配置更新状态
  • 错误信息

8.2 测试用例

  1. 停止指令测试

    数据包: 03 00 0d 00 00 32 64 00 88 13 e8 03 f4 01 01 03 01 b7 90
    预期: 停止电刺激
    
  2. 启动指令测试

    数据包: 03 00 0d 00 10 32 64 00 88 13 e8 03 f4 01 01 03 01 [CRC]
    预期: 启动电刺激频率100Hz强度50