ESP32连续DMA模式DAC无输出问题求助
问题描述
需要在ESP32的DAC输出端生成正弦和余弦信号,但当前DAC无信号输出。必须使用Continuous Wave Output mode (Continuous/DMA Mode),不考虑Cosine Wave Output mode。已参考官方文档和示例代码,使用PlatformIO开发,配置如下:
[env] ; Development platform for Espressif 32 series of microcontrollers platform = https://github.com/Jason2866/platform-espressif32.git #Arduino/IDF5 framework = arduino
提供的代码如下:
main.cpp
#include <Arduino.h> #include <ArduinoLog.h> #include "soc/rtc.h" #include "DAC_Signal_DMA.h" void setup() { Serial.begin(921600); delay(2000); // initialize logging Log.begin(LOG_LEVEL_VERBOSE, &Serial); Log.noticeln(F("%s() is configured"), __func__); DAC_Signal_DMA dac_Re_Im = DAC_Signal_DMA(); delay(2000); dac_Re_Im.Start(); delay(2000); Log.noticeln(F("DAC Signal DMA started")); } void loop() { // Just for testing Serial.printf("Free heap size: %d\n", esp_get_free_heap_size()); Serial.printf("%s(), core: %d: Running time [s]: %d\n", __func__, xPortGetCoreID(), millis()); vTaskDelay(pdMS_TO_TICKS(1000 - 0)); }
DAC_Signal_DMA.h
部分代码已注释(预留用于第二个DAC通道输出余弦信号),目前两个通道均配置为输出同一信号。
#include <math.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "soc/dac_channel.h" #include "driver/dac_continuous.h" // #include "esp_check.h" // #include "dac_continuous_example.h" #define EXAMPLE_DAC_CHAN0_IO DAC_CHAN0_GPIO_NUM // DAC channel 0 io number #define EXAMPLE_DAC_CHAN1_IO DAC_CHAN1_GPIO_NUM // DAC channel 1 io number #define EXAMPLE_ARRAY_LEN 500 // Length of wave array #define EXAMPLE_DAC_AMPLITUDE 255 // Amplitude of DAC voltage. If it's more than 256 will causes #define CONST_2_PI 6.2832 // 2 * PI _Static_assert(EXAMPLE_DAC_AMPLITUDE < 256, "The DAC accuracy is 8 bit-width, doesn't support the amplitude beyond 255"); class DAC_Signal_DMA { public: DAC_Signal_DMA() { generate_waves(); cont_cfg_0 = { .chan_mask = DAC_CHANNEL_MASK_ALL, .desc_num = 8, .buf_size = 2048, .freq_hz = frequency_convert_Hz, .offset = 0, .clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL' .chan_mode = DAC_CHANNEL_MODE_SIMUL, }; // cont_cfg_1 = { // .chan_mask = DAC_CHANNEL_MASK_CH1, // .desc_num = 8, // .buf_size = 2048, // .freq_hz = frequency_convert_Hz, // .offset = 0, // .clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL' // .chan_mode = DAC_CHANNEL_MODE_SIMUL, // }; ESP_ERROR_CHECK(dac_continuous_write_cyclically(handle_0, (uint8_t *)squ_wav, buf_len, NULL)); // ESP_ERROR_CHECK(dac_continuous_write_cyclically(handle_1, (uint8_t *)squ_wav, buf_len, NULL)); Serial.println("DAC_Signal_DMA() constructed"); } ~DAC_Signal_DMA() { Stop(); } void Start() { /* Allocate continuous channel */ ESP_ERROR_CHECK(dac_continuous_new_channels(&cont_cfg_0, &handle_0)); delay(1000); /* Enable the channels in the group */ ESP_ERROR_CHECK(dac_continuous_enable(handle_0)); delay(1000); // /* Allocate continuous channel */ // ESP_ERROR_CHECK(dac_continuous_new_channels(&cont_cfg_1, &handle_1)); // delay(1000); // /* Enable the channels in the group */ // ESP_ERROR_CHECK(dac_continuous_enable(handle_1)); // delay(1000); Serial.println("DAC_Signal_DMA() started"); } void Stop() { ESP_ERROR_CHECK(dac_continuous_disable(handle_0)); ESP_ERROR_CHECK(dac_continuous_del_channels(handle_0)); // ESP_ERROR_CHECK(dac_continuous_disable(handle_1)); // ESP_ERROR_CHECK(dac_continuous_del_channels(handle_1)); } protected: void generate_waves(void) { uint32_t pnt_num = EXAMPLE_ARRAY_LEN; for (int i = 0; i < pnt_num; i++) { sin_wav[i] = (uint8_t)((sin(i * CONST_2_PI / pnt_num) + 1) * (double)(amplitude) / 2 + 0.5); cos_wav[i] = (uint8_t)((cos(i * CONST_2_PI / pnt_num) + 1) * (double)(amplitude) / 2 + 0.5); squ_wav[i] = (i < (pnt_num / 2)) ? amplitude : 0; } } private: uint32_t frequency_Hz = 1000; uint32_t frequency_convert_Hz = EXAMPLE_ARRAY_LEN * frequency_Hz; size_t buf_len = EXAMPLE_ARRAY_LEN; uint8_t amplitude = EXAMPLE_DAC_AMPLITUDE; dac_continuous_config_t cont_cfg_0; dac_continuous_config_t cont_cfg_1; uint8_t sin_wav[EXAMPLE_ARRAY_LEN]; // Used to store sine wave values uint8_t cos_wav[EXAMPLE_ARRAY_LEN]; // Used to store cosine wave values uint8_t squ_wav[EXAMPLE_ARRAY_LEN]; // Used to store square wave values dac_continuous_handle_t handle_0 = NULL; dac_continuous_handle_t handle_1 = NULL; };
解决方案
代码存在几个核心问题,导致DAC无输出:
- 初始化顺序错误:构造函数中调用
dac_continuous_write_cyclically时,handle_0还未初始化(Start()方法才会创建通道句柄),此时调用会导致无效操作。必须先创建通道句柄,再写入循环数据。 - 变量作用域问题:
setup()中创建的DAC_Signal_DMA dac_Re_Im是局部变量,setup()执行完后会被销毁,析构函数会关闭DAC通道,导致后续无输出。需要改为全局变量或静态变量。 - 双通道数据格式问题:使用
DAC_CHANNEL_MODE_SIMUL(同步模式)时,写入的数据需要是双通道交织格式,即每个数据单元包含通道0和通道1的采样值,而非单通道数组。 - 时钟源与频率匹配:
DAC_DIGI_CLK_SRC_DEFAULT可能无法支持过高的采样频率,500kHz的采样率建议切换到DAC_DIGI_CLK_SRC_APLL以稳定输出。
以下是修正后的代码:
修正后的main.cpp
#include <Arduino.h> #include <ArduinoLog.h> #include "soc/rtc.h" #include "DAC_Signal_DMA.h" // 改为全局变量,避免setup结束后被销毁 DAC_Signal_DMA dac_Re_Im; void setup() { Serial.begin(921600); delay(2000); // initialize logging Log.begin(LOG_LEVEL_VERBOSE, &Serial); Log.noticeln(F("%s() is configured"), __func__); dac_Re_Im.Start(); delay(2000); Log.noticeln(F("DAC Signal DMA started")); } void loop() { // Just for testing Serial.printf("Free heap size: %d\n", esp_get_free_heap_size()); Serial.printf("%s(), core: %d: Running time [s]: %d\n", __func__, xPortGetCoreID(), millis()/1000); vTaskDelay(pdMS_TO_TICKS(1000)); }
修正后的DAC_Signal_DMA.h
#include <math.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "soc/dac_channel.h" #include "driver/dac_continuous.h" #define EXAMPLE_ARRAY_LEN 500 // Length of wave array #define EXAMPLE_DAC_AMPLITUDE 255 // DAC amplitude (0-255) #define CONST_2_PI 6.28318530718 // 2*PI,提高精度 _Static_assert(EXAMPLE_DAC_AMPLITUDE < 256, "DAC是8位精度,振幅不能超过255"); class DAC_Signal_DMA { public: DAC_Signal_DMA() { generate_interleaved_waves(); // 生成双通道交织数据 cont_cfg_0 = { .chan_mask = DAC_CHANNEL_MASK_ALL, .desc_num = 8, .buf_size = 2048, .freq_hz = EXAMPLE_ARRAY_LEN * frequency_Hz, .offset = 0, .clk_src = DAC_DIGI_CLK_SRC_APLL, // 切换到APLL,支持更高频率 .chan_mode = DAC_CHANNEL_MODE_SIMUL, }; Serial.println("DAC_Signal_DMA() constructed"); } ~DAC_Signal_DMA() { Stop(); } void Start() { // 1. 创建通道句柄 ESP_ERROR_CHECK(dac_continuous_new_channels(&cont_cfg_0, &handle_0)); // 2. 写入循环数据(必须在创建句柄之后) ESP_ERROR_CHECK(dac_continuous_write_cyclically(handle_0, interleaved_wav, buf_len, NULL)); // 3. 启用通道 ESP_ERROR_CHECK(dac_continuous_enable(handle_0)); Serial.println("DAC_Signal_DMA() started"); } void Stop() { if (handle_0) { ESP_ERROR_CHECK(dac_continuous_disable(handle_0)); ESP_ERROR_CHECK(dac_continuous_del_channels(handle_0)); handle_0 = NULL; } } private: void generate_interleaved_waves(void) { uint32_t pnt_num = EXAMPLE_ARRAY_LEN; // 交织格式:[CH0数据, CH1数据, CH0数据, CH1数据,...] for (int i = 0; i < pnt_num; i++) { // 正弦信号(通道0) uint8_t sin_val = (uint8_t)((sin(i * CONST_2_PI / pnt_num) + 1) * (double)amplitude / 2 + 0.5); // 余弦信号(通道1) uint8_t cos_val = (uint8_t)((cos(i * CONST_2_PI / pnt_num) + 1) * (double)amplitude / 2 + 0.5); interleaved_wav[2*i] = sin_val; interleaved_wav[2*i + 1] = cos_val; } } uint32_t frequency_Hz = 1000; size_t buf_len = EXAMPLE_ARRAY_LEN * 2; // 双通道交织,长度翻倍 uint8_t amplitude = EXAMPLE_DAC_AMPLITUDE; dac_continuous_config_t cont_cfg_0; uint8_t interleaved_wav[EXAMPLE_ARRAY_LEN * 2]; // 双通道交织数据缓存 dac_continuous_handle_t handle_0 = NULL; };
额外注意事项
- 确认ESP32的DAC引脚:通道0对应GPIO25,通道1对应GPIO26(不同型号可能有差异,比如ESP32-S2是GPIO17和GPIO18,需根据硬件调整)。
- 如果输出频率仍有问题,可以调整
desc_num(DMA描述符数量)和buf_size(缓存大小),确保DMA有足够数据可以传输。 - 若使用ESP-IDF 5.x版本,需确认
driver/dac_continuous.h的API是否与代码匹配,部分版本可能有细微调整。
内容的提问来源于stack exchange,提问作者Andrei Krivoshei
相关产品推荐
相关产品推荐

