#include "ch32fun.h"
#include <blink.h>
#include <stdio.h>

#define ADC_BUFFER_SIZE 32

// external defines.
volatile uint32_t *GPIO_PORT_BSHR = &GPIOD->BSHR;

// globals
volatile uint16_t adc_buffer[ADC_BUFFER_SIZE] = {0};
volatile uint16_t avg                         = 0;

// Function Prototypes.
void     ADC_DMA_Init(void);

void     GPIO_Init(void);
void     DMA_Init(void);
void     ADC_Init(void);

uint16_t ADC_Read(void)
{
    // while (!(ADC1->STATR & ADC_FLAG_EOC)); // Wait for conversion to complete
    return ADC1->RDATAR; // Return ADC value
}

int main(void)
{
    SystemInit();

    printf("DMA ADC TESTING\r\n");

    DMA_Init();
    GPIO_Init();
    ADC_Init();

    // set delay fp.
    // delay_ms = Delay_Ms;

    blink_led(5);

    uint16_t old_value = UINT16_MAX;

    printf("DMA_Channel1->CNTR: %d\r\n", (uint16_t)DMA1_Channel1->CNTR);

    if (!(ADC1->STATR & ADC_FLAG_EOC))
    {
        printf("ADC1 Status register EOC: False\r\n");
    }
    if (!(DMA1_Channel1->CFGR & DMA_CFGR1_EN))
    {
        printf("DMA1 Channel1 isn't enabled!\r\n");
    }

    /*
    while(1){
        // Start ADC conversion SWSTART bit 22
        //ADC1->CTLR2 |= (1<<22);
        printf("ADC reading: %d\r\n", ADC_Read());
        //Delay_Ms(1000);
    }
    */

    while (1)
    {
        printf("ADC_BUFFER: ");
        for (int i = 0; i < ADC_BUFFER_SIZE; i++)
        {
            printf("%d ", adc_buffer[i]);
        }
        printf("\r\n");
    }

    while (1)
    {
        // printf("ADC reading: %d\r\n", avg);
        // Delay_Ms(1000);
        if (avg != old_value)
        {
            old_value = avg;
            printf("ADC reading: %d\r\n", avg);
            Delay_Ms(500);
        }
    }
}

void GPIO_Init(void)
{
    printf("GPIO_Init()\r\n");

    // Enable the clock for the GPIO port it's on
    RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;

    // Configure the GPIO pin C4 as analog input. bits[19:16]
    // CFN(config): Analog, Mode: Input
    GPIOC->CFGLR &= ~(0xf << (4 * 4));
}

void ADC_Init(void)
{
    printf("ADC_Init()\r\n");

    // Enable the clock for the ADC
    RCC->APB2PCENR |= RCC_APB2Periph_ADC1;

    // Reset the ADC to init all regs
    RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
    RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;

    // I don't change the prescaler, because I'm not using an external crystal.
    // RCC->CFGR |= (0x1F<<11); //Sets the prescaler for div128.

    // Enable ADC scanning
    ADC1->CTLR1 |= ADC_SCAN;

    // Configure the ADC
    // -- Set the ADON bit.
    ADC1->CTLR2 |= (1 << 0);

    // -- Set the External selection to SWSTART
    ADC1->CTLR2 |= ADC_EXTSEL;

    // -- Enable DMA for the ADC DMA_Enable --> bit8
    ADC1->CTLR2 |= (1 << 8);

    // -- Set the ADC conversion for continuous
    ADC1->CTLR2 |= (1 << 1);

    // -- Set ADC sample time. 3 offset, channel 2
    // Sets the sampling to 3 cycles.
    ADC1->SAMPTR2 &= ~(0xf << (3 * 2));

    // Select ADC channel
    ADC1->RSQR1 = 0; // RSQR1 L num ch conversions = 1
    ADC1->RSQR2 = 0;
    ADC1->RSQR3 = 2;

    // Start ADC conversion SWSTART bit 22
    ADC1->CTLR2 |= (1 << 22);
}

void DMA_Init(void)
{
    // NOTE: Most of this could be a single line for the CFGR but this is more
    // explicit.
    // NOTE: See page 66 in the RM for figuring out the needed DMA channel.

    printf("DMA_Init()\r\n");

    // Enable the clock for dma1
    RCC->APB2PCENR |= RCC_AHBPeriph_DMA1;

    // Set the peripheral address
    DMA1_Channel1->PADDR = (uint32_t)&ADC1->RDATAR;

    // Set the memory address
    DMA1_Channel1->PADDR = (uint32_t)adc_buffer;

    // Set the amount of data to be transfered.
    DMA1_Channel1->CNTR = ADC_BUFFER_SIZE;

    // Set the DMA channel priority, bits[13:12]
    DMA1_Channel1->CFGR = 0;                         // clear it.
    DMA1_Channel2->CFGR &= ~((1 << 12) | (1 << 13)); // sets PL to low

    // Set the direction of data transfer, mode and datawidth for src & dst
    // along with th, tc and te interrupt enable bits.

    // Set the mem2mem as false.
    DMA1_Channel1->CFGR &= ~(1 << 14);

    // Set dir as Peripheral to memory.
    DMA1_Channel1->CFGR &= ~(1 << 4);

    // Set the datawidth for source and destination as 16bits.
    // DMA1_Channel1->CFGR |= (1<<10);
    // DMA1_Channel1->CFGR |= (1<<8);
    DMA1_Channel1->CFGR |= DMA_PeripheralDataSize_HalfWord;
    DMA1_Channel1->CFGR |= DMA_MemoryDataSize_HalfWord;

    // Set circular mode.
    DMA1_Channel1->CFGR |= (1 << 5);

    // Enable memory address increment.
    DMA1_Channel1->CFGR |= (1 << 7);

    // Enable IRQ
    // NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    // Set the transfer complete interrupt.
    DMA1_Channel1->CFGR |= (1 << 1);

    // Set the enable bit in DMA_CCRx register to start channel x
    DMA1_Channel1->CFGR |= (1 << 0);
}

void DMA1_Channel1_IRQHandler(void) __attribute__((interrupt));
void DMA1_Channel1_IRQHandler()
{
    if (DMA1->INTFR & DMA1_FLAG_TC1)
    {
        DMA1->INTFCR = DMA_CTCIF1;
        printf("DMA ISR!\r\n");
        // current_write_buffer = current_read_buffer;
        avg = 0;
        for (int i = 0; i < ADC_BUFFER_SIZE; i++)
        {
            avg += adc_buffer[i];
        }
        // Divide it by 32
        avg = avg >> 5;
        DMA_Init();
    }
}
/*
void DMA1_Channel1_IRQHandler(void) __attribute__((interrupt));
void DMA1_Channel1_IRQHandler(void)
{
    printf("DMA handler!\r\n");
    if (DMA1->INTFR & DMA_TCIF1) {  // Check Transfer Complete flag
        DMA1->INTFR |= DMA_TCIF1;   // Clear the flag
        // Process new ADC data in adc_buffer[]
        // Calculate average of data.
        avg = 0;
        for(int i = 0; i < ADC_BUFFER_SIZE; i++){
            avg += adc_buffer[i];
        }
        //Divide it by 32
        avg = avg >> 5;
    }
}
*/
