#include "libs/task.h"
#include "hal/adc.h"

static ADC_HandleTypeDef hadc1;

void HAL_ADC1_Init(void){	
	ADC_InjectionConfTypeDef sConfigInjected = {0};
	ADC_ChannelConfTypeDef sConfig = {0};
	
	hadc1.Instance = ADC1;
	hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
	hadc1.Init.Resolution = ADC_RESOLUTION_12B;
	hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
	hadc1.Init.ContinuousConvMode = DISABLE;
	hadc1.Init.DiscontinuousConvMode = DISABLE;
	hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
	hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
	hadc1.Init.DataAlign = ADC_DATAALIGN_LEFT;
	hadc1.Init.NbrOfConversion = 2;
	hadc1.Init.DMAContinuousRequests = DISABLE;
	hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
	hadc1.Init.LowPowerAutoWait = DISABLE;
	hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
	if (HAL_ADC_Init(&hadc1) != HAL_OK)
	{
	  Error_Handler();
	}
	/** Configure Injected Channel
	*/
	sConfigInjected.InjectedChannel = ADC_CHANNEL_1;
	sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
	sConfigInjected.InjectedSingleDiff = ADC_SINGLE_ENDED;
	sConfigInjected.InjectedNbrOfConversion = 3;
	sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
	sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONV_EDGE_RISING;
	sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
	sConfigInjected.AutoInjectedConv = DISABLE;
	sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
	sConfigInjected.QueueInjectedContext = ENABLE;
	sConfigInjected.InjectedOffset = 0;
	sConfigInjected.InjectedOffsetNumber = ADC_OFFSET_NONE;
	if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
	{
	  Error_Handler();
	}
	/** Configure Injected Channel
	*/
	sConfigInjected.InjectedChannel = ADC_CHANNEL_7;
	sConfigInjected.InjectedRank = ADC_INJECTED_RANK_2;
	if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
	{
	  Error_Handler();
	}
	/** Configure Injected Channel
	*/
	sConfigInjected.InjectedChannel = ADC_CHANNEL_6;
	sConfigInjected.InjectedRank = ADC_INJECTED_RANK_3;
	if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
	{
	  Error_Handler();
	}
	/** Configure Regular Channel
	*/
	sConfig.Channel = ADC_CHANNEL_2;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	sConfig.SingleDiff = ADC_SINGLE_ENDED;
	sConfig.SamplingTime = ADC_SAMPLETIME_61CYCLES_5;
	sConfig.OffsetNumber = ADC_OFFSET_NONE;
	sConfig.Offset = 0;
	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
	{
	  Error_Handler();
	}
	/** Configure Regular Channel
	*/
	sConfig.Channel = ADC_CHANNEL_8;
	sConfig.Rank = ADC_REGULAR_RANK_2;
	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
	{
	  Error_Handler();
	}

}


void HAL_ADC1_Enable(void) {
	ADC_TypeDef * ADCx = hadc1.Instance;
    /* disable IT and flags in case of LL driver usage
     * workaround for unwanted interrupt enabling done by LL driver */
    LL_ADC_DisableIT_EOC( ADCx );
    LL_ADC_ClearFlag_EOC( ADCx );
    LL_ADC_DisableIT_JEOC( ADCx );
    LL_ADC_ClearFlag_JEOC( ADCx );
    if (LL_ADC_IsEnabled (ADCx) == 0)
    {
		if ( LL_ADC_IsInternalRegulatorEnabled(ADCx) == 0u)
		{
		  /* Enable ADC internal voltage regulator */
		  LL_ADC_EnableInternalRegulator(ADCx);
		
		  /* Wait for Regulator Startup time */
		  /* Note: Variable divided by 2 to compensate partially			  */
		  /*	   CPU processing cycles, scaling in us split to not		  */
		  /*	   exceed 32 bits register capacity and handle low frequency. */
		  volatile uint32_t wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US / 10UL) * (SystemCoreClock / (100000UL * 2UL)));	  
		  while(wait_loop_index != 0UL)
		  {
			wait_loop_index--;
		  }
		}
		
		LL_ADC_StartCalibration( ADCx, LL_ADC_SINGLE_ENDED );
		while ( LL_ADC_IsCalibrationOnGoing( ADCx) == 1u) 
		{}
		/* ADC Enable (must be done after calibration) */
		/* ADC5-140924: Enabling the ADC by setting ADEN bit soon after polling ADCAL=0 
		* following a calibration phase, could have no effect on ADC 
		* within certain AHB/ADC clock ratio.
		*/
		while (  LL_ADC_IsActiveFlag_ADRDY( ADCx ) == 0u)  
		{ 
		  LL_ADC_Enable(  ADCx );
		}
		/* Clear JSQR from CubeMX setting to avoid not wanting conversion*/
		//LL_ADC_INJ_StartConversion( ADCx ); 
		//LL_ADC_INJ_StopConversion(ADCx);
		/* TODO: check if not already done by MX */
		LL_ADC_INJ_SetQueueMode( ADCx, LL_ADC_INJ_QUEUE_2CONTEXTS_END_EMPTY );

      	/* Only the Interrupt of the first ADC is enabled. 
       	* As Both ADCs are fired by HW at the same moment 
       	* It is safe to consider that both conversion are ready at the same time*/
      	LL_ADC_ClearFlag_JEOS( ADCx );
      	LL_ADC_EnableIT_JEOS( ADCx );
    }	

	HAL_NVIC_EnableIRQ(ADC1_IRQn);

	//LL_ADC_INJ_StartConversion( ADCx );
}

void HAL_ADC1_InJ_StartConvert(void) {
	ADC_TypeDef * ADCx = hadc1.Instance;
	while(LL_ADC_INJ_IsConversionOngoing( ADCx ) == 0) {
		LL_ADC_INJ_StartConversion( ADCx );
	}
}

void HAL_ADC1_ChanConfig(u32 channel) {
	if (LL_ADC_IsEnabled(hadc1.Instance) == 0 )
	{
	 	LL_ADC_DisableIT_EOC(hadc1.Instance);
		LL_ADC_ClearFlag_EOC(hadc1.Instance);
	 	LL_ADC_DisableIT_JEOC(hadc1.Instance);
	 	LL_ADC_ClearFlag_JEOC(hadc1.Instance);
	
	  	LL_ADC_StartCalibration( hadc1.Instance, LL_ADC_SINGLE_ENDED );
	  	while ( LL_ADC_IsCalibrationOnGoing( hadc1.Instance ) )
	  	{ }
	  	LL_ADC_Enable( hadc1.Instance );
	}
	/* reset regular conversion sequencer length set by cubeMX */
	LL_ADC_REG_SetSequencerLength( hadc1.Instance, LL_ADC_REG_SEQ_SCAN_DISABLE );
	/* configure the sampling time (should already be configured by for non user conversions)*/
	LL_ADC_SetChannelSamplingTime ( hadc1.Instance, __LL_ADC_DECIMAL_NB_TO_CHANNEL(channel) ,ADC_SAMPLETIME_61CYCLES_5);
}

u16 HAL_ADC1_ReadValue(u32 channel)
{
  	u16 retVal;

  	LL_ADC_REG_SetSequencerRanks( hadc1.Instance,
                                LL_ADC_REG_RANK_1,
                                __LL_ADC_DECIMAL_NB_TO_CHANNEL( channel ) );

  	LL_ADC_REG_ReadConversionData12(hadc1.Instance);

    LL_ADC_REG_StartConversion(hadc1.Instance);
  	/* Wait until end of regular conversion */
	u64 StartTime = get_mseconds();
  	while ( LL_ADC_IsActiveFlag_EOC(hadc1.Instance) == 0u ) {
		if (get_mseconds() - StartTime >= 1) {
			LL_ADC_REG_StartConversion(hadc1.Instance);
			StartTime = get_mseconds();
		}
	}
  	retVal = LL_ADC_REG_ReadConversionData12(hadc1.Instance);
	return retVal;
}

