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

#include "lmcal_cfg.h"
#include "lmcal_uart_port.h"

#include "at32f403a_407_conf.h"

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

#define UART_MODE_SYNC      (0)
#define UART_MODE_ASYNC     (1)


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



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

uart_ch_service_t lmcal_uart_port_service_var[LMCAL_UART_CHANNEL_QTY];



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

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

static fun_res_t f_enable_port_clk(u32 port_num, bool state)
{
    fun_res_t err = ERR_OK;

    if ((gpio_type*)port_num == GPIOA) 
    {
        crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, state);
    } 
    else if((gpio_type*)port_num == GPIOB) 
    {
        crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, state);
    }
    else if((gpio_type*)port_num == GPIOC) 
    {
        crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, state);
    }
    else if((gpio_type*)port_num == GPIOD) 
    {
        crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, state);
    }
    else if((gpio_type*)port_num == GPIOE) 
    {
        crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, state);
    }
    else 
    {
        err = ERR_INVALID_ARGUMENT;
    }

    return err;
}


static fun_res_t f_enable_uart_clk(u32 uart_num, bool state)
{
    fun_res_t err = ERR_OK;

    if((usart_type*)uart_num == LMCAL_UART0) 
    {
        crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART1) 
    {
        crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART2) 
    {
        crm_periph_clock_enable(CRM_USART3_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART3) 
    {
        crm_periph_clock_enable(CRM_UART4_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART4) 
    {
        crm_periph_clock_enable(CRM_UART5_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART5) 
    {
        crm_periph_clock_enable(CRM_USART6_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART6) 
    {
        crm_periph_clock_enable(CRM_UART7_PERIPH_CLOCK, state);
    }
    else if((usart_type*)uart_num == LMCAL_UART7) 
    {
        crm_periph_clock_enable(CRM_UART8_PERIPH_CLOCK, state);
    }
    else 
    {
        err = ERR_INVALID_ARGUMENT;
    }

    return err;
}


// Включить\отключить тактирование всей необходимой периферии
static fun_res_t f_enable_clk(u32 port_num_tx, 
                              u32 port_num_rx,
                              u32 port_num_clk,
                              u32 uart_num,
                              bool state)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(port_num_clk);
    
    err = f_enable_port_clk(port_num_tx, state);
    L_CHECK_ERR(err);

    err = f_enable_port_clk(port_num_rx, state);
    L_CHECK_ERR(err);

    // err = f_enable_port_clk(port_num_clk, state);
    // L_CHECK_ERR(err);

    err = f_enable_uart_clk(uart_num, state);
    L_CHECK_ERR(err);

    L_EXIT;
    return err;
}


static fun_res_t f_gpio_init(u32 uart_num,
                            u32 port_num_tx,  u32 pin_num_tx,
                            u32 port_num_rx,  u32 pin_num_rx,
                            u32 port_num_clk, u32 pin_num_clk)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(uart_num);
    L_UNUSED(port_num_clk);
    L_UNUSED(pin_num_clk);

    gpio_init_type stGPIO_TX_InitStruct, stGPIO_RX_InitStruct;
    
    // Предварительная инициализация структуры настроек порта
    gpio_default_para_init(&stGPIO_TX_InitStruct);
    gpio_default_para_init(&stGPIO_RX_InitStruct);
    
    stGPIO_TX_InitStruct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    stGPIO_TX_InitStruct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    stGPIO_TX_InitStruct.gpio_mode = GPIO_MODE_MUX;
    stGPIO_TX_InitStruct.gpio_pull = GPIO_PULL_NONE;
    
    stGPIO_RX_InitStruct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    stGPIO_RX_InitStruct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    stGPIO_RX_InitStruct.gpio_mode = GPIO_MODE_INPUT;
    stGPIO_RX_InitStruct.gpio_pull = GPIO_PULL_UP; 

    // Настройка ножки TX
    stGPIO_TX_InitStruct.gpio_pins = pin_num_tx;
    gpio_init((gpio_type*)port_num_tx, &stGPIO_TX_InitStruct);

    // Настройка ножки RX
    stGPIO_RX_InitStruct.gpio_pins = pin_num_rx;
    gpio_init((gpio_type*)port_num_rx, &stGPIO_RX_InitStruct);

    return err;
}


static void f_nvic_irq_enable(u32 uart_num, bool state)
{
    if (state == true)
    {
        if (USART1 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(USART1_IRQn);
        }
        else if (USART2 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(USART2_IRQn);
        }
        else if (USART3 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(USART3_IRQn);
        }
        else if (UART4 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(UART4_IRQn);
        }
        else if (UART5 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(UART5_IRQn);
        }
        else if (USART6 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(USART6_IRQn);
        }    
        else if (UART7 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(UART7_IRQn);
        }
        else if (UART8 == (usart_type*)uart_num)
        {
            NVIC_EnableIRQ(UART8_IRQn);
        }
        else
        {
            // Ничего не делаем
        }
    }
    else
    {
        if (USART1 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(USART1_IRQn);
        }
        else if (USART2 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(USART2_IRQn);
        }
        else if (USART3 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(USART3_IRQn);
        }
        else if (UART4 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(UART4_IRQn);
        }
        else if (UART5 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(UART5_IRQn);
        }
        else if (USART6 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(USART6_IRQn);
        }    
        else if (UART7 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(UART7_IRQn);
        }
        else if (UART8 == (usart_type*)uart_num)
        {
            NVIC_DisableIRQ(UART8_IRQn);
        }
        else
        {
            // Ничего не делаем
        }
    }
}



static fun_res_t f_uart_init(u32 uart_num, u32 mode, u32 parity, u32 baudrate)
{
    fun_res_t err = ERR_OK;

    // Настройка UART
    usart_init((usart_type*)uart_num,
            baudrate, 
            USART_DATA_8BITS, 
            USART_STOP_1_BIT);
    usart_parity_selection_config((usart_type*)uart_num, parity);
    usart_transmitter_enable((usart_type*)uart_num, TRUE);
    usart_receiver_enable((usart_type*)uart_num, TRUE);

    // Включение прерывания
    if (mode == UART_MODE_ASYNC)
    {
        usart_interrupt_enable((usart_type*)uart_num, USART_RDBF_INT, true);
        // Разрешение прерывания в ядре
        f_nvic_irq_enable(uart_num, true);
    }
    
    return err;
}


static void f_irq_process(u32 uart_num, u8 rx_mode)
{
    u8 cur_channel = 0;

    // Поиск канала
    for (u8 i = LMCAL_UART_CHANNEL0; i < LMCAL_UART_CHANNEL_QTY; i++)
    {
        if (uart_num == lmcal_uart_port_service_var[i].uart_num)
        {
            cur_channel = i;
            break;
        }
    }

    // Сброс флага и вызов колбека
    // todo - проверка аппаратной ошибки и выставление флага error
    if (rx_mode == false)
    {
        lmcal_uart_port_service_var[cur_channel].busy = false;

        // Запрет прерывания по завершению
        usart_interrupt_enable((usart_type*)uart_num, USART_TDC_INT, false);

        if (lmcal_uart_port_service_var[cur_channel].tx_callback != NULL)
        {
            lmcal_uart_port_service_var[cur_channel].tx_callback();
        }
    }
    else
    {
        // lmcal_uart_port_service_var[cur_channel].rx_data = usart_data_receive((usart_type*)uart_num);
        
        if (lmcal_uart_port_service_var[cur_channel].rx_callback != NULL)
        {
            lmcal_uart_port_service_var[cur_channel].rx_callback();
        }
    }
}


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

// Алгоритм: Проверка флагов, поиск канала, изменение флага канала, вызов колбека

void USART1_IRQHandler(void)
{
    // Прерывание по приему
    if(USART1->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(USART1, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)USART1, true);
        }
    }

    // Прерывание по завершению передачи
    if(USART1->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(USART1, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)USART1, false);
        }
    }
}


