/**
  ******************************************************************************
  * @file    VAPC-meter/lib/adc.c
  * @author  "Vladimir N. Shilov" <shilow@ukr.net>
  * @version V1
  * @date    24-March-2016
  * @brief   This file contains the ADC functions.
  ******************************************************************************
  * Tim2 регулярно запускат АЦП.
  * АЦП измеряет два заданных канала и пинает DMA
  * DMA забирает данные у ADC и складывает их в ADC_Buffer
  * В прерывании по окончанию транзакции данные из ADC_Buffer
  * раскладываем по FastBuffers.
  */

/* Includes ------------------------------------------------------------------*/
#include "adc.h"

/** @addtogroup ADC
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define VOLTAGE_CHANNEL     ADC_Channel_17
#define CURRENT_CHANNEL     ADC_Channel_18
#define ADC1_DR_ADDRESS     ((uint16_t)0x5344)
#define ADC_BUFFER_ADDRESS  ((uint16_t)(&ADC_Buffer))

// Supply voltage
#define ADC_REF             3306

// Normal K for current channel = 4095 * 5.43 (Ку ОУ) * 0.05 (Rш)
//#define ADC_DIV             1151
//#define ADC_DIV_H           575

// Oversampled K for current channel = (4095 * 64 / 8) * 5.43 (Ку ОУ) * 0.0501877 (Rш) * 0.937163 (Compensation)
#define ADC_ODIV            8366
#define ADC_ODIV_H          4183
// for voltage calculation == 4095 * 64 / 8
#define ADC_ORES            32760
#define ADC_ORES_H          16380
// OU parameters
#define ADC_OU_MUL          543
#define ADC_OU_MUL_DIV      100
#define ADC_OU_ZERO_DRIFT   40

// Input Voltage divider
#define ADC_VOLT_K          11

// (Частота МК (16 МHz) / Предделитель таймера (8) * Нужное время в секундах (0.0015625)) - 1
#define TIM_PERIOD          ((uint16_t)3128)
#define TIM_PRESCALER       TIM2_Prescaler_8

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static __IO uint16_t ADC_Buffer[ADC_BUFFER_SIZE];
static __IO uint16_t VoltageFastBuffer[FAST_BUFFER_SIZE];
static __IO uint16_t CurrentFastBuffer[FAST_BUFFER_SIZE];

/* Private constants ---------------------------------------------------------*/
__near __no_init const unsigned char Factory_VREFINT @ 0x4910;

/* Private function prototypes -----------------------------------------------*/
static void ADC_ConfigADC(void);
static void ADC_ConfigDMA(void);
static void ADC_ConfigTIM2(void);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Initialize and start ADC
  * @param  None
  * @retval None
  */
void ADC_Config(void)
{
    /* ADC configuration */
    ADC_ConfigADC();

    /* DMA configuration */
    ADC_ConfigDMA();

    /* TIM2 configuration */
    ADC_ConfigTIM2();
}

/**
  * @brief  Configure ADC peripheral
  * @param  None
  * @retval None
  */
static void ADC_ConfigADC(void)
{
    /* Enable ADC1 clock */
    CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, ENABLE);

    /* Initialize and configure ADC1 */
    ADC_Init(ADC1, ADC_ConversionMode_Single, ADC_Resolution_12Bit, ADC_Prescaler_1);
    ADC_SamplingTimeConfig(ADC1, ADC_Group_SlowChannels, ADC_SamplingTime_384Cycles);
    ADC_SamplingTimeConfig(ADC1, ADC_Group_FastChannels, ADC_SamplingTime_384Cycles);

    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);

    /* Enable ADC1 Voltage meter channel */
    ADC_ChannelCmd(ADC1, VOLTAGE_CHANNEL, ENABLE);
    ADC_SchmittTriggerConfig(ADC1, VOLTAGE_CHANNEL, DISABLE);

    /* Enable ADC1 Current meter channel */
    ADC_ChannelCmd(ADC1, CURRENT_CHANNEL, ENABLE);
    ADC_SchmittTriggerConfig(ADC1, CURRENT_CHANNEL, DISABLE);

#ifdef ADC_MEASURE_REF_VOLT
    /* ADC Voltage Reference */
    ADC_VrefintCmd(ENABLE);
    /* Enable ADC1 Vrefint Channel */
    ADC_ChannelCmd(ADC1, ADC_Channel_Vrefint, ENABLE);
#endif // ADC_MEASURE_REF_VOLT

    /* Enable ADC1 DMA requests*/
    ADC_DMACmd(ADC1, ENABLE);

    /* Start ADC1 Conversion using TIM1 TRGO*/
    ADC_ExternalTrigConfig(ADC1, ADC_ExtEventSelection_Trigger3, ADC_ExtTRGSensitivity_Rising);
}

/**
  * @brief  Configure DMA peripheral
  * @param  None
  * @retval None
  */
