Electrical – ADC continuous conversion STM32F103

adcinterruptsmicrocontrollerstm32stm32f10x

I'm facing with a problem regarding continuous acquisition of ADC channel in STM32F103.

I use the interrupt at the end of conversion to call a callback function to store the acquired value.

The problem regards the callback that is called only the first time.

I configured my project using STM32CubeMx for continuous acquisition and interrupt generation.

This is the ADC configuration:

hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
    Error_Handler();
}

/**Configure Regular Channel 
*/
sConfig.Channel = ADC_CHANNEL_11;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    Error_Handler();
}

This is my acquisition function:

ea_ADC_Err_Code_t ea_ADC_n_data_read(Adc_Channel_t channel, adc_eoc_callback adc_cb) 
{
    ea_ADC_Err_Code_t err_code = ADC_ERR;     
    ADC_ChannelConfTypeDef sConfig;

    adc_read_value    = 0;
    adc_eoc_cb        = adc_cb;
    n_adc_acquisition = ADC_MAX_CONS_ACQ;

    /* Deinit ADC */       
    //while(HAL_ADC_DeInit(&adc_handler) != HAL_OK);

    /* Initialize ADC */ 
    //HAL_ADC_Init(&adc_handler);

    /* Configure ADC Channel */
    sConfig.Channel = channel;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
    HAL_ADC_ConfigChannel(&adc_handler, &sConfig);

    /* Set ADC callback */
    HAL_ADC_ConvCpltCallback(&adc_handler);

    /* ADC Calibration */
    //HAL_ADCEx_Calibration_Start(&adc_handler);

    /* Start conversion with interrupt*/
    if (HAL_ADC_Start_IT(&adc_handler) == HAL_OK)
    {
      err_code = ADC_OK;
    }

    return err_code;
}

And finally my callback:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (n_adc_acquisition)
    {
        adc_read_value += HAL_ADC_GetValue(&adc_handler);
        n_adc_acquisition--;
        edi_Print_L1("ADC Callback %d\n", n_adc_acquisition);
    }
    else
    {
        HAL_ADC_Stop_IT(&adc_handler);
        adc_read_value = adc_read_value >> ADC_DIVIDE_BY_EIGTH;
        adc_eoc_cb(adc_read_value);
    }
}

Did I forget something in the callback?

Best Answer

You forgot to enabled the ADC interrupts, which can be done something like this:

HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);

The next thing with HAL's interrupt handling is that there are two weak function declaration.

  1. for void ADC_IRQHandler()
  2. for void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

You have to implement the ADC_IRQHandler() first, this will be called when an interrupt is generated by the ADC. Inside this function you have to call the HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc), parameter is your ADC's handler (ADC_HandleTypeDef).

void ADC_IRQHandler()
{
    HAL_ADC_IRQHandler(&hadc1);
    //HAL_ADC_IRQHandler(&hadc2); <--- In case of a second ADC
}

Now, HAL_ADC_IRQHandler() will check every kind of error for you (you can check it in its implementation) and if everything is fine, it will call the void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc). As it also has a weak declaration, so you have to implement the function.

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        int converted_value = HAL_ADC_GetValue(hadc);
        // Do stuff
    }

    //if(hadc->Instance == ADC2)  // <-- In case of a second ADC
    //{
    //
    //}
}

In STM32CubeMX, in the ADC1 Configuration window, there is tab called NVIC Settings. Here you can enable the global interrupts for ADC1 and ADC2, and if you do so, then STM32Cube will implements the void ADC_IRQHandler(void) in the stm32f1xx_it.c file.

/******************************************************************************/
/* STM32F4xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f4xx.s).                    */
/******************************************************************************/

/**
* @brief This function handles ADC1 and ADC2 global interrupts.
*/
void ADC_IRQHandler(void)
{
  /* USER CODE BEGIN ADC_IRQn 0 */

  /* USER CODE END ADC_IRQn 0 */
  HAL_ADC_IRQHandler(&hadc1);
  /* USER CODE BEGIN ADC_IRQn 1 */

  /* USER CODE END ADC_IRQn 1 */
}

Also it's just a brief summary, so I suggest to read through the tutorial, @Eugene Sh showed you.