/**
  ******************************************************************************
  * @file    ys32f030_vrefbuf.c
  * @author  YSPRING Application Team
  * @version V1.0.1
  * @date    2023.6.25
  * @brief   This file provides firmware functions to manage the following 
  *          functionalities of the VREFBUF peripheral:
  *           + ADC reference voltage slection management
  *           + Peripherals Configuration
  * 
  *  @verbatim
  *
    ===========================================================================
                         ##### How to use this driver #####
    ===========================================================================
      (#) Enable the SYSCFG APB2 clock using RCC_APB2PeriphClockCmd()
      (#) Configure the ADC reference voltage using VREFBUF_ADC_Config()
      (#) Voltage reference buffer mode enable using VREFBUF_Cmd() function
      (#) Use VREFBUF_Trim() to write CCR.
    @endverbatim
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "ys32f030_vrefbuf.h"
#include "ys32f030_rcc.h"
#include "ys32f030_pwr.h"

/** @addtogroup YS32F030_StdPeriph_Driver
  * @{
  */

/** @defgroup VREFBUF 
  * @brief VREFBUF driver modules
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup VREFBUF_Private_Functions
  * @{
  */

/** @defgroup VREFBUF_Group1 Peripherals Configuration functions
 *  @brief   Peripherals Configuration
 *
@verbatim
  ==============================================================================
               ##### Peripherals Configuration functions #####
  ==============================================================================

@endverbatim
  * @{
  */
	
/**
  * @brief  Config HSI 48M 128Div.
  * @param  None
  * @retval None
  */
void SystemClock_ConfigHSI48(void)
{
		FLASH->ACR = 0x03;
		while(FLASH->ACR != 0x03)
		{
		}
		
		RCC_HSI_CLK(RCC_HSI_16M, RCC_HSI_CLK_Div1);
		RCC_HSICmd(ENABLE);
		while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET)
		{
		}
    RCC_SYSCLKConfig(RCC_SOURCE_HSI);
    while(RCC_GetSYSCLKSource() != RCC_SYSCLKSource_HSI);	
		
    RCC_HSI_CLK(RCC_HSI_48M, RCC_HSI_CLK_Div128);

    RCC_HCLKConfig(RCC_SYSCLK_Div2);
    RCC_PCLKConfig(RCC_HCLK_Div1);		

    RCC_SYSCLKConfig(RCC_SOURCE_HSI);
    while(RCC_GetSYSCLKSource() != RCC_SYSCLKSource_HSI);
}	

/**
  * @brief  Obtain the currently configured clock.
  * @retval None
  */
void Get_Current_SystemClock(Vref_ClocksTypeDef* Vref_Clocks)
{
	Vref_Clocks->vref_latency = FLASH->ACR & 0x03;
	
  Vref_Clocks->vref_tmp = (RCC->CFGR>>RCC_CFGR_SWS_Pos) & 0x07;
  switch (Vref_Clocks->vref_tmp)
  {
    case 0x00:  /* HSI used as system clock */
			Vref_Clocks->vref_hsiClk = (RCC->ICSCR >> RCC_ICSCR_HSI_FS_Pos) & 0x03;
      Vref_Clocks->vref_hsiDiv = (RCC->CR>>RCC_CR_HSIDIV_Pos) & 0x07;
      break;
    case 0x01:  /* HSE used as system clock */
      break;
    case 0x02:  /* PLL used as system clock */
      /* Get PLL clock source and multiplication factor ----------------------*/
      Vref_Clocks->vref_pllmull = (RCC->CFGR & RCC_CFGR_PLLMUL)>>RCC_CFGR_PLLMUL_Pos;
      Vref_Clocks->vref_pllsource = RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC;
      if (Vref_Clocks->vref_pllsource == 0x00)
      {
        /* HSI oscillator clock divided by 2 selected as PLL clock entry */
				Vref_Clocks->vref_hsiClk = (RCC->ICSCR >> RCC_ICSCR_HSI_FS_Pos) & 0x03;
				Vref_Clocks->vref_hsiDiv = (RCC->CR>>RCC_CR_HSIDIV_Pos) & 0x07;
      }    
      break;  
    case 0x03:  /* LSI used as system clock */
      break;
    case 0x04:  /* LSE used as system clock */
      break;
  }
	
	Vref_Clocks->vref_hclk = (RCC->CFGR>>RCC_CFGR_HPRE_Pos) & 0x0f;
	Vref_Clocks->vref_pclk = (RCC->CFGR>>RCC_CFGR_PPRE_Pos) & 0x07;
}

/**
  * @brief  Config original System clock.
  * @retval None
  */
