当前位置: 首页 > article >正文

【ESP32】ESP-IDF开发 | DAC数模转换器+余弦波输出例程

1. 简介

        ESP32上的数字模拟转换器 (DAC) 带有 2 个 8 位通道,因此可输出2路模拟信号。在低功耗模式下也可由 ULP 协处理器通过控制寄存器来实现完全控制。内部自带余弦波形生成器,可用于生成余弦波形/正弦波形,用户可调整频率、振幅、相位和直流偏移

2. 例程

        例程中使用到一个串口示波器上位机,下载地址在这:VOFA+

2.1 余弦波生成

        这个例程使用DAC生成一个余弦波,然后连接ADC进行捕获,捕获的数据发到上位机中。

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "driver/dac_cosine.h"
#include "soc/soc_caps.h"

#include <string.h>

#define TAG "app"

adc_oneshot_unit_handle_t adc_handle = NULL;
dac_cosine_handle_t dac_handle = NULL;
int adc_raw = 0;


int app_main()
{
    /* 初始化DAC */
    dac_cosine_config_t cos_cfg = {
        .chan_id = DAC_CHAN_0,  // 通道0
        .freq_hz = 1000,  // 1kHZ
        .clk_src = DAC_COSINE_CLK_SRC_DEFAULT,  // 默认时钟源,RTC_FAST_CLK
        .offset = 0,  // DC偏移
        .phase = DAC_COSINE_PHASE_0,
        .atten = DAC_COSINE_ATTEN_DEFAULT,  // 不衰减
        .flags.force_set_freq = false,
    };
    ESP_ERROR_CHECK(dac_cosine_new_channel(&cos_cfg, &dac_handle));
    ESP_ERROR_CHECK(dac_cosine_start(dac_handle));

    /* 初始化ADC */
    adc_oneshot_unit_init_cfg_t adc_init = {
        .unit_id = ADC_UNIT_1,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&adc_init, &adc_handle));

    adc_oneshot_chan_cfg_t adc_cfg = {
        .bitwidth = ADC_BITWIDTH_12,
        .atten = ADC_ATTEN_DB_12,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_0, &adc_cfg));

    while(1) {
        ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, ADC_CHANNEL_0, &adc_raw));
        printf("adc:%d\n", adc_raw);
        vTaskDelay(1);
    }
}

        DAC的余弦生成器初始化结构体定义如下:

typedef struct {
    dac_channel_t chan_id;
    uint32_t freq_hz;
    dac_cosine_clk_src_t clk_src;
    dac_cosine_atten_t atten;
    dac_cosine_phase_t phase;
    int8_t offset;
    struct {
        bool force_set_freq: 1;
    } flags;
} dac_cosine_config_t;
  • chan_id:DAC通道(0-1);
  • freq_hz:输出频率,建议设置为120-200000之间,单位HZ;
  • clk_src:时钟源,默认为RTC_FAST;
  • atten:信号衰减;
  • phase:相位;
  • offset:直流偏移;
  • force_set_freq:强制设置频率。

        ADC我使用单次采集模式,配置ADC1,通道0,12db衰减。主循环中不断读取ADC值,并发送到串口。最后加一个delay防止看门狗超时。需要注意的是,ADC采集的频率一定要高于DAC输出频率的两倍或以上(采样定律),不然波形会失真

2.2 三角波生成

        生成其他的波形需要使用DAC的连续输出功能。

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "driver/dac_continuous.h"
#include "soc/soc_caps.h"

#include <string.h>

#define TAG "app"

adc_oneshot_unit_handle_t adc_handle = NULL;
dac_continuous_handle_t dac_handle = NULL;
uint8_t wave[400];
int adc_raw = 0;


