#include "adc.h"
#include "device.h"
#include "debug.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_adc.h"
#include "math.h"

/* murata NCP15WF104D coeff table
A0 = 2.3088245e-3
A1 = 2.1731924e-4
A2 = 1.7282124e-6
A3 = 7.7743300e-8*/
static float Ncp15wCoeff[4] = {2.3088245e-3, 2.1731924e-4, 1.7282124e-6, 7.7743300e-8};
static uint16_t adc_data[4][12] __attribute__((section(".bss_ccm")));
static uint16_t adc_data_sort[12] __attribute__((section(".bss_ccm")));
uint16_t adc_det_temp_from_ltc1867 __attribute__((section(".bss_ccm")));

float adc_to_resistance(uint16_t adc_value)
{
    uint32_t value1, value2;

    value1 = 100 * adc_value;
    value2 = 4096 - adc_value;

    return (float)value1 / (float)value2;
}

float adc_to_resistance16(uint16_t adc_value)
{
    uint32_t value1, value2;

    value1 = 100 * (uint32_t)adc_value;
    value2 = 65536 - (uint32_t)adc_value;

    return (float)value1 / (float)value2;
}

uint32_t adc_rtot(uint16_t adc_value)
{
    int i = 0;
    float t = 0;
    float rtemp;
    
    rtemp = adc_to_resistance(adc_value);
    rtemp = logf(rtemp);
    
    for(i = 3; i >= 0; i--) {
        t = t * rtemp + Ncp15wCoeff[i];
    }
    t = 100.0f / t; /* unit: 0.01K */

    return (uint32_t)t;
}

uint32_t adc_rtot16(uint16_t adc_value)
{
    int i = 0;
    float t = 0;
    float rtemp;
    
    rtemp = adc_to_resistance16(adc_value);
    rtemp = logf(rtemp);
    
    for(i = 3; i >= 0; i--) {
        t = t * rtemp + Ncp15wCoeff[i];
    }
    t = 100.0f / t; /* unit: 0.01K */

    return (uint32_t)t;
}

void adc_input_data(uint8_t ch, uint16_t data)
{
    static uint8_t idx[4] = {0, 0, 0, 0};

    adc_data[ch][idx[ch]] = data;
    idx[ch]++;
    if(idx[ch] >= ADC_FILTER_LEN) {
        idx[ch] = 0;
    }
}

uint16_t adc_get_filter_data(uint8_t ch)
{
    uint32_t i, j, min_idx;
    uint32_t sum;

    /* copy data to buffer */
    for (i = 0; i < ADC_FILTER_LEN - 1; i++) {
        adc_data_sort[i] = adc_data[ch][i];
    }
    /* sort from little to big */
    for (i = 0; i < ADC_FILTER_LEN - 1; i++) {
        min_idx = i;
        for (j = i + 1; j < ADC_FILTER_LEN; j++) {
            if (adc_data_sort[j] < adc_data_sort[min_idx]) {
                min_idx = j;
            }
        }
        if (min_idx != i) {
            uint16_t temp = adc_data_sort[i];
            adc_data_sort[i] = adc_data_sort[min_idx];
            adc_data_sort[min_idx] = temp;
        }
    }
    /* get average in middle 8 elements */
    sum = 0;
    for (i = 2; i < ADC_FILTER_LEN - 2; i++) {
        sum += adc_data_sort[i];
    }
    sum = (sum + (ADC_FILTER_LEN - 4) / 2) / (ADC_FILTER_LEN - 4);
    return (uint16_t)sum;
}

void adc_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    ADC_InitTypeDef  ADC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles;
    ADC_CommonInit(&ADC_CommonInitStructure);

    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfConversion = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
    ADC_Cmd(ADC1, ENABLE);
    adc_trig(0);
}

void adc_trig(uint8_t ch)
{
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_15Cycles);
    ADC_SoftwareStartConv(ADC1);
}

uint16_t adc_read(void)
{
    return ADC_GetConversionValue(ADC1);
}

void adc_loop(void)
{
    static uint32_t ms = 0;
    static uint8_t ch = 0;
    uint16_t val;
    uint32_t value;

    if (system_tick_cnt < (ms + 10)) {
        return;
    }
    ms = system_tick_cnt;
    val = adc_read();
#ifdef DET_TEMPERATURE_LTC1867
    if (ch == 0) {
        val = adc_det_temp_from_ltc1867;
    }
#endif
    // debug_write_data(DEBUG_CH_ADC_RAW + ch, (float)val);
    adc_input_data(ch, val); /* input data from ADC to sort buffer */
    if (ch == 0) {
        val = adc_get_filter_data(ch);
#ifdef DET_TEMPERATURE_LTC1867
        value = adc_rtot16(val);
#else
        value = adc_rtot(val);
#endif
        // debug_write_data(DEBUG_CH_ADC_FILTER + ch, (float)value);
        device_data.detector_temperature = value;
        ch = 1;
    } else if (ch == 1) {
        val = adc_get_filter_data(ch);
        value = adc_rtot(val);
        // debug_write_data(DEBUG_CH_ADC_FILTER + ch, (float)value);
        device_data.lamp_temperature = value;
        ch = 3;
    } else if (ch == 3) {
        val = adc_get_filter_data(ch);
        value = (uint32_t)val * 6600 / 4096;
        // debug_write_data(DEBUG_CH_ADC_FILTER + ch, (float)value);
        device_data.lamp_voltage = value;
        ch = 0;
    } else {
        ch = 0;
    }
    adc_trig(ch);
}