void USART2_IRQHandler(void)
{
    // Прерывание по приему
    if(USART2->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(USART2, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)USART2, true);
        }
    }

    // Прерывание по завершению передачи
    if(USART2->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(USART2, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)USART2, false);
        }
    }
}


void USART3_IRQHandler(void)
{
    // Прерывание по приему
    if(USART3->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(USART3, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)USART3, true);
        }
    }

    // Прерывание по завершению передачи
    if(USART3->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(USART3, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)USART3, false);
        }
    }
}


void UART4_IRQHandler(void)
{
    // Прерывание по приему
    if(UART4->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(UART4, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)UART4, true);
        }
    }

    // Прерывание по завершению передачи
    if(UART4->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(UART4, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)UART4, false);
        }
    }
}


void UART5_IRQHandler(void)
{
    // Прерывание по приему
    if(UART5->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(UART5, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)UART5, true);
        }
    }

    // Прерывание по завершению передачи
    if(UART5->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(UART5, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)UART5, false);
        }
    }
}


void USART6_IRQHandler(void)
{
    // Прерывание по приему
    if(USART6->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(USART6, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)USART6, true);
        }
    }

    // Прерывание по завершению передачи
    if(USART6->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(USART6, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)USART6, false);
        }
    }
}


void UART7_IRQHandler(void)
{
    // Прерывание по приему
    if(UART7->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(UART7, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)UART7, true);
        }
    }

    // Прерывание по завершению передачи
    if(UART7->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(UART7, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)UART7, false);
        }
    }
}


void UART8_IRQHandler(void)
{
    // Прерывание по приему
    if(UART8->ctrl1_bit.rdbfien != RESET)
    {
        if(usart_flag_get(UART8, USART_RDBF_FLAG) != RESET)
        {
            f_irq_process((u32)UART8, true);
        }
    }

    // Прерывание по завершению передачи
    if(UART8->ctrl1_bit.tdcien != RESET)
    {
        if(usart_flag_get(UART8, USART_TDC_FLAG) != RESET)
        {
            f_irq_process((u32)UART8, false);
        }
    }
}


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

