//******************************************** Включаемые файлы *********************************************

#include "lmcal_cfg.h"
#include "lmcal_adc_port.h"

#include "at32f403a_407_conf.h"


//******************************************* Локальные константы *******************************************

// Управление проверками аргументов функций
#define L_ADC_DEBUG     (1)

//******************************************* Проверка параметров *******************************************


//******************************************** Определения типов ********************************************


//************************************ Глобальные (публичные) переменные ************************************

adc_ch_service_t lmcal_adc_port_service_var[LMCAL_ADC_CHANNEL_QTY];


//************************************* Локальные (приватные) переменные ************************************

// Флаг инициализации DMA-ADC0
static bool b_init_adc0_dma_done = false;




//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************


static fun_res_t f_gpio_init_pin(u32 adc_num, u32 in_ch)
{
    fun_res_t err = ERR_OK;
    gpio_init_type stGPIO_InitStruct;
    L_UNUSED(adc_num);

    gpio_default_para_init(&stGPIO_InitStruct);
    stGPIO_InitStruct.gpio_mode = GPIO_MODE_ANALOG;

    switch(in_ch)
    {
        case(LMCAL_ADC_CH0):
            // PA0
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_0;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;
        
        case(LMCAL_ADC_CH1):
            // PA1
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_1;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH2):
            // PA2
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_2;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH3):
            // PA3
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_3;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH4):
            // PA4
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_4;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH5):
            // PA5
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_5;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH6):
            // PA6
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_6;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH7):
            // PA7
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_7;
            gpio_init(GPIOA, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH8):
            // PB0
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_0;
            gpio_init(GPIOB, &stGPIO_InitStruct);
        break;

        case(LMCAL_ADC_CH9):
            // PB1
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_1;
            gpio_init(GPIOB, &stGPIO_InitStruct);            
        break;

        case(LMCAL_ADC_CH10):
            // PC0
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_0;
            gpio_init(GPIOC, &stGPIO_InitStruct);            
        break;

        case(LMCAL_ADC_CH11):
            // PC1
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_1;
            gpio_init(GPIOC, &stGPIO_InitStruct);               
        break;

        case(LMCAL_ADC_CH12):
            // PC2
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_2;
            gpio_init(GPIOC, &stGPIO_InitStruct);               
        break;

        case(LMCAL_ADC_CH13):
            // PC3
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_3;
            gpio_init(GPIOC, &stGPIO_InitStruct);               
        break;

        case(LMCAL_ADC_CH14):
            // PC4
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_4;
            gpio_init(GPIOC, &stGPIO_InitStruct);               
        break;

        case(LMCAL_ADC_CH15):
            // PC5
            stGPIO_InitStruct.gpio_pins = GPIO_PINS_5;
            gpio_init(GPIOC, &stGPIO_InitStruct);               
        break;

        default:
            // Temp.sensor or Internal ref.voltage
        break;
    }

    return err;
}



static fun_res_t f_gpio_init(u32 adc_num,
                             u32 in_qty,
                             const u32* in_num)
{
    fun_res_t err = ERR_OK;
    u32 qty_tmp = 0;
    u32 cnt_tmp = 0;

    // Найти (in_qty) входов из массива (in_num) и выполнить инициализацию
    while(qty_tmp != in_qty)
    {
        // Проверка канала АЦП на наличие конфигурации
        if (in_num[cnt_tmp] != LMCAL_ADC_CH_NONE)
        {
            // Инициализация пина по номеру канала АЦП
            err = f_gpio_init_pin(adc_num, in_num[cnt_tmp]);
            L_CHECK_ERR(err);

            qty_tmp++;
        }

        cnt_tmp++;
        // Дошли до конца массива
        if (cnt_tmp >= 8)
        {
            // Проверка - инициализированы не все каналы?
            if (qty_tmp != in_qty)
            {
                err = ERR_INVALID_ARGUMENT;
            }
            break;
        }
    }

    L_EXIT;
    return err;
}



