#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/adc.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"

#define LED_PIN  PICO_DEFAULT_LED_PIN   // LED
uint slice_num;

#define ADC0_PIN ADC_BASE_PIN
#define ADC1_PIN (ADC_BASE_PIN+1)
#define ADC2_PIN (ADC_BASE_PIN+2)

#define ADC_VREF 3.3f
#define ADC_SAMPLE_CLKS 96
#define SAMPLE_RATE 1000

typedef enum {
    adc_input0,
    adc_input1,
    adc_input2,
    adc_input3,
    adc_input4,
} _adc_input_t;

#define RW_BUFFER_SIZE 256

int adc_dma_ch;

volatile static uint8_t rw_buffer[2][RW_BUFFER_SIZE];
volatile static int write_buf_selector = 0;
volatile static int read_buf_selector = 1;

void adc_dma_irq_handler(void)
{
    // 割り込みフラグクリア
    dma_channel_acknowledge_irq0(adc_dma_ch);
    write_buf_selector = read_buf_selector ^ 1;
    // DMA転送を再トリガー
    dma_channel_set_write_addr(adc_dma_ch, rw_buffer[write_buf_selector], false);
    dma_channel_set_trans_count(adc_dma_ch, RW_BUFFER_SIZE, true);     
}

void pwm_irq_handler(void)
{
    static int counter = 0;
    pwm_clear_irq(slice_num);
    pwm_set_gpio_level(LED_PIN, (uint16_t)rw_buffer[read_buf_selector][counter++]);
    if(counter > RW_BUFFER_SIZE) {
        read_buf_selector = write_buf_selector ^ 1;
        counter = 0;
    }
}

int main()
{
    stdio_init_all();

    float adc_div, pwm_div;

    gpio_set_function(LED_PIN, GPIO_FUNC_PWM);
    pwm_config c = pwm_get_default_config();
    pwm_div = clock_get_hz(clk_sys) / SAMPLE_RATE / RW_BUFFER_SIZE;
    pwm_config_set_clkdiv(&c, pwm_div);
    pwm_config_set_wrap(&c, RW_BUFFER_SIZE);
    slice_num = pwm_gpio_to_slice_num(LED_PIN);
    pwm_init(slice_num, &c, false);
    pwm_set_gpio_level(LED_PIN, 0xFF );

    // PWM IRQ
    pwm_clear_irq(slice_num);
    pwm_set_irq_enabled(slice_num, true);
    irq_set_exclusive_handler(PWM_DEFAULT_IRQ_NUM(), pwm_irq_handler);
    irq_set_enabled(PWM_DEFAULT_IRQ_NUM(), true);

    // ADCの設定
    adc_init();
    adc_gpio_init(ADC0_PIN);
    // ADC FIFOの設定
    adc_fifo_setup(
        true,       // FIFOを有効化するならtrue
        true,      // DREQを有効化するならtrue
        1,          // DREQ、IRQを発生させるFIFOエントリ数
        false,      // 最上位ビットをエラーフラグにするならtrue
        true       // バイトシフトするならtrue
    );
    // ADCクロック分周比
    adc_div = (float)clock_get_hz(clk_adc) / (float)ADC_SAMPLE_CLKS / SAMPLE_RATE;
    adc_set_clkdiv(adc_div);
    printf("adc_div = %f\n", adc_div);

    adc_dma_ch = dma_claim_unused_channel(true);
    dma_channel_config dma_conf = dma_channel_get_default_config(adc_dma_ch);
    channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_8);
    channel_config_set_read_increment(&dma_conf, false);
    channel_config_set_write_increment(&dma_conf, true);
    channel_config_set_dreq(&dma_conf, DREQ_ADC);
    // DMAチャネル設定
    dma_channel_configure(
        adc_dma_ch,
        &dma_conf,
        (rw_buffer[0]),                 // 書き込み先はRW_BUFFER
        &adc_hw->fifo,                  // 読み出し元はRW_BUFFER
        RW_BUFFER_SIZE,                 // 転送回数
        false                           // 開始しない
    );
    dma_channel_set_irq0_enabled(adc_dma_ch, true);
    irq_set_exclusive_handler(DMA_IRQ_0, adc_dma_irq_handler);
    irq_set_enabled(DMA_IRQ_0, true);   

    dma_channel_start(adc_dma_ch);
    adc_run(true);

    pwm_set_enabled(slice_num, true);

    while (true) {
        tight_loop_contents();
    }
}