void Config_Original_SystemClock(Vref_ClocksTypeDef* Vref_Clocks)
{
		int i = 0;
		
		if(Vref_Clocks->vref_tmp == 0x00)  /* HSI used as system clock */
		{
			if(Vref_Clocks->vref_hsiClk == 0x00)
				RCC_HSI_CLK(RCC_HSI_48M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
			else if(Vref_Clocks->vref_hsiClk == 0x01)
				RCC_HSI_CLK(RCC_HSI_36M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
			else if(Vref_Clocks->vref_hsiClk == 0x02)
				RCC_HSI_CLK(RCC_HSI_24M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
			else
				RCC_HSI_CLK(RCC_HSI_16M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
			
			RCC_HSICmd(ENABLE); 
			while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
			RCC_SYSCLKConfig(RCC_SOURCE_HSI);
			while(RCC_GetSYSCLKSource() != RCC_SYSCLKSource_HSI);
		}
		else if(Vref_Clocks->vref_tmp == 0x01)  /* HSE used as system clock */
		{
			RCC_HSEConfig(RCC_HSE_ON);
			while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
			for(i = 1000; i > 0; i--);
			RCC_SYSCLKConfig(RCC_SOURCE_HSE); 
			while(RCC_GetSYSCLKSource() != RCC_SYSCLKSource_HSE);
		}
		else if(Vref_Clocks->vref_tmp == 0x02)  /* PLL used as system clock */
		{
			if(Vref_Clocks->vref_pllsource == 0x00)  /* HSI as PLL source */
			{
				if(Vref_Clocks->vref_hsiClk == 0x00)
					RCC_HSI_CLK(RCC_HSI_48M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
				else if(Vref_Clocks->vref_hsiClk == 0x01)
					RCC_HSI_CLK(RCC_HSI_36M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
				else if(Vref_Clocks->vref_hsiClk == 0x02)
					RCC_HSI_CLK(RCC_HSI_24M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));
				else
					RCC_HSI_CLK(RCC_HSI_16M, (Vref_Clocks->vref_hsiDiv << RCC_CR_HSIDIV_Pos));			

				RCC_HSICmd(ENABLE);
				while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET); 
				RCC_PLLConfig(RCC_PLLSource_HSI, (Vref_Clocks->vref_pllmull<<RCC_CFGR_PLLMUL_Pos)); 
				RCC_PLLCmd(ENABLE);
				while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
				RCC_SYSCLKConfig(RCC_SOURCE_PLL);
				while(RCC_GetSYSCLKSource() != RCC_SOURCE_PLL);
			}
			else  /* HSE as PLL source */
			{
				RCC_HSEConfig(RCC_HSE_ON); 
				while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); 
				RCC_PLLConfig(RCC_PLLSource_HSE, (Vref_Clocks->vref_pllmull<<RCC_CFGR_PLLMUL_Pos));
				RCC_PLLCmd(ENABLE);
				while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
				for(i = 1000; i > 0; i--);
				RCC_SYSCLKConfig(RCC_SOURCE_PLL);
				while(RCC_GetSYSCLKSource() != RCC_SOURCE_PLL);
			}
		}
		else if(Vref_Clocks->vref_tmp == 0x03)  /* LSI used as system clock */
		{
			RCC_LSICmd(ENABLE); 
			RCC_SYSCLKConfig(RCC_SOURCE_LSI);
			while(RCC_GetSYSCLKSource() != RCC_SYSCLKSource_LSI);
		}
		else if(Vref_Clocks->vref_tmp == 0x04)  /* LSE used as system clock */
		{
			RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
			PWR->CR1 |= PWR_CR1_DBP;
      RCC_LSEConfig(RCC_LSE_ON);
      while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
			RCC_SYSCLKConfig(RCC_SOURCE_LSE); 
			while(RCC_GetSYSCLKSource() != RCC_SYSCLKSource_LSE);
		}
		
		RCC_HCLKConfig((Vref_Clocks->vref_hclk<<RCC_CFGR_HPRE_Pos));
    RCC_PCLKConfig((Vref_Clocks->vref_pclk<<RCC_CFGR_PPRE_Pos));
		SystemCoreClockUpdate();
		
		FLASH->ACR = Vref_Clocks->vref_latency;
		while(FLASH->ACR != Vref_Clocks->vref_latency)
		{
		}		
}

/**
  * @brief  ENABLE or DISABLE the VREFBUF peripheral.
  * @param  NewState: new state of VREFBUF peripheral. 
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void VREFBUF_Cmd(FunctionalState NewState)
{
	Vref_ClocksTypeDef vref_clock = {0};
	
  /* Check the parameters */
  assert_param(IS_FUNCTIONAL_STATE(NewState));
	 
	Get_Current_SystemClock(&vref_clock);

  if (NewState == ENABLE)
  {
		SystemClock_ConfigHSI48();
    /* Set the ENABLE bit */
    VREFBUF->CSR |= VREFBUF_CSR_ENVR;
		
		Config_Original_SystemClock(&vref_clock);
  }
  else
  {
     /* Reset the ENABLE bit */   
    VREFBUF->CSR &= ~VREFBUF_CSR_ENVR;
  }
}

/**
  * @brief  ADC reference voltage selection
  * @param  VrefConfig: This parameter can be:
  *            @arg VREFBUF_ADC_VREF: ADC voltage come from VREF
  *            @arg VREFBUF_ADC_VCC: ADC voltage come from VCC
  *            @arg VREFBUF_ADC_VREFBUF: ADC voltage come from VREFBUF
  * @retval None
  */
void VREFBUF_ADC_Config(uint32_t VrefConfig)
{
  /* Check the parameters */  
  assert_param(IS_VREFBUF_ADC_CONFIG(VrefConfig));

  VREFBUF->CSR &= ~VREFBUF_CSR_VREF_SEL;
  VREFBUF->CSR |= VrefConfig;
}

/**
  * @brief  Calibration value
  * @param  TrimValue: VREFBUF trimming value 
  *         This parameter can be: from 0x00 to 0x07
  * @retval None
  */
void VREFBUF_Trim(uint32_t TrimValue)
{
  /* Check the parameters */  
  assert_param(IS_VREFBUF_TRIM_VALUE(TrimValue));

  VREFBUF->CCR = TrimValue;
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