static void ADC_ConfigDMA(void)
{
    /* Enable DMA1 clock */
    CLK_PeripheralClockConfig(CLK_Peripheral_DMA1, ENABLE);

    /* Connect ADC to DMA channel 0 */
    SYSCFG_REMAPDMAChannelConfig(REMAP_DMA1Channel_ADC1ToChannel0);

    DMA_Init(DMA1_Channel0, ADC_BUFFER_ADDRESS,
           ADC1_DR_ADDRESS,
           ADC_BUFFER_SIZE,
           DMA_DIR_PeripheralToMemory,
           DMA_Mode_Circular,
           DMA_MemoryIncMode_Inc,
           DMA_Priority_High,
           DMA_MemoryDataSize_HalfWord);

    /* DMA Channel0 enable */
    DMA_Cmd(DMA1_Channel0, ENABLE);

    /* Enable DMA1 channel0 Transfer complete interrupt */
    DMA_ITConfig(DMA1_Channel0, DMA_ITx_TC, ENABLE);

    /* DMA enable */
    DMA_GlobalCmd(ENABLE);
}

/**
  * @brief  Configure TIM2 peripheral
  * @param  None
  * @retval None
  */
static void ADC_ConfigTIM2(void)
{
    /* Enable TIM2 clock */
    CLK_PeripheralClockConfig(CLK_Peripheral_TIM2, ENABLE);

    /* Initializes the TIM2 Time Base Unit */
    TIM2_TimeBaseInit(TIM_PRESCALER, TIM2_CounterMode_Up, TIM_PERIOD);

    /* Selects the TIM2 Update Request Interrupt source */
    TIM2_UpdateRequestConfig(TIM2_UpdateSource_Regular);

    /* Master Mode selection: Update event */
    TIM2_SelectOutputTrigger(TIM2_TRGOSource_Update);

    /* Enable TIM2 */
    TIM2_Cmd(ENABLE);
}

/**
  * @brief DMA1 channel0 and channel1 Interrupt routine.
  * @param  None
  * @retval None
  */
INTERRUPT_HANDLER(DMA1_CHANNEL0_1_IRQHandler,2)
{
    static uint8_t idx = 0;
    static uint16_t va1=0, va2=0, ca1=0, ca2=0;

    if (ADC_Buffer[0] < va1 && ADC_Buffer[0] < va2) {
      VoltageFastBuffer[idx] = va1;
    } else if (ADC_Buffer[0] > va1 && ADC_Buffer[0] > va2) {
      VoltageFastBuffer[idx] = va1;
    } else {
      VoltageFastBuffer[idx] = ADC_Buffer[0];
    }
    va2 = va1;
    va1 = ADC_Buffer[0];

    if (ADC_Buffer[1] < ca1 && ADC_Buffer[1] < ca2) {
      CurrentFastBuffer[idx] = ca1;
    } else if (ADC_Buffer[1] > ca1 && ADC_Buffer[1] > ca2) {
      CurrentFastBuffer[idx] = ca1;
    } else {
      CurrentFastBuffer[idx] = ADC_Buffer[1];
    }
    ca2 = ca1;
    ca1 = ADC_Buffer[1];

    idx ++;
    if(idx >= FAST_BUFFER_SIZE) {
        idx = 0;
    }

    /* Clear IT Pending Bit */
    DMA1_Channel0->CSPR &= (uint8_t)~(uint8_t)(DMA1_IT_TC0 & (uint8_t)0x06);
}

/**
  * @brief  Return average values from fast buffers
  * @param  None
  * @retval Latest_Voltage
  */
uint16_t * ADC_GetValues(void) {
  static uint16_t avgVal[ADC_BUFFER_SIZE];
  uint32_t volt=0, curr=0, shunt_volt=0;
  uint8_t i;

  /* Summarize buffers values */
  for(i=0;i<FAST_BUFFER_SIZE;i++){
    volt += VoltageFastBuffer[i];
    curr += CurrentFastBuffer[i];
  }

  curr += 4;
  curr /= 8;
  // компенсация смещения нуля ОУ
  if (curr > ADC_OU_ZERO_DRIFT) {
    curr -= ADC_OU_ZERO_DRIFT;

    curr *= ADC_REF;
    shunt_volt = curr;
    curr += ADC_ODIV_H; // для округления
    curr /= ADC_ODIV; // делим на К -- на выходе ток в мА

    /* так как шунт стоит в разрыве земли до выхода из БП
       компенсируем падение напряжения на нём. */
    shunt_volt *= ADC_OU_MUL_DIV;
    shunt_volt += (ADC_OU_MUL/2);
    shunt_volt /= ADC_OU_MUL; // значение с выхода ОУ преобразуем в значение со входа ОУ
  } else {
    curr = 0;
  }

  /* Попытка оверсемплинга, чтобы сделать шаг ~1мВ
     делим на 8, умножаем на опорное напряжение, умножаем на входной делитель
     и делим на (12 бит АЦП * 8). */
  volt += 4; // для округления
  volt /= 8; // усредняем
  volt *= ADC_REF;
  volt *= ADC_VOLT_K;
  volt -= shunt_volt;
  volt += ADC_ORES_H;
  volt /= ADC_ORES;

  avgVal[0] = (uint16_t)volt;
  avgVal[1] = (uint16_t)curr;

  return avgVal;
}

/**
  * @}
  */

/************************ (C) Vladimir N. Shilov *****END OF FILE****/
