STM32H7 ADC inaccuracy
Hi,
I have noticed the machine module (machine.ADC class) returns a raw ADC value that is consistently off by about 1000 counts too high. I have noticed this inaccuracy on the Nucleo_H743ZI2 and as well as my own custom board. I can definitely rule out PIN input impedance as the culprit because I am using voltage followers. This seems like an offset issue, but going through the adc.c, it looks like no offset was set:
https://github.com/micropython/micropython/blob/0fd0eb00aa5b9d311046d48d73a8cfabb30d7dd6/ports/stm32/adc.c#L346
Please advise.
STM32H5 machine.ADC core temperature sensor not working
Description
While testing with an STM32H5 Nucleo board I noticed the internal ADC channels are not working as expected. External (I/O) pins return expected values. Internal ADC input are not reporting reasonable values.
I understand the STM32H5 port is under work and some features not working. I hope my input helps to fix a problem. Let me know if I can help with more testing.
Problem description:
- ADC(ADC.CORE_TEMP) returns incorrect values.
>>> from machine import ADC
>>> coretemp=ADC(ADC.CORE_TEMP)
Expected result:
- Values should lie around 30°C calibration point, or slightly higher.
>>> from machine import mem16
>>> mem16[0x08FFF814]
760
>>> mem16[0x08FFF818]
1014
Observed result:
- ADC value incorrect, indicates wrong core temperature
- Value is far below 30°C calibration point
>>> coretemp.read_u16() >> 4
173
See detailed log below.
The issue could be in machine_adc.c, adc_config_channel(). ADC object shows channel is an encoded number containing additional information besides the ADC channel number. For ADC.CORE_TEMP channel is 0xc3210000. Using this channel number value leads to incorrect loading of the SMPR register.
Note: The same kind of problem will be present for ADC.CORE_VREF, ADC.CORE_VBAT.
>>> coretemp=ADC(ADC.CORE_TEMP)
>>> coretemp
<ADC1 channel=3273719808>
>>> hex(3273719808)
'0xc3210000'
For G4 cores the channel number is extracted via __LL_ADC_CHANNEL_TO_DECIMAL_NB. Maybe this is required for H5 cores as well.
STATIC void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t sample_time) {
[removed]
#if defined(STM32G4)
channel = __LL_ADC_CHANNEL_TO_DECIMAL_NB(channel);
adc->DIFSEL &= ~(1 << channel); // Set channel to Single-ended.
#endif
adc->SQR1 = (channel & 0x1f) << ADC_SQR1_SQ1_Pos | (1 - 1) << ADC_SQR1_L_Pos;
__IO uint32_t *smpr;
if (channel <= 9) {
smpr = &adc->SMPR1;
} else {
smpr = &adc->SMPR2;
channel -= 10;
}
*smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time
#endif
}
Version Information
% git lg1
* 7d66ae603 - (9 days ago) esp32/machine_timer: Switch from legacy driver to timer HAL. - Damien George (HEAD -> master, origin/master, origin/HEAD)
* 671b35cea - (2 weeks ago) py/builtinimport: Fix built-in imports when external import is disabled. - Jim Mussared
* 606ec9bfb - (9 weeks ago) py/compile: Fix async for's stack handling of iterator expression. - Damien George
MicroPython v1.20.0-282-g671b35cea-dirty on 2023-07-14; STM32H573I-DK with STM32H573IIK3Q
Type "help()" for more information.
Log
>>> from machine import ADC
### Working external inputs, PF12 taken as example
>>> adcext=ADC(machine.Pin('F12'))
>>> adcext
<ADC1 channel=6>
# Tied to 3.3 V
>>> adcext.read_u16()
65471
>>> adcext.read_u16()
65519
>>> adcext.read_u16()
65503
>>> adcext.read_u16()
65519
# Tied to GND
>>> adcext.read_u16()
48
>>> adcext.read_u16()
0
>>> adcext.read_u16()
16
### Test with core temperature
>>> coretemp=ADC(ADC.CORE_TEMP)
>>> coretemp
<ADC1 channel=3273719808>
>>> hex(3273719808)
'0xc3210000'
# Check low (30°C) and high (130°C) calibration points
>>> from machine import mem16
>>> mem16[0x08FFF814]
760
>>> mem16[0x08FFF818]
1014
# Read sensor, should be between two calibration points, actually close to 30° value
# Right align 12 bit ADC value
>>> coretemp.read_u16() >> 4
173
>>> coretemp.read_u16() >> 4
176
>>> coretemp.read_u16() >> 4
178
>>> coretemp.read_u16() >> 4
# Computes to a core temperature of -199 °C...
>>> (178-760)/((1014-760)/(130-30))+30
-199.1339