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

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

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

// Макрос, заполняющий структуру конфиграции
#define ADC_CH_CONFIG_FILL(nChNum)                         \
{                                                          \
    .enable    = (bool)LMCAL_ADC_CH##nChNum##_ENABLE,      \
    .adc_num   = (u32) LMCAL_ADC_CH##nChNum##_ADC_NUM,     \
    .sample    = (u32) LMCAL_ADC_CH##nChNum##_SAMPLE_TIME, \
    .in_qty    = (u32) LMCAL_ADC_CH##nChNum##_IN_QTY,      \
    .in_num    = \
    { \
                 (u32) LMCAL_ADC_CH##nChNum##_IN0_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN1_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN2_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN3_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN4_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN5_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN6_NUM,     \
                 (u32) LMCAL_ADC_CH##nChNum##_IN7_NUM      \
    } \
}

// Максимальное кол-во каналов на один АЦП
#define LMCAL_ADC_IN_MAX        (8)

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


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

// Конфигурация канала
typedef struct
{
    bool enable;
    u32  adc_num;
    u32  sample;
    u32  in_qty;
    u32  in_num[LMCAL_ADC_IN_MAX];
} adc_ch_config_t;

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

extern adc_ch_service_t lmcal_adc_port_service_var[LMCAL_ADC_CHANNEL_QTY];


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

#if (LMCAL_DEBUG == 1)
static bool b_init_done = false;
#endif

static const adc_ch_config_t adc_st_ch_config[LMCAL_ADC_CHANNEL_QTY] =
{ 
    ADC_CH_CONFIG_FILL(0),
    ADC_CH_CONFIG_FILL(1),
    ADC_CH_CONFIG_FILL(2),
    ADC_CH_CONFIG_FILL(3),
};

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

#if (LMCAL_DEBUG == 1)
static fun_res_t f_check_channel(lmcal_adc_channel_t channel)
{
    fun_res_t err = ERR_OK;

    if (channel > LMCAL_ADC_CHANNEL_QTY)
    {
        err = ERR_INVALID_ARGUMENT;
    }

    return err;
}
#endif

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



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

fun_res_t lmcal_adc_init(void)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(false == b_init_done);
        L_CHECK_ERR(err);
    #endif
    
    err = lmcal_adc_port_init(ADC_PRE_OP);
    L_CHECK_ERR(err);

    // Инициализация всех включенных каналов
    for (u8 cnt = 0U; cnt < LMCAL_ADC_CHANNEL_QTY; cnt++)
    {
        if (adc_st_ch_config[cnt].enable == true)
        {
            err = lmcal_adc_port_init_periph(adc_st_ch_config[cnt].adc_num,
                                             adc_st_ch_config[cnt].sample,
                                             adc_st_ch_config[cnt].in_qty,
                                             adc_st_ch_config[cnt].in_num,
                                             lmcal_adc_port_service_var[cnt].adc_res);
            L_CHECK_ERR(err);

            // Сброс сервисного массива
            lmcal_adc_port_service_var[cnt].init = true;
            lmcal_adc_port_service_var[cnt].busy = false;
            lmcal_adc_port_service_var[cnt].done_callback = NULL;
            lmcal_adc_port_service_var[cnt].adc_ch_qty = adc_st_ch_config[cnt].in_qty;
            lmcal_adc_port_service_var[cnt].adc_num = adc_st_ch_config[cnt].adc_num;
            memset(lmcal_adc_port_service_var[cnt].adc_res, 0, (LMCAL_ADC_IN_QTY_MAX * sizeof(lmcal_adc_wsize)));
        }
    }

    err = lmcal_adc_port_init(ADC_POST_OP);
    L_CHECK_ERR(err);

    #if (LMCAL_DEBUG == 1)
    b_init_done = true;
    #endif

    L_EXIT;
    return err;
}



