STM32_ADC_HAL - GitMasterNikanjam/ARM_WiKi GitHub Wiki

There are three method to get the ADC data from sensor.
1- POLLFORCONVERSION
2- INTERRUPT
3- DMA

Also, it can be used in Single Mode or Multiple Channel Mode to acquire data.
In Single Mode, only one channel of the ADC is used.
In Multiple Mode, a multi-channel ADC is used.

Important Functions and Variable

ADC_HandleTypeDef hadc1;

typedef struct 
{
  uint32_t Channel;    /*!< Specifies the channel to configure into ADC regular group.
                       This parameter can be a value of @ref ADC_channels */
  uint32_t Rank;       /*!< Specifies the rank in the regular group sequencer.
                       This parameter must be a number between Min_Data = 1 and Max_Data = 16 */
  uint32_t SamplingTime;    /*!< Sampling time value to be set for the selected channel.
  Unit: ADC clock cycles
  Conversion time is the addition of sampling time and processing time (12 ADC clock cycles at ADC resolution 12 bits, 11 cycles at 10 bits, 9 cycles at 
  8 bits, 7 cycles at 6 bits).
  This parameter can be a value of @ref ADC_sampling_times
  Caution: This parameter updates the parameter property of the channel, that can be used into regular and/or injected groups.
  If this same channel has been previously configured in the other group (regular/injected), it will be updated to last setting.
  Note: In case of usage of internal measurement channels (VrefInt/Vbat/TempSensor),
  sampling time constraints must be respected (sampling time can be adjusted in function of ADC clock frequency and sampling time setting)
  Refer to device datasheet for timings values, parameters TS_vrefint, TS_temp (values rough order: 4us min). */
  uint32_t Offset;                 /*!< Reserved for future use, can be set to 0 */
}ADC_ChannelConfTypeDef;

HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout);
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
void HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_ConfigChannel(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig);

POLLFORCONVERSION Method for Multiple channels

1- Select and enable channels of adc in STM32CUBEMX software. for example select IN0 and IN1 for ADC1.

image

2- Config The ADC Configuration tab as below as:

image

Select Number of conversion: 2
For each channel, parameters such as sampling time and rank can be chosen.
Generate the code.

3- In the code, there are some functions that initialize the ADC and define the HAL ADC structure for ADC1.


ADC_HandleTypeDef hadc1;


static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = 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_RIGHT;
  hadc1.Init.NbrOfConversion = 2;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

4- There are some changes that needed to do.
In the MX_ADC1_Init() function, you must change hadc1.Init.NbrOfConversion to 1. and comment all setting for Channels and ranks.

static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = 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_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
/*
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
*/
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
	/*
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	*/
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

5- Define functions in global space in main.c file for selection of channel and relative settings.
Notic that in all these functions the sConfig.Rank is 1.

void adc_select_0(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

void adc_select_1(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

6- now you can use these below code in while/loop for reading adc channel 0 and 1.

 while (1)
  {
	adc_select_0();
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	val[0] = HAL_ADC_GetValue(&hadc1);
	HAL_ADC_Stop(&hadc1);
		
	adc_select_1();
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	val[1] = HAL_ADC_GetValue(&hadc1);
	HAL_ADC_Stop(&hadc1);
	
	HAL_Delay(100);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

INTERRUPT Method for Multiple channels

1- Select and enable multiple ADC channel. eg IN0 and IN1.
2- Enable Scan conversion mode and disable Continuous conversion.
Select EOC flag at the end of single channel conversion.
select number of conversion for regular conversion mode to 1.

image

3- Enable NVIC for ADC interrupts.
4- Generate the code and go to code and main.c file in main.c file above of main function write this below code:

volatile uint32_t val1,val2;
ADC_ChannelConfTypeDef sConfig = {0};
volatile uint8_t adc_flag = 0;			// flag for adc interrupt is busy

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	if(sConfig.Channel == ADC_CHANNEL_0)
		val1 = HAL_ADC_GetValue(&hadc1);
	else if(sConfig.Channel == ADC_CHANNEL_1)
		val2 = HAL_ADC_GetValue(&hadc1);
	adc_flag = 0;
}

void ADC_Read_IT(uint8_t channel_number)  // function for select adc channel and request interrupt for that
{
	while(adc_flag == 1){}		
		
	adc_flag = 1;
	sConfig.Rank = 1;
	switch(channel_number)
	{
	 case 0:
		sConfig.Channel = ADC_CHANNEL_0;
		sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
	 break;
	 case 1:
		sConfig.Channel = ADC_CHANNEL_1;
		sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
	 break;
	}
	
	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}
	
	HAL_ADC_Start_IT(&hadc1);
	
}

and in while loop you can write for read adc of each channels that you need.

while (1)
  {
	ADC_Read_IT(0); // read adc channel 0 by interrupt
	ADC_Read_IT(1); // read adc channel 1 by interrupt
		
	HAL_Delay(5);
  }

DMA Method for Multiple channels

1- Select and enable multiple ADC channel eg IN0 and IN1.
2- Set ADC configuration as below as:
Dont Forget to enable DMA continuous Requests.

image

3- Set DMA for ADC as below as:
Choose Word width for DMA because of 12bit of ADC resolution.

image

4- Generate the code and go to main.c file.
define a uint32_t val[2] variable in global space.
write HAL_ADC_Start_DMA(&hadc1, val, 2); before while loop.
now the adc values store in val variable.