/**
  ******************************************************************************
  * @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))

#define ADC_REF             3300
#define ADC_DIV             4095
#define ADC_ODIV            32768
#define ADC_VOLT_K          11
#define ADC_CURR_K          20

// (Частота МК (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;

    VoltageFastBuffer[idx] = ADC_Buffer[0];
    CurrentFastBuffer[idx] = 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;
  uint16_t shunt_volt;
  uint8_t i;

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

  /* Попытка оверсемплинга, чтобы сделать шаг ~1мВ
     делим на 8, умножаем на опорное напряжение, умножаем на входной делитель
     и делим на (12 бит АЦП * 8). */
#ifdef ADC_USE_V_OVERS
  volt >>= 3;
  volt *= ADC_REF;
  volt *= ADC_VOLT_K;
  volt /= ADC_ODIV;
#else
  volt += 32;
  volt >>= 6;
  volt *= ADC_REF;
  volt *= ADC_VOLT_K;
  volt /= ADC_DIV;
#endif // ADC_USE_V_OVERS

  /* получаем среднее по току, затем фильтруем */
  curr += 32;
  curr >>= 6;
#ifdef ADC_USE_C_FILTR
  uint16_t middle;
  middle = curr + 8;
  middle &= 0xFFF0;
  curr = 0;
  uint8_t cnt = 0;
  uint16_t tmp;
   for(i=1; i<FAST_BUFFER_SIZE; i+=2){
    tmp = CurrentFastBuffer[i];
    tmp += 8;
    tmp &= 0xFFF0;
    if(tmp == middle){
      cnt ++;
      curr += CurrentFastBuffer[i];
    }
   }

  /* Усредняем преобразованный результат
     и переводим код АЦП в милиамперы */
  if(cnt > 0){
    curr /= cnt;
  } else {
    curr = middle;
  }
#endif // ADC_USE_C_FILTR

  curr *= ADC_REF;
  curr /= ADC_DIV;

  shunt_volt = curr / ADC_CURR_K;
  volt -= shunt_volt;

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

  return avgVal;
}

/**
  * @}
  */

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