int app_main()
{
    /* 生成波形 */
    for (int i = 0; i < 400; i ++) {
        wave[i] = (i > (400 / 2)) ? (2 * 255 * (400 - i) / 400) : (2 * 255 * i / 400);
    }

    /* 初始化DAC */
    dac_continuous_config_t cont_cfg = {
        .chan_mask = DAC_CHANNEL_MASK_CH0,
        .desc_num = 8,
        .buf_size = 2048,
        .freq_hz = 20000,  // 400kHz
        .offset = 0,
        .clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
        .chan_mode = DAC_CHANNEL_MODE_SIMUL,
    };
    ESP_ERROR_CHECK(dac_continuous_new_channels(&cont_cfg, &dac_handle));
    ESP_ERROR_CHECK(dac_continuous_enable(dac_handle));
    ESP_ERROR_CHECK(dac_continuous_write_cyclically(dac_handle, wave, sizeof(wave), NULL));

    /* 初始化ADC */
    adc_oneshot_unit_init_cfg_t adc_init = {
        .unit_id = ADC_UNIT_1,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&adc_init, &adc_handle));

    adc_oneshot_chan_cfg_t adc_cfg = {
        .bitwidth = ADC_BITWIDTH_12,
        .atten = ADC_ATTEN_DB_12,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_0, &adc_cfg));

    while(1) {
        ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, ADC_CHANNEL_0, &adc_raw));
        printf("adc:%d\n", adc_raw);
        vTaskDelay(1);
    }
}

        DAC连续输出模式的初始化结构体定义如下:

typedef struct {
    dac_channel_mask_t chan_mask;
    uint32_t desc_num;
    size_t buf_size;
    uint32_t freq_hz;
    int8_t offset;
    dac_continuous_digi_clk_src_t clk_src;
    dac_continuous_channel_mode_t chan_mode;
} dac_continuous_config_t;
  • chan_mask:通道遮罩,即定义输出通道;
  • desc_num:DMA的描述符数量,必需是DMA缓冲区大小的整数倍
  • buf_size:DMA缓冲区大小,必需在32-4096之间
  • freq_hz:输出频率,因为分频因子最大是255,所以要根据选择的时钟源频率确定频率范围
  • offset:偏移,范围-128~127;
  • clk_src:时钟源,PLL时钟或2分频PLL时钟;
  • chan_mode:通道模式,连续或间隔模式。

        代码一开始需要定义一个数组,里面放要生成波形的一个周期的数据,当然数组越大波形的分辨率就越高。使能DAC后,通过dac_continuous_write_cyclically函数配置输出,这样驱动会连续地将数组值通过DMA输入到DAC中。ADC和串口打印的代码和上面一样。


http://www.kler.cn/a/427942.html

相关文章:

  • Spring RESTful API 设计与实现
  • 【AI绘画】MidJourney关键词{Prompt}全面整理
  • Agent 高频知识汇总:查漏补缺参考大全
  • MySQL注入中load_file()函数的使用
  • dify实现原理分析-rag-检索(Retrieval)服务的实现
  • java求职学习day18
  • Flink:入门介绍
  • deepsort复现报错TypeError: tuple indices must be integers or slices, not tuple 解决
  • CSES-1141 Playlist
  • RoformerBERT介绍
  • 架构10-可观测性
  • Unity 设计模式-观察者模式(Observer Pattern)详解
  • 3D 生成重建019-LERF用文本在Nerf中开启上帝之眼
  • 算法训练-位运算
  • Next.js系统性教学:服务器操作与数据变更
  • 毕设记录_论文阅读(动磁式音圈电机的开发与应用)_20241207
  • 保姆级教学 uniapp绘制二维码海报并保存至相册,真机正常展示图片二维码
  • SAP SD学习笔记19 - 形式发票(Proforma Invoice)
  • Oracle 11g ADG 单实例 DG Broker 配置指南
  • ubuntu20.04设置远程桌面
  • 深度全解析开放开源大模型之BLOOM
  • 牛客linux
  • Linux: shell: bash: Makefile:5: *** missing separator. Stop.
  • 过期策略、内存淘汰机制
  • 深入浅出:使用 Gin 框架生成 API 文档
  • 算法日记 41 day 图论