stm32/machine_adc: Fix and improve STM32H5 support.

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.
- 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()).

Reasoning for the second last point: 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().

Signed-off-by: Rene Straub <rene@see5.ch>
This commit is contained in:
Rene Straub 2023-08-02 09:13:16 +02:00 committed by Damien George
parent 64d24fccd6
commit 72ef2e6291
1 changed files with 104 additions and 26 deletions

View File

@ -48,7 +48,9 @@
#elif defined(STM32G4)
#define ADC_STAB_DELAY_US (20)
#elif defined(STM32H5)
#define ADC_STAB_DELAY_US (1) // TODO: Check if this is enough
// Stabilization delay = 1 conversion cycle
// ADC clk = PDIV / 16 = 250 MHz / 16 = 15.625 MHz -> 64 ns -> select 1 us
#define ADC_STAB_DELAY_US (1)
#elif defined(STM32L4)
#define ADC_STAB_DELAY_US (10)
#elif defined(STM32WB)
@ -61,9 +63,14 @@
#elif defined(STM32F4) || defined(STM32F7)
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_15CYCLES
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_480CYCLES
#elif defined(STM32G4) || defined(STM32H5)
#elif defined(STM32G4)
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5
#elif defined(STM32H5)
// Worst case sampling time: slow channel, 12 bits, 680 ohms -> 165 ns
// ADC clk = PDIV / 16 = 250 MHz / 16 = 15.625 MHz -> 64 ns -> 2.57 cycles -> select 6.5 cycles
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_6CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5
#elif defined(STM32H7)
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_8CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_387CYCLES_5
@ -81,8 +88,65 @@
// Timeout for waiting for end-of-conversion
#define ADC_EOC_TIMEOUT_MS (10)
// This is a synthesised channel representing the maximum ADC reading (useful to scale other channels)
#define ADC_CHANNEL_VREF (0xffff)
// Channel IDs for machine.ADC object
typedef enum _machine_adc_internal_ch_t {
// Regular external ADC inputs (0..19)
MACHINE_ADC_EXT_CH_0 = 0,
MACHINE_ADC_EXT_CH_19 = 19,
// Internal ADC channels (256..)
MACHINE_ADC_INT_CH_VREFINT = 256,
MACHINE_ADC_INT_CH_TEMPSENSOR,
#if defined(ADC_CHANNEL_VBAT)
MACHINE_ADC_INT_CH_VBAT,
#endif
#if defined(ADC_CHANNEL_VDDCORE)
MACHINE_ADC_INT_CH_VDDCORE,
#endif
// This is a synthesised channel representing the maximum ADC reading (useful to scale other channels)
MACHINE_ADC_CH_VREF = 0xffff // 0xffff for backward compatibility
} machine_adc_internal_ch_t;
// Convert machine_adc_internal_ch_t value to STM32 library ADC channel literal.
// This function is required as literals are uint32_t types that don't map with MP_ROM_INT (31 bit signed).
STATIC uint32_t adc_ll_channel(uint32_t channel_id) {
uint32_t adc_ll_ch;
switch (channel_id) {
// external channels map 1:1
case MACHINE_ADC_EXT_CH_0 ... MACHINE_ADC_EXT_CH_19:
adc_ll_ch = channel_id;
break;
// internal channels are converted to STM32 ADC defines
case MACHINE_ADC_INT_CH_VREFINT:
adc_ll_ch = ADC_CHANNEL_VREFINT;
break;
case MACHINE_ADC_INT_CH_TEMPSENSOR:
#if defined(STM32G4)
adc_ll_ch = ADC_CHANNEL_TEMPSENSOR_ADC1;
#else
adc_ll_ch = ADC_CHANNEL_TEMPSENSOR;
#endif
break;
#if defined(ADC_CHANNEL_VBAT)
case MACHINE_ADC_INT_CH_VBAT:
adc_ll_ch = ADC_CHANNEL_VBAT;
break;
#endif
#if defined(ADC_CHANNEL_VDDCORE)
case MACHINE_ADC_INT_CH_VDDCORE:
adc_ll_ch = ADC_CHANNEL_VDDCORE;
break;
#endif
// To save code memory for costly error handling, default to Vref for unknown channels
default:
adc_ll_ch = ADC_CHANNEL_VREFINT;
break;
};
return adc_ll_ch;
}
static inline void adc_stabilisation_delay_us(uint32_t us) {
mp_hal_delay_us(us + 1);
@ -150,9 +214,9 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
adc->CFGR2 = 2 << ADC_CFGR2_CKMODE_Pos; // PCLK/4 (synchronous clock mode)
#elif defined(STM32F4) || defined(STM32F7) || defined(STM32L4)
ADCx_COMMON->CCR = 0; // ADCPR=PCLK/2
#elif defined(STM32G4)
#elif defined(STM32G4) || defined(STM32H5)
ADC12_COMMON->CCR = 7 << ADC_CCR_PRESC_Pos; // PCLK/16 (asynchronous clock mode)
#elif defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
ADC12_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos;
#elif defined(STM32H7)
ADC12_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos;
@ -352,9 +416,16 @@ STATIC void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
#else
adc_common->CCR |= ADC_CCR_VBATEN;
#endif
#if defined(STM32H5)
} else if (channel == ADC_CHANNEL_VDDCORE) {
adc->OR |= ADC_OR_OP0; // Enable Vddcore channel on ADC2
#endif
}
#if defined(STM32G4) || defined(STM32H5)
// G4 and H5 use encoded literals for internal channels -> extract ADC channel for following code
if (__LL_ADC_IS_CHANNEL_INTERNAL(channel)) {
channel = __LL_ADC_CHANNEL_TO_DECIMAL_NB(channel);
}
#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;
@ -391,10 +462,13 @@ STATIC uint32_t adc_read_channel(ADC_TypeDef *adc) {
}
uint32_t adc_config_and_read_u16(ADC_TypeDef *adc, uint32_t channel, uint32_t sample_time) {
if (channel == ADC_CHANNEL_VREF) {
if (channel == MACHINE_ADC_CH_VREF) {
return 0xffff;
}
// Map internal channel_id to STM32 ADC driver value/literal.
channel = adc_ll_channel(channel);
// Select, configure and read the channel.
adc_config_channel(adc, channel, sample_time);
uint32_t raw = adc_read_channel(adc);
@ -421,7 +495,7 @@ const mp_obj_type_t machine_adc_type;
typedef struct _machine_adc_obj_t {
mp_obj_base_t base;
ADC_TypeDef *adc;
uint32_t channel;
uint32_t channel; // one of machine_adc_internal_ch_t
uint32_t sample_time;
} machine_adc_obj_t;
@ -452,20 +526,25 @@ STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, s
uint32_t sample_time = ADC_SAMPLETIME_DEFAULT;
ADC_TypeDef *adc;
if (mp_obj_is_int(source)) {
channel = mp_obj_get_int(source);
#if defined(STM32WL)
adc = ADC;
#elif defined(STM32H5)
// on STM32H5 vbat and vddcore channels are on ADC2
if (channel == MACHINE_ADC_INT_CH_VBAT || channel == MACHINE_ADC_INT_CH_VDDCORE) {
adc = ADC2;
} else {
adc = ADC1;
}
#else
adc = ADC1;
#endif
channel = mp_obj_get_int(source);
if (channel == ADC_CHANNEL_VREFINT
#if defined(STM32G4)
|| channel == ADC_CHANNEL_TEMPSENSOR_ADC1
#else
|| channel == ADC_CHANNEL_TEMPSENSOR
#endif
if (channel == MACHINE_ADC_INT_CH_VREFINT || channel == MACHINE_ADC_INT_CH_TEMPSENSOR
#if defined(ADC_CHANNEL_VBAT)
|| channel == ADC_CHANNEL_VBAT
|| channel == MACHINE_ADC_INT_CH_VBAT
#endif
#if defined(ADC_CHANNEL_VDDCORE)
|| channel == MACHINE_ADC_INT_CH_VDDCORE
#endif
) {
sample_time = ADC_SAMPLETIME_DEFAULT_INT;
@ -516,15 +595,14 @@ MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_u16_obj, machine_adc_read_u16);
STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&machine_adc_read_u16_obj) },
{ MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_INT(ADC_CHANNEL_VREF) },
{ MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_VREFINT) },
#if defined(STM32G4)
{ MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR_ADC1) },
#else
{ MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR) },
#endif
{ MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_INT(MACHINE_ADC_CH_VREF) },
{ MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(MACHINE_ADC_INT_CH_VREFINT) },
{ MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(MACHINE_ADC_INT_CH_TEMPSENSOR) },
#if defined(ADC_CHANNEL_VBAT)
{ MP_ROM_QSTR(MP_QSTR_CORE_VBAT), MP_ROM_INT(ADC_CHANNEL_VBAT) },
{ MP_ROM_QSTR(MP_QSTR_CORE_VBAT), MP_ROM_INT(MACHINE_ADC_INT_CH_VBAT) },
#endif
#if defined(ADC_CHANNEL_VDDCORE)
{ MP_ROM_QSTR(MP_QSTR_CORE_VDD), MP_ROM_INT(MACHINE_ADC_INT_CH_VDDCORE) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table);