AnsweredAssumed Answered

How do I read a voltage using the A/D converter in the most basic way?

Question asked by arnold_w on Jun 21, 2016
Latest reply on Jun 21, 2016 by arnold_w
I am working with the STM32F405 microcontroller and I would like to measure a couple of stable voltages on two pins (PB1 and PA2, respectively). I do not wish to use injection, the DMA controller, I don't care about processes being locked, I don't want to use a trigger, etc. Basically, all I want is to read a couple of voltages using a blocking function. This worked fine in the past when I was using an old version of the HAL-files, but when I updated them using STM32CubeMx it stopped working. I have compared and combined the two versions to try to find where it breaks down and the problem seems to be in the function HAL_ADC_PollForConversion. Does anybody know what is wrong and how I solve the problem?

Common functions:
static void ADC_Init(ADC_HandleTypeDef* hadc)
{
    ADC->CCR &= ~(ADC_CCR_ADCPRE);                                               // Set ADC parameters
    ADC->CCR |=  hadc->Init.ClockPrescaler;                                      // Set the ADC clock prescaler
    hadc->Instance->CR1 &= ~(ADC_CR1_SCAN);                                      // Set ADC scan mode
    hadc->Instance->CR1 |=  ADC_CR1_SCANCONV(hadc->Init.ScanConvMode);           // Set ADC scan mode
    hadc->Instance->CR1 &= ~(ADC_CR1_RES);                                       // Set ADC resolution
    hadc->Instance->CR1 |=  hadc->Init.Resolution;                               // Set ADC resolution
    hadc->Instance->CR2 &= ~(ADC_CR2_ALIGN);                                     // Set ADC data alignment
    hadc->Instance->CR2 |= hadc->Init.DataAlign;                                 // Set ADC data alignment
  
    // Enable external trigger if trigger selection is different of software start.
    // Note: This configuration keeps the hardware feature of parameter
    //       ExternalTrigConvEdge "trigger edge none" equivalent to software start.
    if(hadc->Init.ExternalTrigConv != ADC_SOFTWARE_START)
    {
        hadc->Instance->CR2 &= ~(ADC_CR2_EXTSEL);                                // Select external trigger to start conversion
        hadc->Instance->CR2 |= hadc->Init.ExternalTrigConv;                      // Select external trigger to start conversion
        hadc->Instance->CR2 &= ~(ADC_CR2_EXTEN);                                 // Select external trigger polarity
        hadc->Instance->CR2 |= hadc->Init.ExternalTrigConvEdge;                  // Select external trigger polarity
    }
    else
    {
        hadc->Instance->CR2 &= ~(ADC_CR2_EXTSEL);                                // Reset the external trigger
        hadc->Instance->CR2 &= ~(ADC_CR2_EXTEN);                                 // Reset the external trigger
    }
  
    hadc->Instance->CR2 &= ~(ADC_CR2_CONT);                                      // Enable or disable ADC continuous conversion mode
    hadc->Instance->CR2 |= ADC_CR2_CONTINUOUS(hadc->Init.ContinuousConvMode);    // Enable or disable ADC continuous conversion mode
  
    if(hadc->Init.DiscontinuousConvMode != DISABLE)
    {
        hadc->Instance->CR1 |= (uint32_t)ADC_CR1_DISCEN;                         // Enable the selected ADC regular discontinuous mode
        hadc->Instance->CR1 &= ~(ADC_CR1_DISCNUM);                               // Set the number of channels to be converted in discontinuous mode
        hadc->Instance->CR1 |=  ADC_CR1_DISCONTINUOUS(hadc->Init.NbrOfDiscConversion);
    }
    else
    {
        hadc->Instance->CR1 &= ~(ADC_CR1_DISCEN);                                // Disable the selected ADC regular discontinuous mode
    }
  
    hadc->Instance->SQR1 &= ~(ADC_SQR1_L);                                       // Set ADC number of conversion
    hadc->Instance->SQR1 |=  ADC_SQR1(hadc->Init.NbrOfConversion);               // Set ADC number of conversion
    hadc->Instance->CR2 &= ~(ADC_CR2_DDS);                                       // Enable or disable ADC DMA continuous request
    hadc->Instance->CR2 |= ADC_CR2_DMAContReq(hadc->Init.DMAContinuousRequests); // Enable or disable ADC DMA continuous request
    hadc->Instance->CR2 &= ~(ADC_CR2_EOCS);                                      // Enable or disable ADC end of conversion selection
    hadc->Instance->CR2 |= ADC_CR2_EOCSelection(hadc->Init.EOCSelection);        // Enable or disable ADC end of conversion selection
}
  
  
static HAL_StatusTypeDef HAL_ADC_Init_(ADC_HandleTypeDef* hadc)
{
    HAL_StatusTypeDef tmp_hal_status = HAL_OK;
    if(hadc->State == HAL_ADC_STATE_RESET)
    {
        hadc->ErrorCode = HAL_ADC_ERROR_NONE;                                    // Initialize ADC error code
        hadc->Lock = HAL_UNLOCKED;                                               // Allocate lock resource and initialize it
    }
  
    // Configuration of ADC parameters if previous preliminary actions are correctly completed.
    if (HAL_IS_BIT_CLR(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL))
    {
        ADC_STATE_CLR_SET(hadc->State,                                           // Set ADC state
                          HAL_ADC_STATE_REG_BUSY | HAL_ADC_STATE_INJ_BUSY,
                          HAL_ADC_STATE_BUSY_INTERNAL);
        ADC_Init(hadc);                                                          // Set ADC parameters
        hadc->ErrorCode = HAL_ADC_ERROR_NONE;
        ADC_STATE_CLR_SET(hadc->State,                                           // Set ADC state
                          HAL_ADC_STATE_BUSY_INTERNAL, HAL_ADC_STATE_READY);
    }
    else
    {
        tmp_hal_status = HAL_ERROR;
    }
  
    __HAL_UNLOCK(hadc);                                                          // Release Lock
    return tmp_hal_status;
}
  
  
static HAL_StatusTypeDef HAL_ADC_ConfigChannel_(ADC_HandleTypeDef* hadc, ADC_ChannelConfTypeDef* sConfig)
{
    volatile uint32_t counter = 0U;
    __HAL_LOCK(hadc);                                                            // Process locked
  
    if (ADC_CHANNEL_9 < sConfig->Channel)
    {   // ADC_Channel_10 ... ADC_Channel_18 is selected
        hadc->Instance->SMPR1 &= ~ADC_SMPR1(ADC_SMPR1_SMP10, sConfig->Channel);  // Clear the old sample time
        hadc->Instance->SMPR1 |= ADC_SMPR1(sConfig->SamplingTime, sConfig->Channel);  // Set the new sample time
    }
    else
    {   // ADC_Channel include in ADC_Channel_[0..9]
        hadc->Instance->SMPR2 &= ~ADC_SMPR2(ADC_SMPR2_SMP0, sConfig->Channel);   // Clear the old sample time
        hadc->Instance->SMPR2 |= ADC_SMPR2(sConfig->SamplingTime, sConfig->Channel);  // Set the new sample time
    }
  
    if (sConfig->Rank < 7U)
    {   // Rank 1 to 6
        hadc->Instance->SQR3 &= ~ADC_SQR3_RK(ADC_SQR3_SQ1, sConfig->Rank);       // Clear the old SQx bits for the selected rank
        hadc->Instance->SQR3 |= ADC_SQR3_RK(sConfig->Channel, sConfig->Rank);    // Set the SQx bits for the selected rank
    }
    else if (sConfig->Rank < 13U)
    {   // Rank 7 to 12
        hadc->Instance->SQR2 &= ~ADC_SQR2_RK(ADC_SQR2_SQ7, sConfig->Rank);       // Clear the old SQx bits for the selected rank
        hadc->Instance->SQR2 |= ADC_SQR2_RK(sConfig->Channel, sConfig->Rank);    // Set the SQx bits for the selected rank
    }
    else
    {   // Rank 13 to 16
        hadc->Instance->SQR1 &= ~ADC_SQR1_RK(ADC_SQR1_SQ13, sConfig->Rank);      // Clear the old SQx bits for the selected rank
        hadc->Instance->SQR1 |= ADC_SQR1_RK(sConfig->Channel, sConfig->Rank);    // Set the SQx bits for the selected rank
    }
  
  
    if ((hadc->Instance == ADC1) && (sConfig->Channel == ADC_CHANNEL_VBAT))
    {   // ADC1 Channel_18 is selected enable VBAT Channel
        ADC->CCR |= ADC_CCR_VBATE;                                               // Enable the VBAT channel
    }
  
    if ((hadc->Instance == ADC1) && ((sConfig->Channel == ADC_CHANNEL_TEMPSENSOR) || (sConfig->Channel == ADC_CHANNEL_VREFINT)))
    {   // If ADC1 Channel_16 or Channel_17 is selected enable TSVREFE Channel(Temperature sensor and VREFINT)
        ADC->CCR |= ADC_CCR_TSVREFE;                                             // Enable the TSVREFE channel
        if((sConfig->Channel == ADC_CHANNEL_TEMPSENSOR))
        {   // Delay for temperature sensor stabilization time. Compute number of CPU cycles to wait for
            counter = (ADC_TEMPSENSOR_DELAY_US * (SystemCoreClock / 1000000U));
            while(counter != 0U)
            {
                counter--;
            }
        }
    }
  
    __HAL_UNLOCK(hadc);                                                           // Process unlocked
    return HAL_OK;
}
  
  
static HAL_StatusTypeDef HAL_ADC_Start_(ADC_HandleTypeDef* hadc)
{
    volatile uint32_t counter = 0U;
    __HAL_LOCK(hadc);                                                            // Process locked
  
    // Enable the ADC peripheral
    // Check if ADC peripheral is disabled in order to enable it and wait during
    // Tstab time the ADC's stabilization
    if((hadc->Instance->CR2 & ADC_CR2_ADON) != ADC_CR2_ADON)
    {
        __HAL_ADC_ENABLE(hadc);                                                  // Enable the Peripheral
  
        // Delay for ADC stabilization time
        // Compute number of CPU cycles to wait for
        counter = (ADC_STAB_DELAY_US * (SystemCoreClock / 1000000U));
        while(counter != 0U)
        {
            counter--;
        }
    }
  
    if(HAL_IS_BIT_SET(hadc->Instance->CR2, ADC_CR2_ADON))
    {   // Start conversion if ADC is effectively enabled
        // Set ADC state
        // - Clear state bitfield related to regular group conversion results
        // - Set state bitfield related to regular group operation
        ADC_STATE_CLR_SET(hadc->State,
                  HAL_ADC_STATE_READY | HAL_ADC_STATE_REG_EOC | HAL_ADC_STATE_REG_OVR,
                  HAL_ADC_STATE_REG_BUSY);
  
        // If conversions on group regular are also triggering group injected, update ADC state.                                                      */
        if (READ_BIT(hadc->Instance->CR1, ADC_CR1_JAUTO) != RESET)
        {
            ADC_STATE_CLR_SET(hadc->State, HAL_ADC_STATE_INJ_EOC, HAL_ADC_STATE_INJ_BUSY);
        }
  
        // State machine update: Check if an injected conversion is ongoing
        if (HAL_IS_BIT_SET(hadc->State, HAL_ADC_STATE_INJ_BUSY))
        {
            // Reset ADC error code fields related to conversions on group regular
            CLEAR_BIT(hadc->ErrorCode, (HAL_ADC_ERROR_OVR | HAL_ADC_ERROR_DMA));
        }
        else
        {
            ADC_CLEAR_ERRORCODE(hadc);                                           // Reset ADC all error code fields
        }
  
        // Unlock before starting ADC conversions: in case of potential
        // interruption, to let the process to ADC IRQ Handler.
        __HAL_UNLOCK(hadc);                                                      // Process unlocked
  
        // Clear regular group conversion flag and overrun flag
        // (To ensure of no unknown state from potential previous ADC operations)
        __HAL_ADC_CLEAR_FLAG(hadc, ADC_FLAG_EOC);
  
        // Check if Multimode enabled
        if(HAL_IS_BIT_CLR(ADC->CCR, ADC_CCR_MULTI))
        {
            // If no external trigger present enable software conversion of regular channels
            if((hadc->Instance->CR2 & ADC_CR2_EXTEN) == RESET)
            {
                // Enable the selected ADC software conversion for regular group
                hadc->Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
            }
        }
        else
        {
            // If instance of handle correspond to ADC1 and  no external trigger present enable software conversion of regular channels
            if((hadc->Instance == ADC1) && ((hadc->Instance->CR2 & ADC_CR2_EXTEN) == RESET))
            {
                // Enable the selected ADC software conversion for regular group
                hadc->Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
            }
        }
    }
    return HAL_OK;
}
  
  
uint16_t readADC(uint32_t channel)
{
    ADC_HandleTypeDef  AdcHandle;
    __HAL_RCC_ADC1_CLK_ENABLE();
  
    // Configure the ADC peripheral
    AdcHandle.Instance                   = ADC1;
    AdcHandle.Init.ClockPrescaler        = ADC_CLOCKPRESCALER_PCLK_DIV2;
    AdcHandle.Init.Resolution            = ADC_RESOLUTION_12B;
    AdcHandle.Init.ScanConvMode          = DISABLE;
    AdcHandle.Init.ContinuousConvMode    = DISABLE;
    AdcHandle.Init.DiscontinuousConvMode = DISABLE;
    AdcHandle.Init.NbrOfDiscConversion   = 1;
    AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;
    AdcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_CC1;
    AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
    AdcHandle.Init.NbrOfConversion       = 1;
    AdcHandle.Init.DMAContinuousRequests = DISABLE;
    AdcHandle.Init.EOCSelection          = DISABLE;
  
    if(HAL_ADC_Init_(&AdcHandle) != HAL_OK)
    {   // Initialization Error
        DEBUG_CODE(   UART2_SW_TransmitNullTerminatedString((uint8_t*) "\r\nInitialization Error in ADC");   )
    }
  
    // Configure ADC regular channel
    ADC_ChannelConfTypeDef sConfig;
      sConfig.Channel      = channel;
      sConfig.Rank         = 1;
      sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
      sConfig.Offset       = 0;
  
    if(HAL_ADC_ConfigChannel_(&AdcHandle, &sConfig) != HAL_OK)
    {   // Channel Configuration Error
        DEBUG_CODE(   UART2_SW_TransmitNullTerminatedString((uint8_t*) "\r\nChannel Configuration Error in ADC");  )
    }
  
    // Start the conversion process
    if(HAL_ADC_Start_(&AdcHandle) != HAL_OK)
    {   // Start Conversion Error
        DEBUG_CODE(   UART2_SW_TransmitNullTerminatedString((uint8_t*) "\r\nStart Conversion Error in ADC");   )
    }
  
    // Wait for the end of conversion
    //  Before starting a new conversion, you need to check the current state of
    //  the peripheral; if it’s busy you need to wait for the end of current
    //  conversion before starting a new one.
    //  For simplicity reasons, this example is just waiting till the end of the
    //  conversion, but application may perform other tasks while conversion
    //  operation is ongoing.
    HAL_ADC_PollForConversion_(&AdcHandle, 10);
  
    // Check if the continuous conversion of regular channel is finished
    if(AdcHandle.State == HAL_ADC_STATE_EOC_REG)
    {
        return AdcHandle.Instance->DR;
    }
    else
    {
        UART2_SW_TransmitNullTerminatedString((uint8_t*) "\r\nAdcHandle.State is not HAL_ADC_STATE_EOC_REG and this is bad");
        return 0xFFFF;
    }
}

