/**
  ******************************************************************************
  * @file     ADC_user.c
  * @author   SINOMCU-AE
  * @brief    ADC user
  *    
  *          This file provides functions to ADC using:
  *    Activate_ADC();
  *    Gat_ADC_Conversion_Result();
  *    ADC_ERROR();     
  ******************************************************************************
  * @attention 
  *                           DISCLAIMER
  * The present firmware which is for guidance only aims at providing customers
  * with coding information regarding their products in order for them to save
  * time. As a result, SinoMCU shall not be held liable for any direct,indirect
  * or consequential damages with respect to any claims arising from the content 
  * of such firmware and/or the use made by customers of the coding information 
  * contained herein in connection with their products.
  *
  * <h2><center>&copy; Copyright (c) 2021 sinomcu All rights reserved.</center></h2>
  *
  ******************************************************************************
  */

#include "stdio.h"
#include "SysTick_Delay.h"
#include "ADC_user.h"
#include "GPIO_CFG.h"

// this example time,you need calculate actual time + margin,unit: ms 
#define ADC_CALIBRATION_TIMEOUT_MS         ((uint32_t) 2)
#define ADC_ENABLE_TIMEOUT_MS              ((uint32_t) 2)
#define ADC_CONVERSION_TIMEOUT_MS          ((uint32_t) 500)
#define ADC_UNITARY_CONVERSION_TIMEOUT_MS  ((uint32_t) 2)
#define ADC_DELAY_CALIB_ENABLE_CPU_CYCLES  (MS32_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES * 16)

/**
  * @brief  ADC1 activation procedure to make it ready to convert
  * @param  None
  * @retval ADCErrorStatus
  */
ADCErrorStatus Activate_ADC(void)
{
  __IO uint32_t wait_loop_index = 0;
  __IO uint32_t backup_setting_adc_dma_transfer = 0U;
  ADCErrorStatus Activate_ADC_status =  ADC_NO_ERROR;

  #if (USE_TIMEOUT == 1)
  uint32_t Timeout = 0; 
  #endif
 
  if (MS32_ADC_IsEnabled(ADC1) == 0)
  {
    // Disable ADC DMA ,during calibration
    backup_setting_adc_dma_transfer = MS32_ADC_REG_GetDMATransfer(ADC1);
    MS32_ADC_REG_SetDMATransfer(ADC1, MS32_ADC_REG_DMA_TRANSFER_NONE);
    
    MS32_ADC_StartCalibration(ADC1);
    
    // Poll for ADC effectively calibrated
    #if (USE_TIMEOUT == 1)
    Timeout = ADC_CALIBRATION_TIMEOUT_MS;
    #endif 
    
    while (MS32_ADC_IsCalibrationOnGoing(ADC1) != 0)
    {
    #if (USE_TIMEOUT == 1)
      if (MS32_SYSTICK_IsActiveCounterFlag())
      {
        if(Timeout-- == 0)
        {
          Activate_ADC_status = ADC_CALI_ERROR;  // ADC Calibration Time out
          break;
        }
      }
    #endif
    }
    
    // Restore ADC DMA transfer 
    MS32_ADC_REG_SetDMATransfer(ADC1, backup_setting_adc_dma_transfer); 
    
    // Delay between calibration and ADC enable
    wait_loop_index = (ADC_DELAY_CALIB_ENABLE_CPU_CYCLES >> 1);
    while(wait_loop_index != 0)
    {
      wait_loop_index--;
    }
    
    MS32_ADC_Enable(ADC1); 
    
    //Poll ADC ready to convert
    #if (USE_TIMEOUT == 1)
    Timeout = ADC_ENABLE_TIMEOUT_MS;
    #endif
    
    while (MS32_ADC_IsActiveFlag_ADRDY(ADC1) == 0)
    {
    #if (USE_TIMEOUT == 1)
      if (MS32_SYSTICK_IsActiveCounterFlag())
      {
        if(Timeout-- == 0)
        {
          Activate_ADC_status = ADC_ADRDY_ERROR;
          break;
        }
      }
    #endif
    }
  }
  return Activate_ADC_status;
}

/**
  * @brief  ADC start Conversion,get result
  * @param  ADCErrorStatus *
  * @retval ADC result uint16(low 12bit)
  */
uint16_t Get_ADC_Conversion_Result(ADCErrorStatus * ADC_Status)
{
  uint16_t ADC_result = 0;
  #if (USE_TIMEOUT == 1)
  uint32_t Timeout = 0; 
  #endif
  MS32_ADC_REG_StartConversion(ADC1);

  #if (USE_TIMEOUT == 1)
  Timeout = ADC_UNITARY_CONVERSION_TIMEOUT_MS;
  #endif

  while (MS32_ADC_IsActiveFlag_EOC(ADC1) == 0)   // ADC_REG_InitStruct.DMATransfer = MS32_ADC_REG_DMA_TRANSFER_LIMITED
  {
  #if (USE_TIMEOUT == 1)
    if (MS32_SYSTICK_IsActiveCounterFlag())
    {
      if(Timeout-- == 0)
      {
        *ADC_Status =ADC_CONVERSION_EOS_ERROR;   // ADC Conversion End Time out
        break;
      }
    }
  #endif
  }
  MS32_ADC_ClearFlag_EOC(ADC1);
  
  ADC_result = MS32_ADC_REG_ReadConversionData12(ADC1);
  return ADC_result;
}

/**
  * @brief  Adc error handle.
  * @param  ADC_status
  * @retval None
  */
void ADC_ERROR(ADCErrorStatus error_status)
{
  switch(error_status)
  {
    case ADC_CALI_ERROR:
      printf("\r\n##Error:ADC_CALI_ERROR\r\n");
      break;
    case ADC_ADRDY_ERROR:
      printf("\r\n##Error:ADC_ADRDY_ERROR\r\n");
      break;
    case ADC_CONVERSION_EOC_ERROR:
      printf("\r\n##Error:ADC_CONVERSION_EOC_ERROR\r\n");
      break;
    case ADC_CONVERSION_EOS_ERROR:
      printf("\r\n##Error:ADC_CONVERSION_EOS_ERROR\r\n");
      break;
    case ADC_OVERRUN_ERROR:
      printf("\r\n##Error:ADC_OVERRUN_ERROR\r\n");
      break;
    default:
      break;    
  }
      
  for(;;)
  {
    LED1_TOGGLE();
    SysTick_Ms(ADC_ERROR_LED_BLINK_HALF_PER);
  }
}
/******************************** END OF FILE *********************************/
