/**
 * \file    drv_adc.c
 * \brief   DPM ADC operations
 *
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-03-18
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-03-18  Original Creation
 * 
 */

#include "drv_adc.h"
#include "drv_rtc.h"
#include "sync.h"
#include "reg_def_chip.h"
#include "drv_flash.h"

volatile u32_t adc_ctrl = 0x30; // default sample rate 3

#define DRV_ADC_SINGLE_READ_TIMEOUT         (5000UL)
#define DRV_ADC_MULTI_READ_TIMEOUT(cnt)     (DRV_ADC_SINGLE_READ_TIMEOUT * (cnt))

void drv_adc_stc_set(u32_t stc)
{
    DRV_REG_FIELD_SET(ADC_CTRL_REG, ADC_SAMCTRL, stc);
    SAL_FIELD_SET(adc_ctrl, ADC_CTRL_REG, ADC_SAMCTRL, stc);
}

u32_t drv_adc_read(u32_t channel)
{
    volatile u32_t adc_crtl_local = adc_ctrl;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    adc_crtl_local |= channel;
    adc_crtl_local |= ADC_CTRL_REG_ADC_RD_FLDSET;
    SAL_FIELD_SET(adc_crtl_local,ADC_CTRL_REG,ADC_REPEAT,1);
    DRV_REG_SET(ADC_CTRL_REG, adc_crtl_local);
    sys_tick_t start = now();
    while ((DRV_REG_GET(ADC_CTRL_REG) & ADC_CTRL_REG_ADC_RD_FLDSET) && ((u32_t)(now() - start) < DRV_ADC_SINGLE_READ_TIMEOUT));

    adc_crtl_local = DRV_REG_GET(ADC_RSLT_REG);

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return adc_crtl_local;
}

u32_t drv_adc_read_multi(u32_t channel, u32_t count)
{
    volatile u32_t adc_crtl_local = adc_ctrl;

    if (count <= 0)
        count = 1;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    adc_crtl_local |= channel;
    adc_crtl_local |= ADC_CTRL_REG_ADC_RD_FLDSET;
    SAL_FIELD_SET(adc_crtl_local,ADC_CTRL_REG,ADC_REPEAT,count);
    DRV_REG_SET(ADC_CTRL_REG, adc_crtl_local);
    sys_tick_t start = now();
    u32_t timeout = DRV_ADC_MULTI_READ_TIMEOUT(count);
    while ((DRV_REG_GET(ADC_CTRL_REG) & ADC_CTRL_REG_ADC_RD_FLDSET) && ((u32_t)(now() - start) < timeout));

    adc_crtl_local = DRV_REG_GET(ADC_RSLT_REG);

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return adc_crtl_local;
}

u32_t drv_adc_read_multi_avg(u32_t channel, u32_t count)
{
    volatile u32_t adc_crtl_local = adc_ctrl;
    volatile u32_t adc_rslt = 0;
    sys_tick_t start;

    if (count > 1024)
        count = 1024;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    adc_crtl_local |= channel;
    adc_crtl_local |= ADC_CTRL_REG_ADC_RD_FLDSET;
    SAL_FIELD_SET(adc_crtl_local,ADC_CTRL_REG,ADC_REPEAT,1);
    DRV_REG_SET(ADC_CTRL_REG, adc_crtl_local);
    start = now();
    while ((DRV_REG_GET(ADC_CTRL_REG) & ADC_CTRL_REG_ADC_RD_FLDSET) && ((u32_t)(now() - start) < DRV_ADC_SINGLE_READ_TIMEOUT));
    DRV_REG_GET(ADC_RSLT_REG);

    int i = count;
    do 
    {
        i--;
        DRV_REG_SET(ADC_CTRL_REG, adc_crtl_local);
        start = now();
        while ((DRV_REG_GET(ADC_CTRL_REG) & ADC_CTRL_REG_ADC_RD_FLDSET) && ((u32_t)(now() - start) < DRV_ADC_SINGLE_READ_TIMEOUT));
        adc_rslt += DRV_REG_GET(ADC_RSLT_REG);
    } 
    while (i > 0);
    adc_rslt /= count;

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return adc_rslt;
}

