【ESP32-IDF】基础外设开发4:DAC(IDF5.x新版驱动)
这篇文章摘要: 本文介绍了ESP32的DAC(数字模拟转换器)功能及应用。主要内容包括:1) DAC概述,解释了ESP32内置的两个DAC通道(GPIO25和GPIO26)及其特点;2) DAC类型定义和API接口,详细说明了单次模式、连续模式和余弦波模式的配置方法;3) 提供了DAC示例程序代码框架。文章特别指出ESP32-S3不支持内置DAC功能,示例代码适用于ESP32。通过本文,读者可以了
系列文章目录
持续更新…
前言
在嵌入式系统开发中,模拟信号的转换是非常常见的操作,尤其在物联网设备和传感器应用中,如何通过 DAC(数字到模拟转换器)将数字信号转换为模拟信号,以便与外部模拟设备通信。ESP32 提供了内置的 DAC 功能,可以方便地将数字数据转化为模拟信号进行输出。
本篇博文将详细介绍 ESP32 的 DAC 功能,帮助初学者理解 DAC 的工作原理和常见的配置方式,并通过示例代码展示如何在 ESP32 上实现 DAC 控制。
参考文档:ESP32-S3技术参考手册;ESP32-S3编程指南
一、DAC概述
DAC(数字到模拟转换器)是将数字信号转换为对应模拟信号的设备。在 ESP32 中,DAC 通常用于输出模拟信号,通过数字数据控制产生特定的电压值。ESP32 提供了两个 DAC 通道,可以分别通过 GPIO 25 和 GPIO 26 输出模拟信号。这两个 DAC 通道可以通过 DMA 功能进行高速数据传输。
ESP32 的 DAC 系统在多种应用中都能得到广泛使用,尤其是在需要输出音频信号、模拟电压波形等场景。其主要特点如下:
1.支持两通道 DAC 输出,分别映射到 GPIO 25 和 GPIO 26。
2.支持 DMA 功能,能够实现高速的数据传输和转换。
3.支持不同的输出频率和幅度调整,可通过配置参数灵活调节。
DAC 工作原理概述
ESP32 的 DAC 系统内部采用 SAR ADC(逐次逼近转换器),通过调节相关配置,可以生成不同幅度和频率的模拟信号。这些模拟信号可用于驱动外部硬件设备,如音响、传感器等。
二、DAC类型定义及相关API
需包含在 DAC 应用程序中的公共头文件包括:
dac_oneshot.h:新 DAC 驱动的最上层头文件,应包含在使用新驱动 API(单次模式)的应用程序中。
dac_cosine.h:新 DAC 驱动的最上层头文件,应包含在使用新驱动 API(余弦模式)的应用程序中。
dac_continuous.h:新 DAC 驱动的最上层头文件,应包含在使用新驱动 API(连续模式)的应用程序中。
DAC类型定义(新版)
// DAC 通道类型
typedef enum {
DAC_CHAN_0 = 0, // DAC 通道 0,GPIO25(ESP32)/GPIO17(ESP32-S2)
DAC_CHAN_1 = 1, // DAC 通道 1,GPIO26(ESP32)/GPIO18(ESP32-S2)
} dac_channel_t;
// DAC 连续模式时钟源类型
typedef enum {
DAC_DIGI_CLK_SRC_DEFAULT = 0, // 默认时钟源
DAC_DIGI_CLK_SRC_PLL_D2 = 1, // PLL D2 时钟源
DAC_DIGI_CLK_SRC_APLL = 2, // APLL 时钟源
} dac_continuous_digi_clk_src_t;
// DAC 连续模式通道工作模式
typedef enum {
DAC_CHANNEL_MODE_SIMUL = 0, // 同时输出模式
DAC_CHANNEL_MODE_ALTER = 1, // 交替输出模式
} dac_continuous_channel_mode_t;
// DAC 余弦波发生器幅度衰减
typedef enum {
DAC_COSINE_ATTEN_DEFAULT = 0, // 默认无衰减
DAC_COSINE_ATTEN_DB_0 = 1, // 无衰减
DAC_COSINE_ATTEN_DB_6 = 2, // 1/2 幅度
DAC_COSINE_ATTEN_DB_12 = 3, // 1/4 幅度
DAC_COSINE_ATTEN_DB_18 = 4, // 1/8 幅度
} dac_cosine_atten_t;
// DAC 余弦波发生器相位
typedef enum {
DAC_COSINE_PHASE_0 = 0, // 0° 相位
DAC_COSINE_PHASE_180 = 1, // 180° 相位
} dac_cosine_phase_t;
// DAC 连续模式配置结构体
typedef struct {
uint32_t chan_mask; // DAC 通道掩码,选择启用哪些 DAC 通道
uint32_t desc_num; // DMA 描述符数量
size_t buf_size; // DMA 缓冲区大小
uint32_t freq_hz; // DAC 转换频率(单位:Hz)
int8_t offset; // DAC 数据的偏移量
dac_continuous_digi_clk_src_t clk_src; // DAC 数字控制器时钟源
dac_continuous_channel_mode_t chan_mode; // DAC 通道工作模式
} dac_continuous_config_t;
DAC相关API(新版)
// ========================== 单次模式(One-shot) ==========================
// 分配一个新的 DAC 单次通道
esp_err_t dac_oneshot_new_channel(
const dac_oneshot_config_t *oneshot_cfg, // 输入参数:单次模式配置结构体指针
dac_oneshot_handle_t *ret_handle // 输出参数:返回的 DAC 单次通道句柄
);
// 删除一个 DAC 单次通道
esp_err_t dac_oneshot_del_channel(
dac_oneshot_handle_t handle // 输入参数:待删除的 DAC 单次通道句柄
);
// 输出指定 DAC 通道的电压值
esp_err_t dac_oneshot_output_voltage(
dac_oneshot_handle_t handle, // 输入参数:DAC 单次通道句柄
uint8_t digi_value // 输入参数:8 位数字值(0~255),对应模拟电压(0~Vref)
);
// ========================== 连续模式(Continuous/DMA) ==========================
// 分配新的 DAC 连续模式通道
esp_err_t dac_continuous_new_channels(
const dac_continuous_config_t *cont_cfg, // 输入参数:连续模式配置结构体指针
dac_continuous_handle_t *ret_handle // 输出参数:返回的 DAC 连续模式通道句柄
);
// 删除 DAC 连续模式通道
esp_err_t dac_continuous_del_channels(
dac_continuous_handle_t handle // 输入参数:待删除的 DAC 连续模式通道句柄
);
// 启用 DAC 连续模式
esp_err_t dac_continuous_enable(
dac_continuous_handle_t handle // 输入参数:DAC 连续模式通道句柄
);
// 禁用 DAC 连续模式
esp_err_t dac_continuous_disable(
dac_continuous_handle_t handle // 输入参数:DAC 连续模式通道句柄
);
// 向 DAC 连续模式写入数据
esp_err_t dac_continuous_write(
dac_continuous_handle_t handle, // 输入参数:DAC 连续模式通道句柄
uint8_t *buf, // 输入参数:待转换的数字数据缓冲区
size_t buf_size, // 输入参数:数字数据缓冲区大小
size_t *bytes_loaded, // 输出参数:已加载到 DMA 缓冲区的字节数(可为 NULL)
int timeout_ms // 输入参数:超时时间(毫秒),-1 表示无限等待
);
// 启动 DAC 连续模式的异步写入
esp_err_t dac_continuous_start_async_writing(
dac_continuous_handle_t handle // 输入参数:DAC 连续模式通道句柄
);
// 停止 DAC 连续模式的异步写入
esp_err_t dac_continuous_stop_async_writing(
dac_continuous_handle_t handle // 输入参数:DAC 连续模式通道句柄
);
// ========================== 余弦波模式(Cosine) ==========================
// 配置并启用 DAC 余弦波发生器
esp_err_t dac_cosine_new_channel(
const dac_cosine_config_t *cos_cfg, // 输入参数:余弦波配置结构体指针
dac_cosine_handle_t *ret_handle // 输出参数:返回的 DAC 余弦波通道句柄
);
// 启动 DAC 余弦波发生器
esp_err_t dac_cosine_start(
dac_cosine_handle_t handle // 输入参数:DAC 余弦波通道句柄
);
// 停止 DAC 余弦波发生器
esp_err_t dac_cosine_stop(
dac_cosine_handle_t handle // 输入参数:DAC 余弦波通道句柄
);
三、DAC示例程序
ESP32-S3 不支持内置 DAC 功能,参考代码使用的是 ESP32。
示例 1:单次输出模式(One-shot Mode)(IDF5.x新版驱动)
#include "driver/dac_oneshot.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define DAC_CHANNEL DAC_CHAN_0 // 使用通道 0(GPIO25)
void app_main(void)
{
esp_err_t ret;
dac_oneshot_handle_t dac_handle = NULL;
dac_oneshot_config_t oneshot_cfg = {
.chan_id = DAC_CHANNEL,
};
// 分配 DAC 单次通道
ret = dac_oneshot_new_channel(&oneshot_cfg, &dac_handle);
if (ret != ESP_OK)
{
ESP_LOGE("DAC", "Failed to allocate DAC channel");
return;
}
else
{
ESP_LOGI("DAC", "DAC channel allocated successfully");
}
// 输出电压值 128(约为 VDD3P3_RTC 的一半)
ret = dac_oneshot_output_voltage(dac_handle, 128);
if (ret != ESP_OK)
{
ESP_LOGE("DAC", "Failed to output voltage");
}
else
{
ESP_LOGI("DAC", "DAC output voltage set successfully");
}
// 删除 DAC 通道
ret = dac_oneshot_del_channel(dac_handle);
if (ret != ESP_OK)
{
ESP_LOGE("DAC", "Failed to delete DAC channel");
}
else
{
ESP_LOGI("DAC", "DAC channel deleted successfully");
}
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
示例 2:连续输出模式(Continuous Mode)(IDF5.x新版驱动)
#include "driver/dac_continuous.h"
#include "esp_log.h" // 用于日志输出
#include "freertos/FreeRTOS.h" // FreeRTOS相关
#include "freertos/task.h" // 任务延时函数
#include <math.h> // 数学函数(sin等)
// DAC配置参数
#define DAC_CHANNEL_MASK DAC_CHANNEL_MASK_CH0 // 使用通道 0(GPIO25)
#define SAMPLE_RATE 8000 // 采样率 8kHz(生成更流畅的波形)
#define BUFFER_SIZE 100 // DMA缓冲区大小(一个周期的采样点数)
#define OUTPUT_FREQ 80 // 输出信号频率 = SAMPLE_RATE / BUFFER_SIZE = 8000/100 = 80Hz
// 全局变量
uint8_t dma_buffer[BUFFER_SIZE]; // DMA缓冲区,存储波形数据
static const char *TAG = "DAC_CONTINUOUS"; // 日志标签
void generate_waveform(void)
{
ESP_LOGI(TAG, "Generating sine wave data...");
for (int i = 0; i < BUFFER_SIZE; i++)
{
// 生成正弦波:幅值范围0-255,中心值128
dma_buffer[i] = (uint8_t)(128 * (1 + sin(2 * M_PI * i / BUFFER_SIZE)));
}
ESP_LOGI(TAG, "Waveform generation completed. Buffer size: %d samples", BUFFER_SIZE);
}
void app_main(void)
{
esp_err_t ret;
dac_continuous_handle_t dac_handle = NULL;
// DAC连续模式配置结构体
dac_continuous_config_t cont_cfg = {
.chan_mask = DAC_CHANNEL_MASK, // 通道掩码:选择DAC通道0 (GPIO25)
.desc_num = 2, // DMA描述符数量:用于环形缓冲
.buf_size = BUFFER_SIZE, // DMA缓冲区大小:100个采样点
.freq_hz = SAMPLE_RATE, // 采样频率:8000Hz
.offset = 0, // 偏移量:0
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // 时钟源:默认
.chan_mode = DAC_CHANNEL_MODE_SIMUL, // 通道模式:同时模式
};
ESP_LOGI(TAG, "Starting DAC continuous output...");
// 分配 DAC 连续模式通道
ret = dac_continuous_new_channels(&cont_cfg, &dac_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to allocate DAC continuous channels: %s", esp_err_to_name(ret));
return;
}
ESP_LOGI(TAG, "DAC channels allocated successfully");
// 启用 DAC 连续模式
ret = dac_continuous_enable(dac_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to enable DAC continuous mode: %s", esp_err_to_name(ret));
// 清理资源
dac_continuous_del_channels(dac_handle);
return;
}
ESP_LOGI(TAG, "DAC continuous mode enabled");
// 生成波形数据并写入DMA缓冲区
generate_waveform();
// 循环连续输出波形数据
ESP_LOGI(TAG, "Starting continuous waveform output...");
while (1)
{
// 向 DMA 缓冲区写入数据(阻塞模式,超时时间-1表示永久等待)
ret = dac_continuous_write(dac_handle, dma_buffer, BUFFER_SIZE, NULL, -1);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to write data to DAC: %s", esp_err_to_name(ret));
break;
}
// 可选:添加少量延时以避免过于频繁的写入
vTaskDelay(pdMS_TO_TICKS(10));
}
// 程序结束时的清理工作(这段代码在无限循环中不会被执行到)
ESP_LOGI(TAG, "Stopping DAC continuous output...");
// 停止 DAC 连续模式
ret = dac_continuous_disable(dac_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to disable DAC continuous mode: %s", esp_err_to_name(ret));
}
// 删除 DAC 连续模式通道
ret = dac_continuous_del_channels(dac_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to delete DAC continuous channels: %s", esp_err_to_name(ret));
}
ESP_LOGI(TAG, "DAC continuous output stopped and resources cleaned up");
}
总结
本篇博文详细介绍了 ESP32 的新驱动 DAC 功能,包括其硬件原理、常见配置及如何使用新驱动 API(oneshot 和 continuous 模式)进行 DAC 控制。通过 API 示例,展示了如何在 ESP32 上输出数字到模拟信号的转换。
希望通过本篇博文,您能掌握 ESP32 DAC 的基本配置与应用,能够灵活地实现模拟信号的输出,适用于各种项目需求。欢迎大家交流与反馈。
更多推荐



所有评论(0)