跳转至

说明

概述

架构2实现了 DMA + 双缓冲(乒乓缓冲区)模式,用于高频实时传感器数据采集和处理。该架构针对高采样频率(0.1 - 10000 Hz,推荐:1 kHz - 10 kHz)进行了优化,通过 DMA 辅助的 SPI 传输提供低 CPU 使用率。

架构原理

DMA + 双缓冲模式

该架构使用乒乓(双缓冲)机制:

  • 缓冲区 A 和 B:两个独立的缓冲区,在采集和处理之间交替
  • Producer 任务:填充一个缓冲区,同时 Consumer 处理另一个
  • Consumer 任务:处理已填充的缓冲区,同时 Producer 填充另一个
  • 并行操作:采集和处理可以在时间上重叠
  • DMA 支持:SPI 传输自动使用 DMA,减少 CPU 负载

关键设计决策

  1. 双缓冲(乒乓):两个缓冲区允许并行采集和处理
  2. 缓冲区切换:当一个缓冲区满时,Producer 切换到另一个缓冲区
  3. 信号量通知:二进制信号量通知 Consumer 缓冲区已就绪
  4. 互斥锁保护:每个缓冲区有自己的互斥锁用于线程安全访问
  5. DMA 自动:ESP-IDF SPI 驱动自动使用 DMA 进行传输

实现细节

基于定时器的采样

  • ESP 定时器:创建周期定时器,周期 = 1,000,000 / sampling_frequency_hz 微秒
  • 定时器回调:在定时器上下文中执行,通过 xTaskNotify 通知 Producer 任务
  • 立即首次采样:在启动周期定时器之前执行一次采样

Producer 任务

优先级:10(高优先级,用于及时采集)

功能

  1. 通过 xTaskNotifyWait 等待定时器通知
  2. 读取传感器数据(DMA 自动处理 SPI 传输)
  3. 准备带时间戳的样本结构
  4. 在互斥锁保护下写入活动缓冲区(A 或 B)
  5. 当缓冲区满时:
  6. 标记缓冲区为就绪
  7. 切换到另一个缓冲区
  8. 通过信号量通知 Consumer
  9. 重置写索引
  10. 更新采集统计

缓冲区管理

  • 跟踪活动缓冲区(0 = A,1 = B)
  • 顺序写入活动缓冲区
  • 缓冲区满时切换缓冲区
  • 如果 Consumer 未及时处理,检测覆盖

Consumer 任务

优先级:8(高优先级,用于处理)

功能

  1. 等待缓冲区就绪信号量(A 或 B)
  2. 获取就绪缓冲区的互斥锁
  3. 处理缓冲区中的所有样本
  4. 标记缓冲区为已处理并重置就绪标志
  5. 更新处理统计
  6. 输出结果(串口、MQTT、LCD)

处理逻辑

  • 处理整个缓冲区(所有样本)
  • 执行特征提取、检测等
  • 处理带 LCD 反馈的加速度检测
  • 如果启用,通过串口输出第一个样本

双缓冲机制

缓冲区大小:每个缓冲区 512 样本(可通过 RT_DMA_DB_BUFFER_SIZE 配置)

内存:两个缓冲区都从 PSRAM 分配

同步

  • 每个缓冲区的互斥锁(保护写/读访问)
  • 每个缓冲区的二进制信号量(信号表示就绪)
  • 用于缓冲区就绪状态的易失性标志

操作流程

  1. Producer 填充缓冲区 A
  2. 当缓冲区 A 满时,Producer 切换到缓冲区 B 并通知 Consumer
  3. Consumer 处理缓冲区 A,同时 Producer 填充缓冲区 B
  4. 当缓冲区 B 满时,Producer 切换到缓冲区 A 并通知 Consumer
  5. Consumer 处理缓冲区 B,同时 Producer 填充缓冲区 A
  6. 循环重复

双缓冲

图:双缓冲(乒乓缓冲)机制示意图,显示并行采集和处理

DMA 支持

  • 自动 DMA:ESP-IDF SPI 驱动自动使用 DMA 进行传输
  • CPU 减少:DMA 处理数据传输,释放 CPU 用于其他任务
  • 非阻塞:使用 DMA 的 SPI 操作是非阻塞的

数据流

ESP 定时器 → 定时器回调 → xTaskNotify → Producer 任务(Core 0/1)
                                    读取传感器(SPI + DMA)
                                    活动缓冲区(A 或 B)
                                    缓冲区满 → 切换缓冲区
                                    信号量信号 → Consumer 任务
                                    处理缓冲区
                          ┌───────────────────┴───────────────────┐
                          ↓                   ↓                    ↓
                       串口                MQTT                  LCD

配置

默认配置

#define RT_DMA_DB_BUFFER_SIZE 512
#define RT_DMA_DB_PRODUCER_PRIORITY 10
#define RT_DMA_DB_CONSUMER_PRIORITY 8
#define RT_DMA_DB_PRODUCER_STACK_SIZE 4096
#define RT_DMA_DB_CONSUMER_STACK_SIZE 8192

配置参数

  • 采样频率:0.1 - 10000 Hz(初始化时验证)
  • 缓冲区大小:每个缓冲区 512 样本(编译时固定)
  • 总内存:512 × 2 × 32 字节 = 32 KB,来自 PSRAM

功能特性

优势

  • 低 CPU 使用率:DMA 自动处理 SPI 传输
  • 高吞吐量:并行采集和处理
  • 适用于高频:针对 1 kHz - 10 kHz 范围优化
  • 真正并行:Producer 和 Consumer 可以同时运行
  • 无队列开销:直接缓冲区访问,无队列管理

限制

  • 内存使用较高:需要两个大缓冲区(双倍内存)
  • 固定缓冲区大小:缓冲区大小在编译时固定
  • 缓冲区覆盖风险:如果 Consumer 无法跟上,缓冲区可能被覆盖

性能特征

适用频率范围

  • 有效范围:0.1 - 10000 Hz(初始化时验证)
  • 推荐:1 kHz - 10 kHz
  • 最大:最高 10 kHz(验证限制)
  • 最小:0.1 Hz(实际限制)

资源使用

  • 内存:两个缓冲区(512 样本 × 2 × 32 字节 = 32 KB,来自 PSRAM)
  • CPU:低使用率(DMA 处理传输)
  • 同步:两个互斥锁和两个二进制信号量

使用注意事项

  1. 初始化顺序:必须在 arch_dma_db_set_sensor_handle() 之前调用 arch_dma_db_init()
  2. 传感器句柄:启动前必须设置
  3. 缓冲区监控:监控统计中的 overwrite_count 以检测 Consumer 是否落后
  4. 处理延迟:处理延迟取决于缓冲区填充时间(buffer_size / sampling_frequency)
  5. DMA 配置:确保在 ESP-IDF 配置中启用了 SPI DMA

错误处理

  • 互斥锁超时:如果无法在 10ms 内获取互斥锁,Producer 丢弃样本
  • 缓冲区未初始化:如果缓冲区未就绪,丢弃样本
  • 传感器读取失败:记录日志但不停止处理
  • 任务创建失败:返回错误,清理资源

线程安全

  • 互斥锁保护:每个缓冲区有自己的互斥锁用于线程安全访问
  • 任务隔离:Producer 和 Consumer 任务正确隔离
  • 信号量通知:二进制信号量提供安全的任务间通信
  • 统计:在互斥锁保护的部分内原子更新