u32_t drv_adc_read_multi_avg2(u32_t channel, u32_t count)
{
    volatile u32_t adc_crtl_local = adc_ctrl;
    volatile u32_t adc_rslt = 0;
    volatile u32_t adc_rslt_curr = 0;
    volatile u32_t adc_rslt_min = 0;
    volatile u32_t adc_rslt_max = 0;
    sys_tick_t start;

    if (count > 1024)
        count = 1024;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    adc_crtl_local |= channel;
    adc_crtl_local |= ADC_CTRL_REG_ADC_RD_FLDSET;
    SAL_FIELD_SET(adc_crtl_local,ADC_CTRL_REG,ADC_REPEAT,1);
    DRV_REG_SET(ADC_CTRL_REG, adc_crtl_local);
    start = now();
    while ((DRV_REG_GET(ADC_CTRL_REG) & ADC_CTRL_REG_ADC_RD_FLDSET) && ((u32_t)(now() - start) < DRV_ADC_SINGLE_READ_TIMEOUT));
    DRV_REG_GET(ADC_RSLT_REG);

    int i = count;
    do 
    {
        i--;
        DRV_REG_SET(ADC_CTRL_REG, adc_crtl_local);
        start = now();
        while ((DRV_REG_GET(ADC_CTRL_REG) & ADC_CTRL_REG_ADC_RD_FLDSET) && ((u32_t)(now() - start) < DRV_ADC_SINGLE_READ_TIMEOUT));
        adc_rslt_curr = DRV_REG_GET(ADC_RSLT_REG);
        if (adc_rslt_curr > adc_rslt_max)
        {
            adc_rslt_max = adc_rslt_curr;
        }
        if ((adc_rslt_min > adc_rslt_curr) || (!adc_rslt_min))
        {
            adc_rslt_min = adc_rslt_curr;
        }
        adc_rslt += adc_rslt_curr;
    } 
    while (i > 0);

    if ((count) && (adc_rslt_max))
    {
        adc_rslt -= adc_rslt_max;
        count--;
    }
    if ((count) && (adc_rslt_min))
    {
        adc_rslt -= adc_rslt_min;
        count--;
    }
    adc_rslt /= count;

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return adc_rslt;
}

void drv_adc_current_sense_init(drv_adc_igain_t gain)
{
    DRV_REG_FIELD_SET(PD_CTRL_REG, IGAIN, gain);
}

drv_adc_igain_t drv_adc_current_sense_gain_get()
{
    return (drv_adc_igain_t)DRV_REG_FIELD_GET(PD_CTRL_REG, IGAIN);
}

u32_t drv_adc_current_sense_gain_times_get()
{
    return (DRV_REG_FIELD_GET(PD_CTRL_REG, IGAIN) == DRV_ADC_CURRENT_SENSE_GAIN_15) ? 15 : 7;
}

static volatile u8_t s_adc_ibus_direction; 

u32_t drv_adc_ibus_get(u32_t sample_resister)
{
    u32_t adc_rslt = drv_adc_read_multi_avg(DRV_ADC_CHN_IBUS, 7);
    u32_t offset = drv_flash_trim_current_sense_offset_get();

    if (adc_rslt >= offset)
    {
        adc_rslt -= offset;
        s_adc_ibus_direction = 0;
    }
    else {
        adc_rslt = offset - adc_rslt;
        s_adc_ibus_direction = 1;
    }
    
    return ((adc_rslt * drv_flash_trim_ref_volt_get()) >> ADC_RSLT_REG_ADC_RSLT_WTH) * 1000 /* resister, mohm */ / (sample_resister * drv_adc_current_sense_gain_times_get());
}

u8_t drv_adc_ibus_dir_get()
{
    return s_adc_ibus_direction;
}