static fun_res_t f_adc_init(u32 adc_num,
                            u32 in_qty,
                            const u32* in_num,
                            u32 sample)
{
    fun_res_t err = ERR_OK;
    u32 qty_tmp = 0;
    u32 cnt_tmp = 0;

    // Найти (in_qty) входов из массива (in_num) и выполнить инициализацию
    while(qty_tmp != in_qty)
    {
        // Проверка канала АЦП на наличие конфигурации
        if (in_num[cnt_tmp] != LMCAL_ADC_CH_NONE)
        {
            qty_tmp++;

            // Добавить обычный канал
            adc_ordinary_channel_set((adc_type*) adc_num, 
                                     in_num[cnt_tmp],
                                     qty_tmp,
                                     sample);
        }

        cnt_tmp++;
        // Дошли до конца массива
        if (cnt_tmp >= 8)
        {
            // Проверка - инициализированы не все каналы?
            if (qty_tmp != in_qty)
            {
                err = ERR_INVALID_ARGUMENT;
            }
            break;
        }
    }
   
    return err;
}



static fun_res_t f_dma_adc_init(u32 adc_num,
                                u32 in_qty,
                                u32* dst_buf)
{
    fun_res_t err = ERR_OK;
    adc_base_config_type stADC_InitStruct;
    dma_init_type stDMA_InitStruct;

    // Защита от повторной инициализации 
    if (b_init_adc0_dma_done == false)
    {
        b_init_adc0_dma_done = true;

        // Инициализация DMA
        crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
        nvic_irq_enable(DMA1_Channel1_IRQn, 0, 0);
        dma_reset(DMA1_CHANNEL1);
        dma_default_para_init(&stDMA_InitStruct);

        // Указываем количество включенных каналов
        stDMA_InitStruct.buffer_size = in_qty;
        stDMA_InitStruct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
        stDMA_InitStruct.memory_base_addr = (uint32_t)dst_buf;
        stDMA_InitStruct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
        stDMA_InitStruct.memory_inc_enable = TRUE;
        stDMA_InitStruct.peripheral_base_addr = (uint32_t)&(((adc_type*)adc_num)->odt);
        stDMA_InitStruct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
        stDMA_InitStruct.peripheral_inc_enable = FALSE;
        stDMA_InitStruct.priority = DMA_PRIORITY_HIGH;
        stDMA_InitStruct.loop_mode_enable = TRUE;
        dma_init(DMA1_CHANNEL1, &stDMA_InitStruct);
        dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);
        dma_channel_enable(DMA1_CHANNEL1, TRUE);

        // Инициализация ADC
        adc_combine_mode_select(ADC_INDEPENDENT_MODE);
        adc_base_default_para_init(&stADC_InitStruct);
        stADC_InitStruct.sequence_mode = TRUE;
        stADC_InitStruct.repeat_mode = FALSE;
        stADC_InitStruct.data_align = ADC_RIGHT_ALIGNMENT;
        
        // Указываем количество включенных каналов
        stADC_InitStruct.ordinary_channel_length = in_qty;
        adc_base_config((adc_type*)adc_num, &stADC_InitStruct);
        adc_ordinary_conversion_trigger_set((adc_type*)adc_num, ADC12_ORDINARY_TRIG_SOFTWARE, TRUE);
        adc_dma_mode_enable((adc_type*)adc_num, TRUE);
        adc_enable((adc_type*)adc_num, TRUE);

        // Запуск калибровки АЦП
        adc_calibration_init((adc_type*)adc_num);
        while(adc_calibration_init_status_get((adc_type*)adc_num));
        adc_calibration_start((adc_type*)adc_num);
        while(adc_calibration_status_get((adc_type*)adc_num));
    }

    return err;
}



static void f_irq_process(u32 adc_num)
{
    u8 cur_channel = 0;

    // Поиск канала
    for (u8 i = LMCAL_ADC_CHANNEL0; i < LMCAL_ADC_CHANNEL_QTY; i++)
    {
        // Должен совпасть номер ADC
        if (adc_num == lmcal_adc_port_service_var[i].adc_num)
        {
            cur_channel = i;
            break;
        }
    }

    // Сброс флага и вызов колбека
    lmcal_adc_port_service_var[cur_channel].busy = false;

    if (lmcal_adc_port_service_var[cur_channel].done_callback != NULL)
    {
        lmcal_adc_port_service_var[cur_channel].done_callback();
    }
}



//***********************************************************************************************************
//******************************** Определения процедур обработки прерываний ********************************
//***********************************************************************************************************

void DMA1_Channel1_IRQHandler(void)
{
    if(dma_flag_get(DMA1_FDT1_FLAG) != RESET)
    {
        dma_flag_clear(DMA1_FDT1_FLAG);
        
        // Сервис обработки прерывания ADC
        f_irq_process((u32)LMCAL_ADC0);
    }
}


//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************