fun_res_t lmcal_adc_deinit(void)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_adc_port_deinit(ADC_PRE_OP);
    L_CHECK_ERR(err);

    // Выполняем деинициализацию только включенных каналов
    for (u8 cnt = 0U; cnt < LMCAL_ADC_CHANNEL_QTY; cnt++)
    {
        if (adc_st_ch_config[cnt].enable == true)
        {
            err = lmcal_adc_port_deinit_periph(adc_st_ch_config[cnt].adc_num,
                                               adc_st_ch_config[cnt].in_qty,
                                               adc_st_ch_config[cnt].in_num);
            L_CHECK_ERR(err);
        }
    }

    err = lmcal_adc_port_deinit(ADC_POST_OP);
    L_CHECK_ERR(err);
        
    #if (LMCAL_DEBUG == 1)
    b_init_done = false;
    #endif

    L_EXIT;
    return err;
}



err_or_res_t lmcal_adc_get_status(lmcal_adc_channel_t channel)
{
    err_or_res_t res = ERR_OK;
    fun_res_t err = ERR_OK;
    lmcal_ch_sts_t sts = 0;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == adc_st_ch_config[channel].enable);
        L_CHECK_ERR(err);        
    #endif

    // Проверка инициализации канала
    sts = (lmcal_adc_port_service_var[channel].init == false) ? (CH_STS_NOT_INIT) : (0);
    L_CHECK_VAL(sts);

    sts = (lmcal_adc_port_service_var[channel].busy == true) ? (CH_STS_BUSY) : (0);
    L_CHECK_VAL(sts);
    
    // проверяем аппаратный флаг статуса
    err = lmcal_adc_port_get_status(adc_st_ch_config[channel].adc_num, &sts);

    #if (LMCAL_DEBUG == 1)
    #endif

    L_EXIT;
    res = (err_or_res_t)((err == ERR_OK) ? (sts) : (err));
    return res;
}



fun_res_t lmcal_adc_enable(lmcal_adc_channel_t channel, bool new_state)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == adc_st_ch_config[channel].enable);
        L_CHECK_ERR(err);
    #endif

    err = lmcal_adc_port_enable(adc_st_ch_config[channel].adc_num, new_state);

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif

    return err;
}



fun_res_t lmcal_adc_set_callback(lmcal_adc_channel_t channel, void_fun_t function)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == adc_st_ch_config[channel].enable);
        L_CHECK_ERR(err);        
    #endif
    
    lmcal_adc_port_service_var[channel].done_callback = function;
    
    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif

    return err;
}



fun_res_t lmcal_adc_start_conv(lmcal_adc_channel_t channel)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == adc_st_ch_config[channel].enable);
        L_CHECK_ERR(err);        
    #endif
    
    if (lmcal_adc_port_service_var[channel].busy == false)
    {
        lmcal_adc_port_service_var[channel].busy = true;

        err = lmcal_adc_port_start_conv(adc_st_ch_config[channel].adc_num);
    }
    else
    {
        err = ERR_RUNTIME_ERROR;
    }

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



fun_res_t lmcal_adc_get_res(lmcal_adc_channel_t channel,
                            lmcal_adc_wsize* dst_buf,
                            u16 qty)
{
    fun_res_t err = ERR_OK;

    #if (LMCAL_DEBUG == 1)
        err = L_ASSERT(true == b_init_done);
        L_CHECK_ERR(err);
        err = f_check_channel(channel);
        L_CHECK_ERR(err);
        err = L_ASSERT(true == adc_st_ch_config[channel].enable);
        L_CHECK_ERR(err);            
        err = L_ASSERT(qty <= adc_st_ch_config[channel].in_qty);
        L_CHECK_ERR(err);            
    #endif
    
    for(u8 i=0; i < qty; i++)
    {
        dst_buf[i] = lmcal_adc_port_service_var[channel].adc_res[i];
    }

    #if (LMCAL_DEBUG == 1)
    L_EXIT;
    #endif
    return err;
}



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