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

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/gptimer.h"
#include "driver/dac_oneshot.h"
#include "esp_log.h"
#include "dac_continuous_example.h"

#define EXAMPLE_TIMER_RESOLUTION    1000000                 // 1MHz，1个tick = 1us
#define EXAMPLE_WAVE_FREQ_HZ        50                      // 默认波形频率50Hz，不能太高
#define EXAMPLE_CONVERT_FREQ_HZ     (EXAMPLE_ARRAY_LEN * EXAMPLE_WAVE_FREQ_HZ) // DAC转换波形数组中每个数据的频率
#define EXAMPLE_TIMER_ALARM_COUNT   (EXAMPLE_TIMER_RESOLUTION / EXAMPLE_CONVERT_FREQ_HZ)    // 触发定时器报警回调的计数值

static const char *TAG = "dac continuous(timer)";
static const char wav_name[DAC_WAVE_MAX][15] = {"sine", "triangle", "sawtooth",  "square"};
static dac_oneshot_handle_t chan0_handle;
static dac_oneshot_handle_t chan1_handle;

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

/* 定时器中断服务例程 */
static bool IRAM_ATTR on_timer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
    static uint32_t point_cnt = 0;  // 用于计算一个波形的输出点数
    static uint32_t index = 0;      // 波形缓冲区的当前索引
    static dac_example_wave_type_t wav_sel = DAC_SINE_WAVE; // 从正弦波开始

    // 每CONFIG_EXAMPLE_WAVE_PERIOD_SEC秒切换波形
    if (point_cnt < EXAMPLE_CONVERT_FREQ_HZ * CONFIG_EXAMPLE_WAVE_PERIOD_SEC) {
        switch (wav_sel) {
        case DAC_SINE_WAVE:
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan0_handle, sin_wav[index]));
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan1_handle, sin_wav[index]));
            break;
        case DAC_TRIANGLE_WAVE:
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan0_handle, tri_wav[index]));
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan1_handle, tri_wav[index]));
            break;
        case DAC_SAWTOOTH_WAVE:
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan0_handle, saw_wav[index]));
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan1_handle, saw_wav[index]));
            break;
        case DAC_SQUARE_WAVE:
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan0_handle, squ_wav[index]));
            ESP_ERROR_CHECK(dac_oneshot_output_voltage(chan1_handle, squ_wav[index]));
            break;
        default:
            break;
        }
        point_cnt++;
        index++;
        index %= EXAMPLE_ARRAY_LEN;
    } else {
        wav_sel++;
        wav_sel %= DAC_WAVE_MAX;
        point_cnt = 0;
        index = 0;
        ESP_EARLY_LOGI(TAG, "%s wave start", wav_name[wav_sel]);
    }
    return false;
}

void example_dac_continuous_by_timer(void)
{
    gptimer_handle_t gptimer = NULL;
    gptimer_config_t timer_config = {
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        .direction = GPTIMER_COUNT_UP,
        .resolution_hz = EXAMPLE_TIMER_RESOLUTION, // 1MHz，1个tick = 1us
    };
    ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
    dac_oneshot_config_t dac0_cfg = {
        .chan_id = DAC_CHAN_0,
    };
    ESP_ERROR_CHECK(dac_oneshot_new_channel(&dac0_cfg, &chan0_handle));
    dac_oneshot_config_t dac1_cfg = {
        .chan_id = DAC_CHAN_1,
    };
    ESP_ERROR_CHECK(dac_oneshot_new_channel(&dac1_cfg, &chan1_handle));

    example_log_info(EXAMPLE_CONVERT_FREQ_HZ, EXAMPLE_WAVE_FREQ_HZ);

    gptimer_alarm_config_t alarm_config = {
        .reload_count = 0,
        .alarm_count = EXAMPLE_TIMER_ALARM_COUNT,
        .flags.auto_reload_on_alarm = true,
    };
    gptimer_event_callbacks_t cbs = {
        .on_alarm = on_timer_alarm_cb,
    };
    ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
    ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
    ESP_ERROR_CHECK(gptimer_enable(gptimer));
    ESP_ERROR_CHECK(gptimer_start(gptimer));
}
