← index #12048PR #12152
Likely Duplicate · high · value 0.734
QUERY · ISSUE

STM32H5 machine.ADC core temperature sensor not working

openby renestraubopened 2023-07-20updated 2023-10-23
bugport-stm32

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
CANDIDATE · PULL REQUEST

stm32/machine_adc: Add STM32H5 support.

mergedby renestraubopened 2023-08-03updated 2023-09-27
port-stm32

Description

Add STM32H5 ADC support to machine.ADC.

Changes are:

  • Run ADC on PCLK/16
  • Verify and optimize timings (ADC_STAB_DELAY_US, ADC_SAMPLETIME_DEFAULT)
  • Add support for STM32H5 VBAT and COREVDD channels on ADC2.
  • Replace ADC constants in machine_adc_locals_dict_table . Reasoning: The STM32 driver literals are uint32_t that don't work with MP_ROM_INT() which handles signed 31 bit integers only. Introduce enumerator machine_adc_internal_ch_t to define external channels (0..19), internal channels (256..) and the special channel VREF (0xffff). Values are converted to STM32 literals with adc_ll_channel() when required in adc_config_and_read_u16().
  • Convert STM32 literal to channel numbers in adc_config_channel with corresponding STM32 LL library functions (__LL_ADC_IS_CHANNEL_INTERNAL(), __LL_ADC_CHANNEL_TO_DECIMAL_NB())

Thanks to @yn386 for discussions.

Environment

  • NUCLEO-H563ZI
  • Using STM32H573I-DK board with minor modifications (input clock, on-chip flash)
  • First ADC Input (PF12) connected externally to GPIO PF14
MicroPython v1.20.0-283-g7d66ae603-dirty on 2023-08-01; STM32H573I-DK with STM32H573IIK3Q

Tests

from machine import ADC, Pin
from machine import mem16

io = Pin('F14', Pin.OUT_PP)     # Connect to PF12
io.low()

ts_cal_30 = mem16[0x08FFF814]
ts_cal_130 = mem16[0x08FFF818]
scale = (ts_cal_130 - ts_cal_30)/(130-30)
print(f"Calibration values: 30°C = {ts_cal_30}, 130°C = {ts_cal_130}")

adc_pf12 = ADC(Pin('F12'))
print(adc_pf12)
<ADC1 channel=6 sampletime=1>

adc_vref = ADC(ADC.VREF)
print(adc_vref)
<ADC1 channel=65535 sampletime=1>

adc_vrefint = ADC(ADC.CORE_VREF)
print(adc_vrefint)
<ADC1 channel=256 sampletime=6>

adc_temp = ADC(ADC.CORE_TEMP)
print(adc_temp)
<ADC1 channel=257 sampletime=6>

adc_vbat = ADC(ADC.CORE_VBAT)
print(adc_vbat)
<ADC2 channel=258 sampletime=6>

adc_corevdd = ADC(ADC.CORE_VDD)
print(adc_corevdd)
<ADC2 channel=259 sampletime=6>


# Expect: 3.30 V (fixed value)
adc_vref.read_u16() * 3.3 / 65535
3.3

# Expect: 1.21 V
adc_vrefint.read_u16() * 3.3 / 65535
1.210378

# Expect: 25 to 35 °C
val = adc_temp.read_u16() >> 4
((val - ts_cal_30) / scale)+30
31.5748

# Expect: 3.30V
adc_vbat.read_u16() * 3.3 / 65535 * 4
3.326638

# Expect: 1.30 to 1.40 V
adc_corevdd.read_u16() * 3.3 / 65535
1.360284

# Expect 0.0
io.low()
adc_pf12.read_u16() * 3.3 / 65535
0.002417029

# Expect 3.3
io.high()
adc_pf12.read_u16() * 3.3 / 65535
3.296777

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied