cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 ADC Baremetal - Cannot get ADC module ready

AJelen
Associate II

I am trying to make baremetal ADC driver for following development board STM32H723ZG Nucleo (144 pins).

To simplify, lets assume (for example) to use PA3. According to Datasheet, PA3 corresponds to Channel 15. In order to use ADC module and proper pins, I perform following actions:

  1. Enable Clk access to GPIOA
  2. Set PA3 as Analog Mode
  3. Enable Clk access to ADC1 module
  4. Set conversion mode of ADC1 to continuous
  5. Set sequence length to 1 (since only channel 15 is converted)
  6. Set ADC channel to be converted in SQR1 (channel 15)
  7. Disable deep powered mode by clearing DEEPPWD in Control Register
  8. Enable regulator voltage VREGEN in Control Register
  9. Wait for LDO to be supplied (LDORDY in ADC_ISR)
  10. Enable the ADC by setting ADEN in Control Register
  11. Start conversion with ADSTART in Control Register

Now the crazy thing is. All steps succeed. I can see the results in SFRs. But conversion is still not happening. Observing ADC_ISR bit ADRDY, it looks like ADC is not ready at all.

I dug through documentation forwards and backwards.

No errors triggered as well. Board is directly from the ST themselves.

Anyone has any helpful tips? :)

Thanks!

code:

#define ADC_1_CONV          (0U << 4U)
#define ADC_ISR_LDO_RDY     (1U << 12U)
#define PA3_ADC_CHN15           (15U << 6U)
 
void ADC_Init(void){
 
    /**Configure the ADC pin**/
    // Enable Clock Access to pin
    RCC_SetAHB4_PeriphClock(RCC_AHB4ENR_GPIOAEN);
    // Configure ADC pin to Analog mode
    GPIO_SetPinMode(GPIOA,PA3_PIN,GPIO_ANALOG_MODE);
 
    /* Configure the ADC module */
    // Enable clock to ADC
    RCC_SetAHB1_PeriphClock(RCC_AHB1ENR_ADC12EN);
    // Set the conversion mode (continuous)
    SET_BIT(ADC1->CFGR,ADC_CFGR_CONT);
    // Set sequencer length
    MODIFY_REG(ADC1->SQR1,ADC_SQR1_L,ADC_1_CONV);
    // Set ADC channel
    MODIFY_REG(ADC1->SQR1,ADC_SQR1_SQ1,PA3_ADC_CHN15);
    //SET_BIT(ADC1->PCSEL_RES0,ADC_PCSEL_PCSEL_3);
    //ADC_ISR_ADRDY
 
    // Enable ADC module
    CLEAR_BIT(ADC1->CR,ADC_CR_DEEPPWD);
    SET_BIT(ADC1->CR,ADC_CR_ADVREGEN);
    while (!(ADC1->ISR & ADC_ISR_LDO_RDY));
    //SET_BIT(ADC1->ISR,ADC_ISR_ADRDY);
    SET_BIT(ADC1->CR,ADC_CR_ADEN);
    //while (!(ADC1->ISR & ADC_ISR_ADRDY));
    for (int i = 0; i<10000; i++){};
 
    // Start conversion
    SET_BIT(ADC1->CR,ADC_CR_ADSTART);
}

Please note, helper functions like

GPIO_SetPinMode and RCC_SetAHB1_PeriphClock

work with 100% certainty :)

8 REPLIES 8

ADC clock is detemined by ADCx_CCR.CKMODE, and by default it uses an APB-independent clock, adc_ker_ck_input, directly from RCC, See ADC clocks subchapter of ADC chapter. That clock is selected in RCC_D3CCIPR.ADCSEL and is set to pll2_p_ck by default, and I guess you haven't configured PLL2 for that.

Also note, that RCC is in D3 domain and it may take some time between enabling a peripheral's clock and being able to use that peripheral. Read Peripheral allocation subchapter of RCC chapter in RM.

The 'H7 is an overly complicated beast and maybe not the best platform for first experiments.

I don't use 'H7.

JW

DGree.2
Associate III

