ESP32基于espidf DAC输出正/余弦/三角波形

ESP32基于espidf DAC输出正/余弦/三角波形


  • 🔖ESP-IDF 版本:v5.4
  • ✨带DAC功能外设的ESP32型号:esp32、esp32s2.(ESP32S3 不带DAC)
  • ESP32 有 2 个 8-bit DAC 通道,将 2 路数字信号分别转化为 2 个模拟电压信号输出,两个通道可以独立地工作。
  • 通道1(GPIO25)和DAC通道2(GPIO26)用于数模转换。
  • DAC 输出范围(0-255).
  • ESP32 功能框图
    在这里插入图片描述

数/模转换器 (DAC)介绍

ESP32 有 2 个 8-bit DAC 通道,将 2 路数字信号分别转化为 2 个模拟电压信号输出,两个通道可以独立地工作。
DAC 电路由内置电阻串和 1 个缓冲器组成。这 2 个 DAC 可以作为参考电压使用。

  • 可配置 GPIO 25 和 GPIO 26 管脚用于数模转换。
DAC 的主要特性包括:

• 2个8位DAC通道
• 支持双通道的独立/同时转换
• 可从VDD3P3_RTC引脚获得电压参考
• 含有余弦波型发生器
• 支持DMA功能
• 可通过软件或SARADCFSM开始转换。更多信息,请见SARADC章节。
• 可由ULP协处理器通过控制寄存器来实现完全控制。请见ULP协处理器章节。

  • 双通道DAC的2个8位通道可实现独立配置,每个通道的输出模拟电压计算方式见下:
    在这里插入图片描述
  • DAC 的功能概况
    在这里插入图片描述
    • PDACn_DAC 拥有多个来源:余弦波形生成器、寄存器RTCIO_PAD_DACn_REG,及DMA。

  • 橙色:GPIO25,紫色:GPIO26,波形输出:

在这里插入图片描述

  • 余弦和三角波输出

在这里插入图片描述

📘所需组件包含

所需要使用到的组件esp_driver_dac esp_adc esp_timer

  • 🌿CMakeLists.txt:
idf_component_register(SRCS "dac_cosine_example_main.c"
                    INCLUDE_DIRS "."
                    PRIV_REQUIRES esp_driver_dac esp_adc esp_timer)

📝DAC输出正/余弦波形驱动代码:

正弦波形,是通过余弦波形相位偏移(90 度)得来。DAC 通道 1:GPIO25(余弦波输出),DAC 通道 2:GPIO26(正弦波输出)

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/dac_cosine.h"
#include "driver/dac_oneshot.h"
#include "esp_timer.h"

#define DAC_COSINE_FREQ 1000  // 余弦波频率 (Hz)
#define PHASE_OFFSET    90    // 正弦波与余弦波的相位差 (90度)

// 正弦波查表法(预计算正弦波值)
#define SINE_TABLE_SIZE 100
static uint8_t sine_table[SINE_TABLE_SIZE];

void generate_sine_table() {
    for (int i = 0; i < SINE_TABLE_SIZE; i++) {
        float angle = 2 * M_PI * i / SINE_TABLE_SIZE;
        sine_table[i] = (uint8_t)((sin(angle) + 1) * 127.5);  // 转换为DAC输出值 (0-255)
    }
}

void app_main(void)
{
    // 预生成正弦波表
    generate_sine_table();

    // 配置余弦波生成器 (DAC通道1, GPIO25)
    dac_cosine_handle_t cos_handle = NULL;
    dac_cosine_config_t cos_cfg = {
        .chan_id = DAC_CHAN_0,
        .freq_hz = DAC_COSINE_FREQ,
        .clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
        .offset = 0,
        .phase = DAC_COSINE_PHASE_0,
        .atten = DAC_COSINE_ATTEN_DEFAULT,
        .flags.force_set_freq = true,
    };
    ESP_ERROR_CHECK(dac_cosine_new_channel(&cos_cfg, &cos_handle));
    ESP_ERROR_CHECK(dac_cosine_start(cos_handle));

    // 配置DAC单次输出 (DAC通道2, GPIO26)
    dac_oneshot_handle_t sin_handle = NULL;
    dac_oneshot_config_t sin_cfg = {
        .chan_id = DAC_CHAN_1,
    };
    ESP_ERROR_CHECK(dac_oneshot_new_channel(&sin_cfg, &sin_handle));

    // 计算每个波形点的时间间隔
    uint32_t interval_us = 1000000 / (DAC_COSINE_FREQ * SINE_TABLE_SIZE);
    uint32_t last_time = esp_timer_get_time();
    int table_index = 0;

    while (1) {
        // 输出正弦波值到DAC通道2
        ESP_ERROR_CHECK(dac_oneshot_output_voltage(sin_handle, sine_table[table_index]));

        // 更新查表索引
        table_index = (table_index + 1) % SINE_TABLE_SIZE;

        // 等待下一个波形点的时间
        while (esp_timer_get_time() - last_time < interval_us) {
            // 空循环等待
        }
        last_time = esp_timer_get_time();
    }

    // 停止余弦波生成器(不会执行到这里)
    ESP_ERROR_CHECK(dac_cosine_stop(cos_handle));
    ESP_ERROR_CHECK(dac_cosine_del_channel(cos_handle));
    ESP_ERROR_CHECK(dac_oneshot_del_channel(sin_handle));
}

