cancel
Showing results for 
Search instead for 
Did you mean: 

Getting a reading from STM32 ADC when connecting a pull-up input pin to GND

RPD
Associate II

Using CubeIDE and a STM32F4 board I'm trying to start an ADC conversion and make one of the two LEDs blink depending on the ADC value - if the value is smaller than a certain limit then one LED should blink and the other stay off, but if the ADC value is larger than the limit I set then the other LED should blink. That should happen when I push a button and blinking should continue until the button is pushed down.

So I have 4 pins - two (G2 and D8) GPIO outputs for blinking LEDs, one (A0) pin is analog input, and one pin (F2) GPIO input for the pushbutton. It is set as pull-up and it is connected to GND through a pushbutton.

The relevant code is

/* USER CODE BEGIN 0 */
uint32_t adcVal;
/* USER CODE END 0 */
  /* USER CODE BEGIN 2 */
//HAL_ADC_Start(&hadc1);
HAL_ADC_Start_IT (&hadc1);
  /* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    adcVal = HAL_ADC_GetValue(&hadc1);
    if (!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_2)) {
        HAL_ADC_Start_IT (&hadc1);
        adcVal = HAL_ADC_GetValue(&hadc1);
        if (adcVal > 2000) {HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_8); HAL_GPIO_WritePin(GPIOG, GPIO_PIN_2, GPIO_PIN_RESET);}
        else {HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_2); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET);}
        HAL_Delay(500);
        //HAL_ADC_Start_IT (&hadc1);
    }
    else {HAL_GPIO_WritePin(GPIOG, GPIO_PIN_2, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET);}
}
/* USER CODE END 4 */

I didn't put anything to the while(1) loop.

When compiling, I get no errors or warnings, when I run the code then the F2 pin is set HIGH (to 3V when I measure it with a tester, and goes to zero when the button is pushed) but the LEDs don't react at all, although voltage is applied to A0. I suspect I'm using the ADC in a wrong way. I want the ADC to stay idle and read and convert a value when the pin connected to the button goes low.

Probably I have a simple mistake somewhere but I think I need some advice in this matter.

5 REPLIES 5
TDK
Guru

Seems like the check for HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_2) should be in a main loop and not the ADC end of conversion callback. If the ADC is idle, this is never going to be called.

Also beware of calling HAL_ADC_Start_IT again without the previous conversion completing. Read the return value from this an instrument an error handler.

If you feel a post has answered your question, please click "Accept as Solution".
RomainR.
ST Employee

Hi RPD,

You must never call HAL_Delay() from any peripheral callback interrupt.

Interrupt function or user interrupt callback must be short as possible and without overhead and without other API based on peripheral interrupt (in your case the Systick).

My advice concerning your application:

Use GPIO PG2 button in EXTI falling edge mode, write the callback and toggle state a boolean flag inside.

Use ADC with DMA Circular mode in continuous, and feed a buffer with a size according to your need.

Now in your ADC callback, count the number of sample (size of the DMA buffer) and perform an average of the buffer.

In your while(1) loop, check the state of the value of boolean flag and blink the LED depending of ADC average value you have computed into ADC callback.

BR

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

RomainR.
ST Employee

Something like this should works:

Only need to ajdust your GPIO for your board and configure ADC in DMA Circular + Continuous + ADC Interrupt

/* USER CODE BEGIN PD */
#define ADC_SAMPLE  (32u)
 
/* USER CODE END PD */
/* USER CODE BEGIN PV */
__IO uint16_t adcBuffer[ADC_SAMPLE] = {0,};
__IO uint32_t adcSUMResult = 0;
__IO uint16_t adcAVGResult = 0;
__IO uint16_t adcSample = 0;
__IO int8_t butTrigger = -1;
 
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
 
/**
  * @brief  Conversion complete callback in non-blocking mode.
  * @param hadc ADC handle
  * @retval None
  */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
  if (adcSample < ADC_SAMPLE)
  {
    adcSUMResult += HAL_ADC_GetValue(&hadc1);
    adcSample++;
  }
  else
  {
    adcAVGResult = adcSUMResult / (uint32_t)(ADC_SAMPLE);
    adcSUMResult = 0;
    adcSample = 0;
  }
}
 
/**
  * @brief  EXTI line detection callback.
  * @param  GPIO_Pin Specifies the port pin connected to corresponding EXTI line.
  * @retval None
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == GPIO_PIN_13)
  {
    if (butTrigger == 1)
      butTrigger = -1;
    else
      butTrigger = 1;  
  }
}
 
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
  
  /**> Start ADC1 with DMA in continuous mode */
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adcBuffer, ADC_SAMPLE);
                                                               
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (butTrigger == 1)
    {
      if (adcAVGResult > 2000)
      {
        HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
        HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
      }      
      else
      {
        HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);       
        HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
      }
      HAL_Delay(200);
    }
    else
    {
      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);   
    }
    
 
    /* USER CODE END WHILE */

Let try it

BR

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

RPD
Associate II

Right you are, now I put the if-statement into the loop like that

 while (1)
  {
	  HAL_ADC_Start_IT (&hadc1);
	  if (!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_2)) {
	  		if (adcVal > 2000) {HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_8); HAL_GPIO_WritePin(GPIOG, GPIO_PIN_2, GPIO_PIN_RESET);}
	  		else {HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_2); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET);}
	  		HAL_Delay(100);
	  		//HAL_ADC_Start_IT (&hadc1);
	  	}
	  	else {HAL_GPIO_WritePin(GPIOG, GPIO_PIN_2, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET);}
    /* USER CODE END WHILE */

and the conversion complete callback just gives a value to adcVal

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	adcVal = HAL_ADC_GetValue(&hadc1);
 
}

and it works! I just wonder that when HAL_ADC_Start_IT is called after the first if-statement, i.e. only when the button is pushed and the condition is satisfied then always an old conversion value is used first when the code after that if-statement is executed. E.g. one LED is blinking, then I release the button and turn the potentiometer to the other end and push the button again, and then the LED which was blinking lights up for one blink and after that the other LED starts blinking. So in the beginning the adcVal has an old value. When HAL_ADC_Start_IT is in the beginning of the loop (like shown above in the code snippet) then the adcVal is always updated and the previous LED does not light up for the first blink. But might it be an issue for the ADC when HAL_ADC_Start_IT is called in every loop cycle? E.g. how fast does the ADC operate in the single conversion mode, should there be a delay in the loop?

But I wonder why there is an old value stored in adcVal when HAL_ADC_Start_IT is called immediately after the button is pushed and adcVal is used after that?

Hello! Meanwhile I managed to make it work, thank you very much! I hadn't even tried DMA before... But as I understand, now the ADC is running on the background all the time, but is the continuous mode desirable when ADC is not used all the time? In my case, power supply is usually not an issue, but the general workload or computing power might be sometimes. I also noticed that averaging doesn't change the values much the ADC delivers, does the ADC have also an internal averaging cycle? But if I would use the continuous mode without a such buffer then would I have to be able to ask the ADC for a value always at the moment of a conversion? If I tried to read a value from it between the two conversions, would I get just null or an error or there would just be a delay until the next conversion?