STMicroelectronics / STM32CubeU5

Full Firmware Package for the STM32U5 series: HAL+LL drivers, CMSIS, BSP, MW, plus a set of Projects (examples and demos) running on all boards provided by ST (Nucleo, Evaluation and Discovery Kits).
Other
124 stars 67 forks source link

Extended calibration in 535/545 devices results in incorrect ADC operation, or hangs the system #29

Closed DonBrus closed 1 year ago

DonBrus commented 1 year ago

Describe the set-up

Describe the bug

When performing the calibration procedure for the ADC1 peripheral through the _HAL_ADCEx_CalibrationStart function, with mode=ADC_CALIB_OFFSET and for single ended channels, the system will either hang/crash or the ADC peripheral won't work correctly, though the configuration is correct. I pinned the problem to the following part of the RM: Reading calibration factor procedure (under section 33.4.8)

Once the calibration is complete (ADCAL bit cleared by hardware), the calibration factor can be read using the ADC_CALFACT2 register. > Nine read accesses are required to perform this operation[...]

These nine access are effectively not performed in the aforementioned function, as interacting with CALFACT2 takes place in the following part of code (stm32u5xx_hal_adc_ex:203):

/* Assess whether extended calibration is available on the selected device */
if ((dev_id == 0x455UL) || (dev_id == 0x476UL) || (((dev_id == 0x481UL) || (dev_id == 0x482UL)) && (rev_id >= 0x3000UL)))
{
  /* Perform extended calibration */
  /* Refer to ref manual for extended calibration procedure details */
  tmp_hal_status = ADC_Enable(hadc);

  if (tmp_hal_status == HAL_OK)
  {
    MODIFY_REG(hadc->Instance->CR, ADC_CR_CALINDEX, 0x9UL << ADC_CR_CALINDEX_Pos);
    MODIFY_REG(hadc->Instance->CALFACT2, 0x00FF0000UL, 0x00020000UL);
    SET_BIT(hadc->Instance->CALFACT, ADC_CALFACT_LATCH_COEF);

How to reproduce the bug

  1. Indicate the global behavior of your application project
    • This particular part of the code is responsible for acquiring a single ended signal at a frequency of 5Khz
  2. List the modules that you suspect to be the cause of the problem
    • the responsible driver is implemented in stm32u5xx_hal_adc_ex.c::HAL_ADCEx_Calibration_Start
  3. Describe the use case that generates the problem
    • See 1.
  4. How we can reproduce the problem
    • Setup the ADC and all related clock settings for acquiring a single ended channel
    • (optional) Setup the ADC trigger source as a timer
    • Start calibration procedure via HAL_ADCEx_Calibration_Start
    • The system either hangs, crashes, or if not, the ADC will always result in a wrong DR value (0 in my case) after starting the acquisition process (or the timer as in this case)

Additional context

Following the advice contained in the RM (i.e. performing 9 read accesses to CALFACT2), I managed to solve the issue by adding the following lines in _HAL_ADCEx_CalibrationStart (from line 213 onwards):

MODIFY_REG(hadc->Instance->CR, ADC_CR_CALINDEX, 0x9UL << ADC_CR_CALINDEX_Pos);
for(uint8_t idx = 0; idx <9; idx ++)
{
 volatile __unused uint32_t foobar= READ_REG(hadc->Instance->CALFACT2);
}
MODIFY_REG(hadc->Instance->CALFACT2, 0x00FF0000UL, 0x00020000UL);
for(uint8_t idx = 0; idx <9; idx ++)
{
 volatile __unused uint32_t foobar= READ_REG(hadc->Instance->CALFACT2);
}
SET_BIT(hadc->Instance->CALFACT, ADC_CALFACT_LATCH_COEF);

I tried removing one of the loops as just a single one should suffice but that didn't solve the problem.

Notes:

ARIOSTM commented 1 year ago

Hello DonBrus

Thank you for your contribution. I couldn't reproduce the issue on my side. Would it be possible to share a project that generates the problem ?

Best regards

DonBrus commented 1 year ago

Hello DonBrus

Thank you for your contribution. I couldn't reproduce the issue on my side. Would it be possible to share a project that generates the problem ?

Best regards

Hi @ARIOSTM, unfortunately the code is proprietary and I cannot show it here. I'll try to recreate a minimal example ASAP, although the basic steps to recreate the current situation would be:

The device will then either hang or have an incorrect operation when sampling from ADC1. I've tested this code on multiple devices and the issue arises consistently; adding the fix I've posted in the OP mitigates it. Again I'll try to reproduce the issue with a simple makefile and see if I can provide more details on a MWE

DonBrus commented 1 year ago

@ARIOSTM I've published a basic makefile project as its own repo (to avoid bundling the whole cube library in a .zip). Just make all from root directory; I've used linux to build so I'm not sure it also works on windows out of the box. A couple of notes:

As mentioned in the OP, if you launch this program without any modification to the library it will crash the whole system; if you add my modification inside the calibration procedure it'll work. Let me know if you need any further help in replicating this issue

ARIOSTM commented 1 year ago

Hello, Thank you these information. I downloaded your project and compiled it.

I will now test it and come back to you as soon as possible with updates.

ARIOSTM commented 1 year ago

Hello DonBrus

I reproduced the issue thanks to your program. It appears to be dependent on the clock configuration, the compiler and the optimization option selected.

The 9 read accesses to CALFACT2 mentioned in the Reference Manual are referring to the different calibration factor stored in the ADC_CALFACT2 register. They can be accessed by changing the ADC_CR_CALINDEX[3:0] value and therefore, to read all calibration factor, 9 accesses to ADC_CALFACT2 are needed.

However, you were right adding some kind of delay/loop between each access to the ADC register solve the issue.

Thanks again for reporting this issue and for the information you provided. We are still investigating to find the root cause. We will provide a fix in a future release.

In the meantime, I suggest to use a Data Memory Barrier (DMB) as a workaround. By inserting it between the access to ADC_CR_CALINDEX and ADC_CALFACT2 and again before the access to ADC_CALFACT.

MODIFY_REG(hadc->Instance->CR, ADC_CR_CALINDEX, 0x9UL << ADC_CR_CALINDEX_Pos);
__DMB();
MODIFY_REG(hadc->Instance->CALFACT2, 0x00FF0000UL, 0x00020000UL);
__DMB();
SET_BIT(hadc->Instance->CALFACT, ADC_CALFACT_LATCH_COEF);

Please let me know if this work for you or if you have any questions.

Best Regards,

ST Internal Reference: 160779

RJMSTM commented 1 year ago

ST Internal Reference: 160779

RJMSTM commented 1 year ago

Hello,

I hope you are fine. The issue you reported has been fixed in the frame of version v1.4.0 of the STM32CubeU5 published recently on GitHub. Thank you again for having reported.