cancel
Showing results for 
Search instead for 
Did you mean: 

adc fails to initialize

mo_krauti
Associate III

Board: stm32h753zi nucleo

Firmware & CubeMX: latest version

I have configured the adc to continous conversion and DMA output. The initialization fails in main.c in HAL_ADC_INIT. I stepped through the HAL lib and saw that LL_ADC_IsInternalRegulatorEnabled returns the HAL_ERROR.

The docstring of this function says

  /* Verification that ADC voltage regulator is correctly enabled, whether    */
  /* or not ADC is coming from state reset (if any potential problem of       */
  /* clocking, voltage regulator would not be enabled).                       */
  if (LL_ADC_IsInternalRegulatorEnabled(hadc->Instance) == 0UL)

I do not find any documents online which describe how the clock frequency of the adc should be set up. Could you please give me a hint.

18 REPLIES 18
mo_krauti
Associate III

Thank you, in fact it really is disabled:

ADC12EN: 0 ADC1/2 Peripheral Clocks Enable

Do I have to enable it manually in CubeMX. I do not remember doing it in the other working CubeMX project.

waclawek.jan
Super User

I don't know, I don't use CubeMX, sorry. I would assume it gets set automatically once you start using that peripheral in CubeMX.

But you can check the other working CubeMX project, there should be some macro enabling that clock, and then try to find it in the non-working CubeMX project.

JW

mo_krauti
Associate III

I stepped through the adc initialization using the debugger once again.

In /Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_adc.c:

  /* Actions performed only if ADC is coming from state reset:                */
  /* - Initialization of ADC MSP                                              */
  if (hadc->State == HAL_ADC_STATE_RESET)
  {
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
    // not set, left it out for better readability
#else
    /* Init the low level hardware */
    HAL_ADC_MspInit(hadc);
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */

    /* Set ADC error code to none */
    ADC_CLEAR_ERRORCODE(hadc);

    /* Initialize Lock */
    hadc->Lock = HAL_UNLOCKED;
  }

But the hadc1 state is 0x100, so HAL_ADC_MspInit is never called. This is the method enabling the ADC clock, so it is never enabled.
I tried manually calling this method before the generated ADC setup method, but this still resulted in a hardfault. When I just added a call to the  __HAL_RCC_ADC12_CLK_ENABLE(); macro, the hardfault disappeared. But I do not think that the adc will work because the adc clock, the gpio channels and the dma are also configured in HAL_ADC_MspInit.

waclawek.jan
Super User

> But the hadc1 state is 0x100

Why? Where is it set so (data breakpoint a.k.a. watchpoint on that variable may reveal this)? What does that mean? And what's the value of HAL_ADC_STATE_RESET?

JW

mo_krauti
Associate III

HAL_ADC_STATE_RESET is 0x00000000UL

I used a write memory breakpoint to watch hadc1.State. After resetting the board the State variable is already set to 0x00000100UL HAL_ADC_STATE_REG_BUSY on the first breakpoint.
Then the failed LL_ADC_IsInternalRegulatorEnabled function called mentioned in my original post cause the State to change to 0x00000110UL, so HAL_ADC_STATE_ERROR_INTERNAL is also set.

waclawek.jan
Super User

> After resetting the board the State variable is already set to 0x00000100UL HAL_ADC_STATE_REG_BUSY on the first breakpoint.

Data breakpoints are not precise, so the processor stops a couple of cycles *after* the given variable is written (unlike the usual instruction breakpoints, which stop *just before* the marked instruction is executed). So it's more tricky to use during debugging, as you have to "walk back" somewhat, and in some cases that's simply impractical, e.g. if the write occurs just before a function return.

Another way to potentially tackle this is to look at Cube/HAL sources (and potentially yours, too) to find instances where HAL_ADC_STATE_REG_BUSY symbol is used.

As I've said I don't use Cube/HAL so these are generic recommendations. Maybe somebody more versed in Cube/HAL will chime in and enlighten us on what can potentially go wrong in that code.

JW

mo_krauti
Associate III

Thank you for the hint about data breakpoints. Now I see that just before the breakpoint, the State is really written:

      /* Set ADC state                                                        */
      /* - Clear state bitfield related to regular group conversion results   */
      /* - Set state bitfield related to regular 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_EOSMP,
                        HAL_ADC_STATE_REG_BUSY);

But this should just clear the adc.

I noticed that when starting up HAL_ADC_Start_DMA is called. But I only do this later in the code and is is stuck in the Error Handler before. Somehow this call must survive my openocd reset routine and still execute afterwards.

mo_krauti
Associate III

I discovered my error. I have built some cpp classes as abstractions for io functionality. There is also an analog class in which I started the analog DMA transfer in the constructor. Then I created an global instances of this class so the constructor interfered with the ADC startup logic in main. I was sure that the error could not originate from my abstraction code because the fault already occurred in the CubeMX generated main.c, but I did not think of the constructor of the global variable also being called at startup.

Thank you very much for your help.

waclawek.jan
Super User

Thanks for coming back with the solution.

JW