fun_res_t lmcal_uart_port_init(u8 param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    // Опциональная функция

    return err;
}



fun_res_t lmcal_uart_port_init_periph(u32 uart_num,
                                      u32 mode,
                                      u32 port_num_tx,
                                      u32 pin_num_tx,
                                      u32 port_num_rx,
                                      u32 pin_num_rx,
                                      u32 port_num_clk,
                                      u32 pin_num_clk,
                                      u32 parity,
                                      u32 baudrate)
{
    fun_res_t err = ERR_OK;

    err = f_enable_clk(port_num_tx, port_num_rx, port_num_clk, uart_num, true);
    L_CHECK_ERR(err);

    err = f_gpio_init(uart_num,
                      port_num_tx,  pin_num_tx,
                      port_num_rx,  pin_num_rx,
                      port_num_clk, pin_num_clk);
    L_CHECK_ERR(err);

    err = f_uart_init(uart_num, mode, parity, baudrate);
    L_CHECK_ERR(err);

    L_EXIT;
    if (err != ERR_OK)
    {
        usart_reset((usart_type*)uart_num);
    }
    return err;
}



fun_res_t lmcal_uart_port_deinit(u8 param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    // Опциональная функция

    return err;
}



fun_res_t lmcal_uart_port_deinit_periph(u32 uart_num,
                                        u32 port_num_tx,
                                        u32 pin_num_tx,
                                        u32 port_num_rx,
                                        u32 pin_num_rx,
                                        u32 port_num_clk,
                                        u32 pin_num_clk)
{
    fun_res_t err = ERR_OK;
    
    usart_reset((usart_type*)uart_num);

    // Добавить деинициализацию пинов
    #warning "TODO lmcal_uart_port_deinit_periph()"

    L_UNUSED(port_num_tx);
    L_UNUSED(pin_num_tx);
    L_UNUSED(port_num_rx);
    L_UNUSED(pin_num_rx);
    L_UNUSED(port_num_clk);
    L_UNUSED(pin_num_clk);

    return err;
}



fun_res_t lmcal_uart_port_get_status(u32 uart_num, lmcal_ch_sts_t* sts)
{
    fun_res_t err = ERR_OK;

    if (usart_flag_get((usart_type*)uart_num, USART_TDC_FLAG) == SET)
    {
        // Передача завершена, UART свободен
        *sts = CH_STS_IDLE;
    }
    else
    {
        *sts = CH_STS_BUSY;
    }

    return err;
}



fun_res_t lmcal_uart_port_enable(u32 uart_num, bool new_state)
{
    fun_res_t err = ERR_OK;
    
    while(RESET == usart_flag_get((usart_type*)uart_num, USART_TDC_FLAG));
    usart_enable((usart_type*)uart_num, new_state);

    return err;
}



fun_res_t lmcal_uart_port_write(u32 uart_num, u32 mode, u16 data)
{
    fun_res_t err = ERR_OK;

    if (mode == UART_MODE_ASYNC)
    {
        while(RESET == usart_flag_get((usart_type*)uart_num, USART_TDBE_FLAG));
        usart_data_transmit((usart_type*)uart_num, data);
        // Разрешение прерывания по завершению
        usart_interrupt_enable((usart_type*)uart_num, USART_TDC_INT, true);
    }
    else
    {
        // Синхронный режим
        usart_data_transmit((usart_type*)uart_num, data);
        while(RESET == usart_flag_get((usart_type*)uart_num, USART_TDBE_FLAG));
    }

    return err;
}


fun_res_t lmcal_uart_port_read(u32 uart_num, u32 mode, u16* data)
{
    fun_res_t err = ERR_OK;
    
    if (mode == UART_MODE_ASYNC)
    {
        // *data = lmcal_uart_port_service_var[cur_channel].rx_data; 
        *data = usart_data_receive((usart_type*)uart_num); 
    }
    else
    {
        // Синхронный режим
        while(RESET == usart_flag_get((usart_type*)uart_num, USART_RDBF_FLAG));
        *data = usart_data_receive((usart_type*)uart_num);
    }
    return err;
}


fun_res_t lmcal_uart_port_write_buf(u32 uart_num, u32 mode, u8* data, u16 size)
{
    fun_res_t err = ERR_NOT_IMPLEMENTED;

    #warning "TODO lmcal_uart_port_write_buf()"

    L_UNUSED(uart_num);
    L_UNUSED(mode);
    L_UNUSED(data);
    L_UNUSED(size);

    return err;
}


fun_res_t lmcal_uart_port_read_buf(u32 uart_num, u32 mode, u8* data, u16 size)
{
    fun_res_t err = ERR_NOT_IMPLEMENTED;

    #warning "TODO lmcal_uart_port_read_buf()"

    L_UNUSED(uart_num);
    L_UNUSED(mode);
    L_UNUSED(data);
    L_UNUSED(size);

    return err;
}


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