You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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无输出:

  1. 初始化顺序错误:构造函数中调用dac_continuous_write_cyclically时,handle_0还未初始化(Start()方法才会创建通道句柄),此时调用会导致无效操作。必须先创建通道句柄,再写入循环数据。
  2. 变量作用域问题:setup()中创建的DAC_Signal_DMA dac_Re_Im是局部变量,setup()执行完后会被销毁,析构函数会关闭DAC通道,导致后续无输出。需要改为全局变量或静态变量。
  3. 双通道数据格式问题:使用DAC_CHANNEL_MODE_SIMUL(同步模式)时,写入的数据需要是双通道交织格式,即每个数据单元包含通道0和通道1的采样值,而非单通道数组。
  4. 时钟源与频率匹配: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 13:44:54