/*
 * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <inttypes.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/dac_channel.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_check.h"
#include "dac_continuous_example.h"

/**
 *  有两种方式可以连续将数字数据转换为模拟信号：
 *  - 使用定时器：在定时器中断中定期设置DAC电压
 *                这种方式，DAC可以达到相对较低的转换频率
 *                但与使用DMA相比，这不是一种高效的方式
 *  - 使用DMA：通过DMA传输数据缓冲区，
 *              转换频率由DMA传输速度控制
 *              这种方式，转换频率可以达到几MHz，
 *              但由于受DMA时钟源限制，无法达到很低的转换频率
 *  一般来说，推荐使用DMA，如果DMA外设被占用或所需的转换频率很低，
 *  则使用定时器代替
 */

/* ADC配置 */
#if CONFIG_IDF_TARGET_ESP32
#define EXAMPLE_DAC_CHAN0_ADC_CHAN          ADC_CHANNEL_8   // GPIO25，与DAC通道0相同
#define EXAMPLE_DAC_CHAN1_ADC_CHAN          ADC_CHANNEL_9   // GPIO26，与DAC通道1相同
#define EXAMPLE_ADC_WIDTH                   ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define EXAMPLE_DAC_CHAN0_ADC_CHAN          ADC_CHANNEL_6   // GPIO17，与DAC通道0相同
#define EXAMPLE_DAC_CHAN1_ADC_CHAN          ADC_CHANNEL_7   // GPIO18，与DAC通道1相同
#define EXAMPLE_ADC_WIDTH                   ADC_WIDTH_BIT_13
#endif
#define EXAMPLE_DAC_CHAN0_IO                DAC_CHAN0_GPIO_NUM             // DAC通道0的IO号
#define EXAMPLE_DAC_CHAN1_IO                DAC_CHAN1_GPIO_NUM             // DAC通道1的IO号
#define EXAMPLE_ADC_ATTEN                   ADC_ATTEN_DB_12

_Static_assert(EXAMPLE_DAC_AMPLITUDE < 256, "DAC精度为8位，不支持超过255的幅度");

static const char *TAG = "dac continuous";

uint8_t sin_wav[EXAMPLE_ARRAY_LEN];                      // 用于存储正弦波值
uint8_t tri_wav[EXAMPLE_ARRAY_LEN];                      // 用于存储三角波值
uint8_t saw_wav[EXAMPLE_ARRAY_LEN];                      // 用于存储锯齿波值
uint8_t squ_wav[EXAMPLE_ARRAY_LEN];                      // 用于存储方波值

static void example_generate_wave(void)
{
    uint32_t pnt_num = EXAMPLE_ARRAY_LEN;

    for (int i = 0; i < pnt_num; i ++) {
        sin_wav[i] = (uint8_t)((sin(i * CONST_PERIOD_2_PI / pnt_num) + 1) * (double)(EXAMPLE_DAC_AMPLITUDE) / 2 + 0.5);
        tri_wav[i] = (i > (pnt_num / 2)) ? (2 * EXAMPLE_DAC_AMPLITUDE * (pnt_num - i) / pnt_num) : (2 * EXAMPLE_DAC_AMPLITUDE * i / pnt_num);
        saw_wav[i] = (i == pnt_num) ? 0 : (i * EXAMPLE_DAC_AMPLITUDE / pnt_num);
        squ_wav[i] = (i < (pnt_num / 2)) ? EXAMPLE_DAC_AMPLITUDE : 0;
    }
}

void example_log_info(uint32_t conv_freq, uint32_t wave_freq)
{
    ESP_LOGI(TAG, "--------------------------------------------------");
#if CONFIG_EXAMPLE_DAC_CONTINUOUS_BY_DMA
    ESP_LOGI(TAG, "DAC continuous output by DMA");
#else
    ESP_LOGI(TAG, "DAC continuous output by timer");
#endif
    ESP_LOGI(TAG, "DAC channel 0 io: GPIO_NUM_%d", EXAMPLE_DAC_CHAN0_IO);
    ESP_LOGI(TAG, "DAC channel 1 io: GPIO_NUM_%d", EXAMPLE_DAC_CHAN1_IO);
    ESP_LOGI(TAG, "Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE");
    ESP_LOGI(TAG, "DAC conversion frequency (Hz): %"PRIu32, conv_freq);
    ESP_LOGI(TAG, "DAC wave frequency (Hz): %"PRIu32, wave_freq);
    ESP_LOGI(TAG, "--------------------------------------------------");
}

static void adc_monitor_task(void *args)
{
    adc_oneshot_unit_handle_t adc2_handle = (adc_oneshot_unit_handle_t)args;

    int chan0_val = 0;
    int chan1_val = 0;
    while (1) {
        /* 读取DAC输出电压 */
        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 channel 0 value: %4d\tDAC channel 1 value: %4d\n", chan0_val, chan1_val);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void app_main(void)
{
    example_generate_wave();
#if CONFIG_EXAMPLE_DAC_CONTINUOUS_BY_DMA
    /* 使用DMA输出2kHz波形 */
    example_dac_continuous_by_dma();
#else
    /* 使用定时器中断输出50Hz波形 */
    example_dac_continuous_by_timer();
#endif

    /* 设置ADC2通道，这些通道内部连接到DAC通道 */
    adc_oneshot_unit_handle_t adc2_handle;
    adc_oneshot_unit_init_cfg_t adc_cfg = {
        .unit_id = ADC_UNIT_2,
        .ulp_mode = false,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&adc_cfg, &adc2_handle));
    adc_oneshot_chan_cfg_t chan_cfg = {
        .atten = EXAMPLE_ADC_ATTEN,
        .bitwidth = ADC_BITWIDTH_DEFAULT,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, EXAMPLE_DAC_CHAN0_ADC_CHAN, &chan_cfg));
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, EXAMPLE_DAC_CHAN1_ADC_CHAN, &chan_cfg));
    /* 创建ADC监控任务以检测DAC引脚上的电压 */
    xTaskCreate(adc_monitor_task, "adc_monitor_task", 4096, adc2_handle, 5, NULL);
}