Function that doesn't work
static HAL_StatusTypeDef HAL_ADC_PollForConversion_(ADC_HandleTypeDef* hadc, uint32_t Timeout)
{
    // Verification that ADC configuration is compliant with polling for each conversion:
    // Particular case is ADC configured in DMA mode and ADC sequencer with
    // several ranks and polling for end of each conversion.
    // For code simplicity sake, this particular case is generalized to
    // ADC configured in DMA mode and polling for end of each conversion.
    if (HAL_IS_BIT_SET(hadc->Instance->CR2, ADC_CR2_EOCS) &&
        HAL_IS_BIT_SET(hadc->Instance->CR2, ADC_CR2_DMA))
    {
        SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);                        // Update ADC state machine to error
        __HAL_UNLOCK(hadc);                                                      // Process unlocked
        return HAL_ERROR;
    }
  
    uint32_t numOneMillisecondIterations = Timeout;
    while ((0 < numOneMillisecondIterations--) && (!(__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOC))))
    {
        delayMicroseconds(1000);
    }
    if (numOneMillisecondIterations == 0)
    {
        SET_BIT(hadc->State, HAL_ADC_STATE_TIMEOUT);                             // Update ADC state machine to timeout
        __HAL_UNLOCK(hadc);                                                      // Process unlocked
        return HAL_TIMEOUT;
    }
  
    __HAL_ADC_CLEAR_FLAG(hadc, ADC_FLAG_STRT | ADC_FLAG_EOC);                    // Clear regular group conversion flag
    SET_BIT(hadc->State, HAL_ADC_STATE_REG_EOC);                                 // Update ADC state machine
  
    // Determine whether any further conversion upcoming on group regular
    // by external trigger, continuous mode or scan sequence on going.
    // Note: On STM32F4, there is no independent flag of end of sequence.
    //       The test of scan sequence on going is done either with scan
    //       sequence disabled or with end of conversion flag set to
    //       of end of sequence.
    if (ADC_IS_SOFTWARE_START_REGULAR(hadc) && (hadc->Init.ContinuousConvMode == DISABLE) &&
       (HAL_IS_BIT_CLR(hadc->Instance->SQR1, ADC_SQR1_L) || HAL_IS_BIT_CLR(hadc->Instance->CR2, ADC_CR2_EOCS)))
    {
        CLEAR_BIT(hadc->State, HAL_ADC_STATE_REG_BUSY);                          // Set ADC state
        if (HAL_IS_BIT_CLR(hadc->State, HAL_ADC_STATE_INJ_BUSY))
        {
            SET_BIT(hadc->State, HAL_ADC_STATE_READY);
        }
    }
    return HAL_OK;
}

