Skip to content

NOTES

Overview

Architecture 2 implements a DMA + Double Buffer (ping-pong buffer) pattern for high-frequency real-time sensor data acquisition and processing. This architecture is optimized for high sampling frequencies (0.1 - 10000 Hz, recommended: 1 kHz - 10 kHz) and provides low CPU usage through DMA-assisted SPI transfers.

Architecture Principles

DMA + Double Buffer Pattern

The architecture uses a ping-pong (double buffer) mechanism:

  • Buffer A and Buffer B: Two separate buffers that alternate between acquisition and processing
  • Producer Task: Fills one buffer while consumer processes the other
  • Consumer Task: Processes filled buffer while producer fills the other
  • Parallel Operation: Acquisition and processing can overlap in time
  • DMA Support: SPI transfers use DMA automatically, reducing CPU load

Key Design Decisions

  1. Double Buffer (Ping-Pong): Two buffers allow parallel acquisition and processing
  2. Buffer Switching: When one buffer is full, producer switches to the other buffer
  3. Semaphore Signaling: Binary semaphores notify consumer when a buffer is ready
  4. Mutex Protection: Each buffer has its own mutex for thread-safe access
  5. DMA Automatic: ESP-IDF SPI driver automatically uses DMA for transfers

Implementation Details

Timer-Based Sampling

  • ESP Timer: Creates a periodic timer with period = 1,000,000 / sampling_frequency_hz microseconds
  • Timer Callback: Executes in timer context, notifies producer task via xTaskNotify
  • Immediate First Sample: Performs one sample immediately before starting periodic timer

Producer Task

Priority: 10 (High priority for timely acquisition)

Functionality:

  1. Waits for timer notification via xTaskNotifyWait
  2. Reads sensor data (DMA handles SPI transfer automatically)
  3. Prepares sample structure with timestamp
  4. Writes to active buffer (A or B) with mutex protection
  5. When buffer is full:
  6. Marks buffer as ready
  7. Switches to other buffer
  8. Signals consumer via semaphore
  9. Resets write index
  10. Updates acquisition statistics

Buffer Management:

  • Tracks active buffer (0 = A, 1 = B)
  • Writes sequentially to active buffer
  • Switches buffers when full
  • Detects overwrites if consumer hasn't processed buffer in time

Consumer Task

Priority: 8 (High priority for processing)

Functionality:

  1. Waits for buffer ready semaphore (either A or B)
  2. Takes mutex for the ready buffer
  3. Processes all samples in the buffer
  4. Marks buffer as processed and resets ready flag
  5. Updates processing statistics
  6. Outputs results (serial, MQTT, LCD)

Processing Logic:

  • Processes entire buffer (all samples)
  • Performs feature extraction, detection, etc.
  • Handles acceleration detection with LCD feedback
  • Outputs first sample via serial (if enabled)

Double Buffer Mechanism

Buffer Size: 512 samples per buffer (configurable via RT_DMA_DB_BUFFER_SIZE)

Memory: Both buffers allocated from PSRAM

Synchronization:

  • Mutex for each buffer (protects write/read access)
  • Binary semaphore for each buffer (signals when ready)
  • Volatile flags for buffer ready state

Operation Flow:

  1. Producer fills buffer A
  2. When buffer A is full, producer switches to buffer B and signals consumer
  3. Consumer processes buffer A while producer fills buffer B
  4. When buffer B is full, producer switches to buffer A and signals consumer
  5. Consumer processes buffer B while producer fills buffer A
  6. Cycle repeats

Double Buffering

Figure: Double buffer (ping-pong) mechanism showing parallel acquisition and processing

DMA Support

  • Automatic DMA: ESP-IDF SPI driver automatically uses DMA for transfers
  • CPU Reduction: DMA handles data transfer, freeing CPU for other tasks
  • Non-blocking: SPI operations are non-blocking with DMA

Data Flow

ESP Timer → Timer Callback → xTaskNotify → Producer Task (Core 0/1)
                                    Read Sensor (SPI + DMA)
                                    Active Buffer (A or B)
                                    Buffer Full → Switch Buffer
                                    Semaphore Signal → Consumer Task
                                    Process Buffer
                          ┌───────────────────┴───────────────────┐
                          ↓                   ↓                    ↓
                       Serial              MQTT                  LCD

Configuration

Default Configuration

#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

Configuration Parameters

  • Sampling Frequency: 0.1 - 10000 Hz (validated at initialization)
  • Buffer Size: 512 samples per buffer (fixed at compile time)
  • Total Memory: 512 × 2 × 32 bytes = 32 KB from PSRAM

Features

Advantages

  • Low CPU Usage: DMA handles SPI transfers automatically
  • High Throughput: Parallel acquisition and processing
  • Suitable for High Frequency: Optimized for 1 kHz - 10 kHz range
  • True Parallelism: Producer and consumer can operate simultaneously
  • No Queue Overhead: Direct buffer access, no queue management

Limitations

  • Higher Memory Usage: Requires two large buffers (double the memory)
  • Fixed Buffer Size: Buffer size is fixed at compile time
  • Buffer Overwrite Risk: If consumer cannot keep up, buffers may be overwritten

Performance Characteristics

Suitable Frequency Range

  • Valid Range: 0.1 - 10000 Hz (validated at initialization)
  • Recommended: 1 kHz - 10 kHz
  • Maximum: Up to 10 kHz (validated limit)
  • Minimum: 0.1 Hz (practical limit)

Resource Usage

  • Memory: Two buffers (512 samples × 2 × 32 bytes = 32 KB from PSRAM)
  • CPU: Low usage (DMA handles transfers)
  • Synchronization: Two mutexes and two binary semaphores

Usage Notes

  1. Initialization Order: Must call arch_dma_db_init() before arch_dma_db_set_sensor_handle()
  2. Sensor Handle: Must be set before starting
  3. Buffer Monitoring: Monitor overwrite_count in statistics to detect if consumer is falling behind
  4. Processing Latency: Processing latency depends on buffer fill time (buffer_size / sampling_frequency)
  5. DMA Configuration: Ensure SPI DMA is enabled in ESP-IDF configuration

Error Handling

  • Mutex Timeout: Producer drops sample if mutex cannot be acquired within 10ms
  • Buffer Not Initialized: Sample dropped if buffers not ready
  • Sensor Read Failure: Logged but does not stop processing
  • Task Creation Failure: Returns error, cleans up resources

Thread Safety

  • Mutex Protection: Each buffer has its own mutex for thread-safe access
  • Task Isolation: Producer and consumer tasks are properly isolated
  • Semaphore Signaling: Binary semaphores provide safe inter-task communication
  • Statistics: Updated atomically within mutex-protected sections