使用 ESP32 DMA 和 I2S 高效進行 ADC 數據收集 | 完整指南
在本文中,我們將介紹如何在 ESP32 上使用 DMA(直接存儲器存取)和 I2S(互聯音頻串行)技術來高效地收集 ADC(模數轉換器)數據。通過 ESP32 DMA 的高效數據傳輸,我們可以大幅減少 CPU 負載並提升數據吞吐量,使其更適合高速度應用需求。這種方法顯著增強了 ESP32 在連續數據流應用中的表現,如音頻處理、實時數據監控以及高頻數據採樣等應用場景。
內容
簡介
在嵌入式系統中,數據收集的效率對於性能至關重要,特別是當你使用像 ESP32 這樣的微控制器時。如果您正在處理類比信號,ADC(模數轉換器)是將這些信號轉換為數字數據的必備工具。然而,當需要收集大量數據時,ESP32 的 DMA(直接存儲器訪問)和 I2S(互聯音頻串行)外設可以顯著提高這一過程的效率,通過減少 CPU 的負擔來優化數據吞吐量。
ESP32 DMA 和 I2S
直接存儲器訪問(DMA)
ESP32 DMA 是一種技術,允許外設(如 ADC)將數據直接傳輸到內存,繞過 CPU。這對於需要連續或大量數據收集的場景來說非常理想,因為它可以避免每次樣本傳輸時都打斷 CPU 的運行。對於 ADC 操作,使用 ESP32 DMA 可以確保你可以將 ADC 的樣本直接傳輸到內存緩衝區,而無需每個樣本都打斷 CPU。
I2S 接口
雖然 I2S 傳統上是用來進行音頻數據傳輸的,但它也可以用於從 ADC 高速傳輸數據。ESP32 配備了內建的 I2S接口,可以配置為各種任務,包括高速度的 ADC 數據收集。通過使用 I2S,我們可以利用它處理連續數據流的能力,這使得 I2S 成為與 DMA 配合的理想選擇,以實現高效的數據傳輸。
設置 I2S 和 DMA 進行 ADC 數據收集
步驟 1:配置 ADC
在設置 DMA 和 I2S 之前,我們需要配置 ESP32 上的 ADC。ESP32 擁有多個內建的 ADC 通道,可以用來對類比信號進行取樣。下面是如何配置 ADC 的示範代碼:
#include "driver/adc.h"
#include "esp_log.h"
#define ADC_WIDTH ADC_WIDTH_BIT_12
#define ADC_SAMPLE_RATE 5000 // 5k samples per second
void configure_adc() {
adc1_config_width(ADC_WIDTH);
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0);
}
這段代碼將 ADC1_CHANNEL_0 配置為以 12 位解析度 進行取樣,並設置衰減為 0dB(默認值)。
步驟 2:設置 I2S 進行數據傳輸
現在 ADC 配置完成,我們將設置 I2S 進行高速數據傳輸。I2S 需要配置為從 ADC 收集數據並將其存儲到內存中。
#include "driver/i2s.h"
#define I2S_NUM I2S_NUM_0
#define BUFFER_SIZE 1024
void configure_i2s() {
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX, // Set I2S as a receiver
.sample_rate = ADC_SAMPLE_RATE, // Sample rate, should match ADC sample rate
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // We use only one channel for ADC data
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = BUFFER_SIZE
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
// Connect the I2S interface to the ADC
i2s_pin_config_t pin_config = {
.ws_io_num = I2S_PIN_NO_CHANGE,
.bck_io_num = I2S_PIN_NO_CHANGE,
.data_in_num = ADC1_CHANNEL_0
};
i2s_set_pin(I2S_NUM, &pin_config);
}
步驟 3:配置 DMA
一旦 I2S 設置完成後,需要配置 DMA 來將數據從 ADC 傳輸到內存,並且不需要 CPU 的干預。您需要將 I2S 驅動程序設置為使用 DMA 緩衝區,並確保數據能夠直接寫入內存。
ESP32 的 I2S 驅動程序會自動管理 DMA 緩衝區,因此不需要單獨進行 DMA 配置,但您仍然需要確保分配給 DMA 緩衝區的內存足夠大,以處理您的數據流,避免緩衝區溢出。
#define DMA_BUFFER_SIZE 1024
int16_t dma_buffer[DMA_BUFFER_SIZE];
void start_adc_reading() {
size_t bytes_read;
while (1) {
// Read data into DMA buffer
i2s_read(I2S_NUM, dma_buffer, DMA_BUFFER_SIZE * sizeof(int16_t), &bytes_read, portMAX_DELAY);
// Process data in dma_buffer...
ESP_LOGI("ADC", "Data read: %d", dma_buffer[0]);
}
}
在這個範例中,i2s_read() 函數從 I2S 接口中讀取數據並將其存儲到 DMA 緩衝區中。這些數據隨後可以進行處理,而不需要 CPU 的干預。
數據收集與處理
一旦您設置了 DMA 和 I2S,數據收集就變得非常簡單。從 ADC 收集的數據將被存儲在 DMA 緩衝區中,您可以即時處理這些數據或將其存儲以便稍後使用。以下是一個簡單的循環,用來從緩衝區讀取並處理 ADC 值:
void process_adc_data() {
while (1) {
size_t bytes_read;
// Read data from DMA buffer
i2s_read(I2S_NUM, dma_buffer, DMA_BUFFER_SIZE * sizeof(int16_t), &bytes_read, portMAX_DELAY);
// Process the data (e.g., store or analyze)
ESP_LOGI("ADC", "ADC Value: %d", dma_buffer[0]);
}
}
完整程式碼
完整 ESP32 程式碼範例,它包括 ADC、I2S、DMA 的設置及數據收集,並使用 ESP_LOGI 打印 ADC 數據至串列監視器,以便即時查看讀取的數值。
#include <stdio.h>
#include "driver/adc.h"
#include "driver/i2s.h"
#include "esp_log.h"
#define ADC_WIDTH ADC_WIDTH_BIT_12 // ADC resolution: 12 bits
#define ADC_SAMPLE_RATE 5000 // ADC sample rate: 5000 samples per second
#define I2S_NUM I2S_NUM_0 // I2S peripheral number
#define BUFFER_SIZE 1024 // DMA buffer size
#define DMA_BUFFER_SIZE 1024 // Length of DMA buffer
static const char *TAG = "ADC_I2S_DMA"; // Log tag
// DMA buffer for ADC data
int16_t dma_buffer[DMA_BUFFER_SIZE];
// Configure the ADC
void configure_adc() {
// Set ADC width to 12 bits
adc1_config_width(ADC_WIDTH);
// Set channel attenuation
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0);
ESP_LOGI(TAG, "ADC configured");
}
// Configure I2S
void configure_i2s() {
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX, // I2S as master, in receive mode
.sample_rate = ADC_SAMPLE_RATE, // Sample rate
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // 16-bit samples
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // Single channel
.communication_format = I2S_COMM_FORMAT_I2S, // I2S communication format
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // Interrupt allocation
.dma_buf_count = 8, // Number of DMA buffers
.dma_buf_len = BUFFER_SIZE // DMA buffer size
};
// Initialize I2S driver
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
// Configure I2S pin mapping to the ADC
i2s_pin_config_t pin_config = {
.ws_io_num = I2S_PIN_NO_CHANGE,
.bck_io_num = I2S_PIN_NO_CHANGE,
.data_in_num = ADC1_CHANNEL_0 // Connect I2S data input to ADC channel
};
i2s_set_pin(I2S_NUM, &pin_config);
ESP_LOGI(TAG, "I2S configured");
}
// Start ADC data reading
void start_adc_reading() {
size_t bytes_read;
// Continuously read and log ADC data
while (1) {
// Use I2S to read data into the DMA buffer
i2s_read(I2S_NUM, dma_buffer, DMA_BUFFER_SIZE * sizeof(int16_t), &bytes_read, portMAX_DELAY);
// Log the first sample in the buffer (or process it further if needed)
ESP_LOGI(TAG, "ADC Data read: %d", dma_buffer[0]);
}
}
void app_main() {
// Configure ADC and I2S
configure_adc();
configure_i2s();
// Start reading data
start_adc_reading();
}
程式碼解釋
configure_adc()
:設置 ADC 解析度和衰減參數,使其以 12 位解析度讀取數據。configure_i2s()
:配置 I2S 作為數據接收器,設置取樣率、位深度和單通道格式。這裡i2s_set_pin()
將 I2S 接收數據管腳設置為 ADC 通道,以便從 ADC 獲取數據。start_adc_reading()
:利用 I2S 的 DMA 緩衝區從 ADC 讀取數據。i2s_read()
會自動填充dma_buffer
,且不需要 CPU 的干預。隨後用ESP_LOGI()
將第一筆讀取的數據打印到串口。app_main()
:主程序,首先調用configure_adc()
和configure_i2s()
來初始化 ADC 和 I2S 配置,然後進入start_adc_reading()
來開始數據收集。
執行結果
此程序會持續在串口打印由 ADC 讀取的數據 :
I (500) ADC_I2S_DMA: ADC configured
I (500) ADC_I2S_DMA: I2S configured
I (1000) ADC_I2S_DMA: ADC Data read: 1234
I (1500) ADC_I2S_DMA: ADC Data read: 1250
...
DMA 優勢
- 減少 CPU 負載:利用 DMA 將數據直接傳輸至內存,不必佔用 CPU,讓 CPU 能夠處理其他任務。
- 高吞吐量:DMA 和 I2S 提供快速、可靠的數據傳輸,適用於高速度數據收集。
- 高效數據收集:在最小延遲的情況下可大量收集 ADC 數據,適合即時應用。
實際應用場景
- 信號處理:在實時分析類比信號(如音頻信號)的應用中,可持續進行低延遲的取樣與處理。
- 傳感器數據記錄:在環境監控或工業數據記錄的長時間任務中,這種設置確保 ADC 讀數高效持續收集。
- 高速取樣:此技術適合需要高速取樣的應用場景,如醫療設備或電子測試工具。
結論
我們展示了如何使用 ESP32 DMA 和 I2S 高效地收集 ADC 數據。通過利用這些強大的外設,您可以將數據傳輸的工作從 CPU 中卸載,實現高速、低延遲的數據收集。這種方法非常適合需要實時數據收集和處理的項目,並且使得 ESP32 成為嵌入式應用的強大平台。無論您是構建傳感器陣列、音頻系統,還是只需要快速的 ADC 取樣,使用 ESP32 DMA 和 I2S 都是提高項目性能和效率的絕佳方式。