What do you mean " I can see the results in SFRs"? Are you saying that you read the correct value from ADC_DR, based on the voltage you're applying to pin PA3? Or is the reading inaccurate, or is it always 0 or always 0xFFFF or random?

What do you mean "But conversion is still not happening"? The two lines you commented out about ADC_ISR_ADRDY, I believe those are mandatory. See Reference Manual link you posted above, Section 28.4.9. It says you must clear the ADRDY flag by setting the bit, and then wait until the CPU sets the bit in the register.

Thank you for you reply!

"I can see the results in SFRs" --> is related to the points mentioned above. The configuration of registers succeed. Hence the results of successfully configured registers can be seen in SFRs.

"But conversion is still not happening" --> no conversion occurring. Meaning SAR ADC is not doing anything or perhaps anything between ADC and DR is misconfigured.

About the commented lines. I have first tried with them uncommented. However, there was no difference.

DGree.2
Associate III

So what value do you read from ADC DR? What voltage are you applying to the pin PA3? Keep in mind that the impedance of the voltage source must be very low if you are using a fast sampling time. To keep it easy, set the sampling time to the slowest possible in the SMPR register.

As JW says, I agree with. The CKMODE register bits must be set correctly as well. Easiest way is to set these bits to use the AHB clock and not worry about PLL2.

You also should try enabling all associated ADC interrupts and create IRQ Handler functions for each. Then set a breakpoint or debug message in each IRQ handler. That may tell you something about what's happening.

I have written a driver for the H7 ADC, without any trouble, by using the LL library from ST. I would suggest using either the ST HAL or LL, or else using CubeMX to generate a working example code.

Once you get that working, if you want to remove the dependency on the HAL, then you can observe what registers are being set by the HAL or LL functions and do that yourself in your own way.

Hi Jan! Thanks for your reply.

Yeah I was planning to use 'F4 but somehow my HIL emulator is running on it. While waiting for another board, I said to myself "Lets give it a shot with H7, it cant be so hard"... :)

I have not paid enough attention to the dual clock domain. I inherently thought that the default settings for adc_ker_ck_input are to be synchronous with AHB.

Additionally, I assumed since ADC1 registers were successfully configured, that ADC kernel is provided with proper clock source. However, that is not the case. Thanks for the hint!

So no, PLL2 was not configured at all. I have left ADCSEL at default and therefore no clock was provided to kernel.

I will have a look at the peripheral allocation chapter.

Three important take-aways:

1) For the delay handling when enabling, I assume page 353 provides excellent description on what to do.

2) As per your explanation and "Kernel clock selection" chapter on page 335 in RM, an adc_hclk is per default used only for register and bus interface. Thats why I could set ADC registers successfully. But Kernel clock was not provided correctly.

3) I have to read documentation more carefully.

But I got to admit. Clock gating is still a bit unclear to me. Have to dive deeper into RM.

I will let you know about the result in the following days :)

Hi! Thanks for the reply :)

I am not reading anything. Or should I say, the DR is 0.

PA3 is connected to the 3.3v stable voltage source through voltage divide and common ground between the components. SMPR is set to max cycles.

As you and JW already noticed, the CKMODE was not set correctly. This was due to my lack of attention towards Dual Clock Domain.

Funnily enough, just while you mentioned interrupts, I programmed few of them to see what happens. Of course as expected not much, since kernel clock is not properly gated to ADC.

Anyhow, the ST HAL is good place to start, great way to build projects for rapid prototyping.

But I need to understand the low level behavior and dependencies between different peripherals, therefore baremetal.

Making basic UART and GPIO driver was not as problematic as the ADC.

Thanks for the hints! :)

Can we do it using cubeMX because I am facing issue to achieve the sample rate of 3.6MSPS?

Chris76
Associate

Hi, i know its a long time ago but i just had the same problem and want to share my solution.

For me it was not so easy because there are many configurations to do.

This example is for reading adc-value from port PA0 (ADC1_INP16) on STM32H755

Chris76_2-1724929627337.png

 

Chris76_1-1724929567783.png

Regards, Chris