📝DAC输出正/余弦波形驱动代码和说明:

DAC 通道 1(GPIO25):使用 dac_cosine.h 硬件生成余弦波。
DAC 通道 2(GPIO26):使用 dac_oneshot.h 逐点输出三角波。

  • 余弦波生成:

使用 dac_cosine.h 配置 DAC 通道 1(GPIO25)生成余弦波。
通过 dac_cosine_config_t 设置频率、相位、衰减等参数。
调用 dac_cosine_start 启动余弦波生成。

  • 三角波生成:

使用 dac_oneshot.h 配置 DAC 通道 2(GPIO26)生成三角波。
通过查表法预计算三角波的值,并存储在 triangle_table 数组中。
在循环中逐点输出三角波值,并通过 esp_timer_get_time 控制输出速率。

  • 查表法:

三角波的上升沿和下降沿分别计算,并存储在 triangle_table 中。
通过查表法避免实时计算,提高代码效率。
时间控制:

  • 使用 esp_timer_get_time 获取当前时间,并通过空循环精确控制每个波形点的输出时间。
/*
 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/dac_cosine.h"
#include "esp_adc/adc_oneshot.h"
#include "driver/dac_oneshot.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_timer.h"
#include <math.h>


#if CONFIG_IDF_TARGET_ESP32
#define EXAMPLE_DAC_CHAN0_ADC_CHAN          ADC_CHANNEL_8   // GPIO25, same as DAC channel 0
#define EXAMPLE_DAC_CHAN1_ADC_CHAN          ADC_CHANNEL_9   // GPIO26, same as DAC channel 1
#define EXAMPLE_ADC_WIDTH                   ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define EXAMPLE_DAC_CHAN0_ADC_CHAN          ADC_CHANNEL_6   // GPIO17, same as DAC channel 0
#define EXAMPLE_DAC_CHAN1_ADC_CHAN          ADC_CHANNEL_7   // GPIO18, same as DAC channel 1
#define EXAMPLE_ADC_WIDTH                   ADC_WIDTH_BIT_13
#endif
#define EXAMPLE_ADC_ATTEN                   ADC_ATTEN_DB_12

#define DAC_COSINE_FREQ 1000  // 余弦波频率 (Hz)
#define DAC_SINE_FREQ   1000  // 正弦波频率 (Hz)
#define PHASE_OFFSET    90    // 正弦波与余弦波的相位差 (90度)

#define TRIANGLE_FREQ   1000  // 三角波频率 (Hz)
#define TRIANGLE_STEPS  100   // 三角波的步数

// 三角波查表法(预计算三角波值)
static uint8_t triangle_table[TRIANGLE_STEPS];
// 正弦波查表法(预计算正弦波值)
#define SINE_TABLE_SIZE 100
static uint8_t sine_table[SINE_TABLE_SIZE];

void generate_sine_table() {
    for (int i = 0; i < SINE_TABLE_SIZE; i++) {
        float angle = 2 * M_PI * i / SINE_TABLE_SIZE;
        sine_table[i] = (uint8_t)((sin(angle) + 1) * 127.5);  // 转换为DAC输出值 (0-255)
    }
}



void generate_triangle_table() {
    for (int i = 0; i < TRIANGLE_STEPS / 2; i++) {
        // 上升沿
        triangle_table[i] = (uint8_t)(255 * i / (TRIANGLE_STEPS / 2));
    }
    for (int i = TRIANGLE_STEPS / 2; i < TRIANGLE_STEPS; i++) {
        // 下降沿
        triangle_table[i] = (uint8_t)(255 * (TRIANGLE_STEPS - i) / (TRIANGLE_STEPS / 2));
    }
}

static void adc_monitor_task(void *args)
{
    /* Set the ADC2 channels, these channels are connected to the DAC channels internally */
    adc_oneshot_unit_handle_t adc2_handle = (adc_oneshot_unit_handle_t)args;
    int chan0_val = 0;
    int chan1_val = 0;
    while (1) {
        /* Read the DAC output voltage */
        ESP_ERROR_CHECK(adc_oneshot_read(adc2_handle, EXAMPLE_DAC_CHAN0_ADC_CHAN, &chan0_val));
        ESP_ERROR_CHECK(adc_oneshot_read(adc2_handle, EXAMPLE_DAC_CHAN1_ADC_CHAN, &chan1_val));
        printf("DAC ch0-1: %4d,%4d\n", chan0_val, chan1_val);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void app_main(void)
{
    // 预生成正弦波表
   // generate_sine_table();
    // 预生成三角波表
    generate_triangle_table();
    // 配置余弦波生成器
    dac_cosine_handle_t  cos_handle = NULL;
    dac_cosine_config_t cos_cfg = {
        .chan_id = DAC_CHAN_0,  // DAC通道1 (GPIO25)
        .freq_hz = DAC_COSINE_FREQ,
        .clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
        .offset = 0,            // 直流偏移量
        .phase = DAC_COSINE_PHASE_0,  // 初始相位
        .atten = DAC_COSINE_ATTEN_DEFAULT,  // 衰减
        .flags.force_set_freq = true,  // 强制设置频率
    };
    ESP_ERROR_CHECK(dac_cosine_new_channel(&cos_cfg, &cos_handle));

    // 启动余弦波生成器
    ESP_ERROR_CHECK(dac_cosine_start(cos_handle));

    // 配置DAC单次输出(用于正弦波)
    // dac_oneshot_handle_t  sin_handle = NULL;
    // dac_oneshot_config_t  sin_cfg = {
    //     .chan_id = DAC_CHAN_1,  // DAC通道2 (GPIO26)
    // };
    // ESP_ERROR_CHECK(dac_oneshot_new_channel(&sin_cfg, &sin_handle));

    // 计算正弦波和余弦波的相位差
    // float phase_step = 360.0 / (DAC_COSINE_FREQ * 1000.0 / DAC_SINE_FREQ);
    // float phase = 0.0;
    // 计算每个波形点的时间间隔
// 配置DAC单次输出 (DAC通道2, GPIO26)
    dac_oneshot_handle_t tri_handle = NULL;
    dac_oneshot_config_t tri_cfg = {
        .chan_id = DAC_CHAN_1,
    };
    ESP_ERROR_CHECK(dac_oneshot_new_channel(&tri_cfg, &tri_handle));

    // 计算每个波形点的时间间隔
    uint32_t interval_us = 1000000 / (TRIANGLE_FREQ * TRIANGLE_STEPS);
    // 计算每个波形点的时间间隔
   // uint32_t interval_us = 1000000 / (DAC_COSINE_FREQ * SINE_TABLE_SIZE);
    uint32_t last_time = esp_timer_get_time();
    int table_index = 0;
    while (1) {

// 计算正弦波的值(通过相位偏移模拟)
        // float sine_value = cos((phase + PHASE_OFFSET) * M_PI / 180.0);  // 余弦函数模拟正弦波
        // uint8_t dac_sine = (uint8_t)((sine_value + 1) * 127.5);  // 转换为DAC输出值 (0-255)
// 输出正弦波值到DAC通道2
     //   ESP_ERROR_CHECK(dac_oneshot_output_voltage(sin_handle, sine_table[table_index]));

        // 输出正弦波到DAC通道2
      //  ESP_ERROR_CHECK(dac_oneshot_output_voltage(sin_handle, dac_sine));
       // 输出三角波值到DAC通道2
        ESP_ERROR_CHECK(dac_oneshot_output_voltage(tri_handle, triangle_table[table_index]));

        // 更新查表索引
        table_index = (table_index + 1) % TRIANGLE_STEPS;
        // 等待下一个波形点的时间
        while (esp_timer_get_time() - last_time < interval_us) {
            // 空循环等待
        }
        last_time = esp_timer_get_time();

    }
    // 停止余弦波生成器(不会执行到这里)
    // ESP_ERROR_CHECK(dac_cosine_stop(cos_handle));
    // ESP_ERROR_CHECK(dac_cosine_del_channel(cos_handle));
    // ESP_ERROR_CHECK(dac_oneshot_del_channel(sin_handle));
    //ESP_ERROR_CHECK(dac_oneshot_del_channel(tri_handle));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值