fun_res_t lmcal_adc_port_init(u8 param)
{
    fun_res_t err = ERR_OK;
    
    if (ADC_PRE_OP == param)
    {
        // Включение тактирования ADC
        // Делитель частоты настраивается в драйвере CPU 
        crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, true);
        
        crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, true);
        crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, true);
        crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, true);
    }
    else if (ADC_POST_OP == param)
    {
        
    }

    return err;
}



fun_res_t lmcal_adc_port_init_periph(u32 adc_num,
                                     u32 sample,
                                     u32 in_qty,
                                     const u32* in_num,
                                     lmcal_adc_wsize* dst_buf)
{
    fun_res_t err = ERR_OK;
    
    #if (L_ADC_DEBUG == 1)
    // Поддержка только АЦП0
    err = (adc_num == (u32)LMCAL_ADC0) ? (ERR_OK) : (ERR_NOT_IMPLEMENTED);
    L_CHECK_ERR(err);
    err = ((in_qty == 0) || (in_qty > 8)) ? (ERR_NOT_IMPLEMENTED) : (ERR_OK);
    L_CHECK_ERR(err);
    #endif

    // Настройка ножек
    err = f_gpio_init(adc_num, in_qty, in_num);
    L_CHECK_ERR(err);

    // Настройка каналов АЦП
    err = f_adc_init(adc_num, in_qty, in_num, sample);
    L_CHECK_ERR(err);

    // Инициализация DMA-АЦП
    err = f_dma_adc_init(adc_num, in_qty, (u32*)dst_buf);
    L_CHECK_ERR(err);

    L_EXIT;
    if (err != ERR_OK)
    {
        // Сброс GPIO и ADC
    }
    return err;
}



fun_res_t lmcal_adc_port_deinit(u8 param)
{
    fun_res_t err = ERR_OK;
    
    if (ADC_PRE_OP == param)
    {
        crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, false);
    }
    else if (ADC_POST_OP == param)
    {
        
    }

    return err;
}



fun_res_t lmcal_adc_port_deinit_periph(u32 adc_num,
                                       u32 in_qty,
                                       const u32* in_num)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(in_qty);
    L_UNUSED(in_num);
    
    #if (L_ADC_DEBUG == 1)
    // Поддержка только АЦП0
    err = (adc_num == (u32)LMCAL_ADC0) ? (ERR_OK) : (ERR_NOT_IMPLEMENTED);
    L_CHECK_ERR(err);
    #endif

    // Отключение АЦП
    adc_enable((adc_type*)adc_num, false);
    dma_channel_enable(DMA1_CHANNEL1, false);
    
    L_EXIT;
    return err;
}



fun_res_t lmcal_adc_port_get_status(u32 adc_num, lmcal_ch_sts_t* sts)
{
    fun_res_t err = ERR_OK;

    #if (L_ADC_DEBUG == 1)
    // Поддержка только АЦП0
    err = (adc_num == (u32)LMCAL_ADC0) ? (ERR_OK) : (ERR_NOT_IMPLEMENTED);
    L_CHECK_ERR(err);
    #endif

    #warning "TODO lmcal_adc_port_get_status()"

    *sts = CH_STS_IDLE;

    //! Этот флаг аппаратно сбрасывается при чтении регистра результата
    // flag_status flag = 0;
    // ADC_CCE_FLAG - channels conversion end flag
    // flag = adc_flag_get((adc_type*)adc_num, ADC_CCE_FLAG);
    // *sts = (flag == SET) ? (CH_STS_IDLE) : (CH_STS_BUSY);

    L_EXIT;
    return err;
}



fun_res_t lmcal_adc_port_enable(u32 adc_num, bool new_state)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(new_state);

    #if (L_ADC_DEBUG == 1)
    // Поддержка только АЦП0
    err = (adc_num == (u32)LMCAL_ADC0) ? (ERR_OK) : (ERR_NOT_IMPLEMENTED);
    // L_CHECK_ERR(err);
    #endif

    #warning "TODO lmcal_adc_port_enable()"

    // L_EXIT;
    return err;
}



fun_res_t lmcal_adc_port_start_conv(u32 adc_num)
{
    fun_res_t err = ERR_OK;

    #if (L_ADC_DEBUG == 1)
    // Поддержка только АЦП0
    err = (adc_num == (u32)LMCAL_ADC0) ? (ERR_OK) : (ERR_NOT_IMPLEMENTED);
    L_CHECK_ERR(err);
    #endif

    adc_ordinary_software_trigger_enable((adc_type*)adc_num, true);

    L_EXIT;
    return err;
}

//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