Function that works if I comment out one line of code. If I don't comment out the line of code, the file won't compile.
static HAL_StatusTypeDef HAL_ADC_PollForConversion_(ADC_HandleTypeDef* hadc, uint32_t Timeout)
{
    // Verification that ADC configuration is compliant with polling for each conversion:
    // Particular case is ADC configured in DMA mode and ADC sequencer with
    // several ranks and polling for end of each conversion.
    // For code simplicity sake, this particular case is generalized to
    // ADC configured in DMA mode and polling for end of each conversion.
    if (HAL_IS_BIT_SET(hadc->Instance->CR2, ADC_CR2_EOCS) &&
        HAL_IS_BIT_SET(hadc->Instance->CR2, ADC_CR2_DMA))
    {
        SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);                        // Update ADC state machine to error
        __HAL_UNLOCK(hadc);                                                      // Process unlocked
        return HAL_ERROR;
    }
  
    uint32_t numOneMillisecondIterations = Timeout;
    while ((0 < numOneMillisecondIterations--) && (!(__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOC))))
    {
        delayMicroseconds(1000);
    }
    if (numOneMillisecondIterations == 0)
    {
        SET_BIT(hadc->State, HAL_ADC_STATE_TIMEOUT);                             // Update ADC state machine to timeout
        __HAL_UNLOCK(hadc);                                                      // Process unlocked
        return HAL_TIMEOUT;
    }
  
    __HAL_ADC_CLEAR_FLAG(hadc, ADC_FLAG_STRT | ADC_FLAG_EOC);                    // Clear regular group conversion flag
    SET_BIT(hadc->State, HAL_ADC_STATE_REG_EOC);                                 // Update ADC state machine
  
    /* Check if an injected conversion is ready */
    if(hadc->State == HAL_ADC_STATE_EOC_INJ)
    {
//        hadc->State = HAL_ADC_STATE_EOC_INJ_REG;                                 // Change ADC state
    }
    else
    {
        hadc->State = HAL_ADC_STATE_EOC_REG;                                     // Change ADC state
    }
  
    return HAL_OK;
}

Outcomes