#include "ltc2640.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_dma.h"
#include "device.h"
#include "user_misc.h"
#include "ltc1867.h"

uint8_t data_ltc2640_output[LTC2640_OUTPUT_LEN];
struct data_ltc2640_s data_ltc2640 __attribute__((section(".bss_ccm")));

void ltc2640_calc_sig_ref(uint8_t ch, uint32_t temperature, uint32_t *sig, uint32_t *ref)
{
    const struct lookup_table_s * const p = &(*bitmap_gas)[ch].lookup_table;
    float k; /* delta_b = y1 + (y2 - y1) / (x2 - x1) * (temp - x1) */
    uint8_t idx_t = 0; /* temperature index */
    float val;

    /* step1: get temperature index */
    for (idx_t = 0; idx_t < p->temperature_points; idx_t++) {
        if (temperature < p->detector_temperature[idx_t]) {
            break;
        }
    }
    if (idx_t == 0) {
        idx_t = 1; /* temperature is less than lowest temperature */
    } else if (idx_t >= p->temperature_points) {
        idx_t = p->temperature_points - 1; /* temperature is more than highest temperature */
    } else {
        /* calc with linear relation */
    }
    /* step2: calc const variable of temperature */
    k = (float)(temperature - p->detector_temperature[idx_t - 1]) / \
        (float)(p->detector_temperature[idx_t] - p->detector_temperature[idx_t - 1]);
    /* step3: calc sig & ref value by temperature */
    val = (float)(p->sig[0][idx_t - 1]) + \
         k * \
        ((float)(p->sig[0][idx_t]) - \
         (float)(p->sig[0][idx_t - 1]));
    *sig = (uint32_t)val;
    val = (float)(p->ref[0][idx_t - 1]) + \
         k * \
        ((float)(p->ref[0][idx_t]) - \
         (float)(p->ref[0][idx_t - 1]));
    *ref = (uint32_t)val;
}

void ltc2640_data_init(void)
{
    uint32_t sig, ref;
    uint8_t ch = 2;

    ltc2640_calc_sig_ref(ch, 29315, &sig, &ref);
    data_ltc2640.sig_low = sig / 2;
    data_ltc2640.sig_high = sig * 2;
    data_ltc2640.ref_low = ref / 2;
    data_ltc2640.ref_high = ref * 2;
    data_ltc2640.temperature_low = 21300;
    data_ltc2640.temperature_high = 37300;
    data_ltc2640.reading_range = (*bitmap_gas)[ch].head.range1;
    data_ltc2640.reading_neg_threshold = 0 - data_ltc2640.reading_range / 10;
    data_ltc2640.reading_range_high = data_ltc2640.reading_range / 4 * 5;
}

int32_t ltc2640_data_process(void)
{
    uint8_t ch = 2;
    uint32_t sig_ref;
    int32_t reading;

    if (flag.filter_climb > 0) {
        return VOLTAGE_400MV;
    }

    sig_ref = device_data.gas_sig[ch];
    if (sig_ref < data_ltc2640.sig_low || sig_ref > data_ltc2640.sig_high) {
        return VOLTAGE_200MV;
    }
    sig_ref = device_data.gas_sig[data_gas_info.ref[ch]];
    if (sig_ref < data_ltc2640.ref_low || sig_ref > data_ltc2640.ref_high) {
        return VOLTAGE_200MV;
    }

    reading = device_data.gas_concentration[ch];
    if (reading < data_ltc2640.reading_neg_threshold) {
        return VOLTAGE_300MV;
    } else if (reading < 0) {
        return (reading - data_ltc2640.reading_neg_threshold) * \
               (VOLTAGE_400MV - VOLTAGE_300MV) / \
               (0 - data_ltc2640.reading_neg_threshold) + \
               VOLTAGE_300MV;
    } else if (reading > data_ltc2640.reading_range_high) {
        return VOLTAGE_2500MV;
    } else if (reading > data_ltc2640.reading_range) {
        return (reading - data_ltc2640.reading_range) * \
               (VOLTAGE_2400MV - VOLTAGE_2000MV) / \
               (data_ltc2640.reading_range_high - data_ltc2640.reading_range) + \
               VOLTAGE_2000MV;
    } else {
        return reading * (VOLTAGE_2000MV - VOLTAGE_400MV) / data_ltc2640.reading_range + VOLTAGE_400MV;
    }

    return 0;
}

void ltc2640_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    SPI_InitTypeDef SPI_InitStructure;
    DMA_InitTypeDef  DMA_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);

    /* config ltc2640 pin */
    GPIO_InitStructure.GPIO_Pin = LTC2640_PIN_CS;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = LTC2640_PIN_SCK | LTC2640_PIN_SDI;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI3);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI3);
    /* config ltc2640 clr pin */
    GPIO_InitStructure.GPIO_Pin = LTC2640_PIN_CLR;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB, LTC2640_PIN_CLR);

    /* config SPI function */
    SPI_I2S_DeInit(SPI3);
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI3, &SPI_InitStructure);
    SPI_Cmd(SPI3, ENABLE);
    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

    /* Configure DMA controller to manage SPI request */ 
    DMA_InitStructure.DMA_BufferSize = LTC2640_OUTPUT_LEN;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)(&(SPI3->DR));
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)data_ltc2640_output;
    DMA_Init(DMA1_Stream5, &DMA_InitStructure);
    ltc2640_send_data(0);
    ltc2640_data_init();
}

void ltc2640_send_data(uint16_t data)
{
    data_ltc2640_output[0] = 0x30;
    data_ltc2640_output[1] = (uint8_t)((data >> 4) & 0xFF);
    data_ltc2640_output[2] = (uint8_t)((data << 4) & 0xFF);
    DMA_ClearFlag(DMA1_Stream5, DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_TCIF5);
    DMA_SetCurrDataCounter(DMA1_Stream5, LTC2640_OUTPUT_LEN);
    DMA_Cmd(DMA1_Stream5, ENABLE);
}

void ltc2640_loop(void)
{
    static uint32_t ms = 0;
    int32_t ret;

    if (system_tick_cnt < (ms + 50)) {
        return;
    }
    GPIO_SetBits(GPIOA, LTC2640_PIN_CS);
    ms = system_tick_cnt;
    ret = ltc2640_data_process();
    GPIO_ResetBits(GPIOA, LTC2640_PIN_CS);
    ltc2640_send_data((uint16_t)ret);
}
