diff options
Diffstat (limited to 'stmhal/adc.c')
-rw-r--r-- | stmhal/adc.c | 148 |
1 files changed, 115 insertions, 33 deletions
diff --git a/stmhal/adc.c b/stmhal/adc.c index bcf8956254..11259546b9 100644 --- a/stmhal/adc.c +++ b/stmhal/adc.c @@ -55,14 +55,34 @@ #define ADCx_CLK_ENABLE __ADC1_CLK_ENABLE #define ADC_NUM_CHANNELS (19) -#if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) +#if defined(MCU_SERIES_F4) + #define ADC_FIRST_GPIO_CHANNEL (0) #define ADC_LAST_GPIO_CHANNEL (15) +#define ADC_CAL_ADDRESS (0x1fff7a2a) +#define ADC_CAL1 ((uint16_t*)(ADC_CAL_ADDRESS + 2)) +#define ADC_CAL2 ((uint16_t*)(ADC_CAL_ADDRESS + 4)) + +#elif defined(MCU_SERIES_F7) + +#define ADC_FIRST_GPIO_CHANNEL (0) +#define ADC_LAST_GPIO_CHANNEL (15) +#define ADC_CAL_ADDRESS (0x1ff0f44a) +#define ADC_CAL1 ((uint16_t*)(ADC_CAL_ADDRESS + 2)) +#define ADC_CAL2 ((uint16_t*)(ADC_CAL_ADDRESS + 4)) + #elif defined(MCU_SERIES_L4) + #define ADC_FIRST_GPIO_CHANNEL (1) #define ADC_LAST_GPIO_CHANNEL (16) +#define ADC_CAL_ADDRESS (0x1fff75aa) +#define ADC_CAL1 ((uint16_t*)(ADC_CAL_ADDRESS - 2)) +#define ADC_CAL2 ((uint16_t*)(ADC_CAL_ADDRESS + 0x20)) + #else + #error Unsupported processor + #endif #if defined(STM32F405xx) || defined(STM32F415xx) || \ @@ -72,7 +92,8 @@ #define VBAT_DIV (2) #elif defined(STM32F427xx) || defined(STM32F429xx) || \ defined(STM32F437xx) || defined(STM32F439xx) || \ - defined(STM32F746xx) + defined(STM32F746xx) || defined(STM32F767xx) || \ + defined(STM32F769xx) #define VBAT_DIV (4) #elif defined(STM32L476xx) #define VBAT_DIV (3) @@ -84,6 +105,10 @@ #define CORE_TEMP_V25 (943) /* (0.76v/3.3v)*(2^ADC resoultion) */ #define CORE_TEMP_AVG_SLOPE (3) /* (2.5mv/3.3v)*(2^ADC resoultion) */ +// scale and calibration values for VBAT and VREF +#define ADC_SCALE (3.3f / 4095) +#define VREFIN_CAL ((uint16_t *)ADC_CAL_ADDRESS) + typedef struct _pyb_obj_adc_t { mp_obj_base_t base; mp_obj_t pin_name; @@ -91,6 +116,18 @@ typedef struct _pyb_obj_adc_t { ADC_HandleTypeDef handle; } pyb_obj_adc_t; +// convert user-facing channel number into internal channel number +static inline uint32_t adc_get_internal_channel(uint32_t channel) { + #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) + // on F4 and F7 MCUs we want channel 16 to always be the TEMPSENSOR + // (on some MCUs ADC_CHANNEL_TEMPSENSOR=16, on others it doesn't) + if (channel == 16) { + channel = ADC_CHANNEL_TEMPSENSOR; + } + #endif + return channel; +} + STATIC bool is_adcx_channel(int channel) { #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) return IS_ADC_CHANNEL(channel); @@ -162,14 +199,13 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { adcHandle->Init.DataAlign = ADC_DATAALIGN_RIGHT; adcHandle->Init.NbrOfConversion = 1; adcHandle->Init.DMAContinuousRequests = DISABLE; + adcHandle->Init.Resolution = ADC_RESOLUTION_12B; #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) - adcHandle->Init.Resolution = ADC_RESOLUTION12b; - adcHandle->Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2; + adcHandle->Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; adcHandle->Init.ScanConvMode = DISABLE; adcHandle->Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1; adcHandle->Init.EOCSelection = DISABLE; #elif defined(MCU_SERIES_L4) - adcHandle->Init.Resolution = ADC_RESOLUTION_12B; adcHandle->Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; adcHandle->Init.ScanConvMode = ADC_SCAN_DISABLE; adcHandle->Init.EOCSelection = ADC_EOC_SINGLE_CONV; @@ -239,7 +275,7 @@ STATIC void adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t /// \classmethod \constructor(pin) /// Create an ADC object associated with the given pin. /// This allows you to then read analog values on that pin. -STATIC mp_obj_t adc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check number of arguments mp_arg_check_num(n_args, n_kw, 1, 1, false); @@ -249,7 +285,7 @@ STATIC mp_obj_t adc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uin uint32_t channel; if (MP_OBJ_IS_INT(pin_obj)) { - channel = mp_obj_get_int(pin_obj); + channel = adc_get_internal_channel(mp_obj_get_int(pin_obj)); } else { const pin_obj_t *pin = pin_find(pin_obj); if ((pin->adc_num & PIN_ADC1) == 0) { @@ -426,28 +462,33 @@ typedef struct _pyb_adc_all_obj_t { ADC_HandleTypeDef handle; } pyb_adc_all_obj_t; -void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution) { +void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution, uint32_t en_mask) { switch (resolution) { - case 6: resolution = ADC_RESOLUTION6b; break; - case 8: resolution = ADC_RESOLUTION8b; break; - case 10: resolution = ADC_RESOLUTION10b; break; - case 12: resolution = ADC_RESOLUTION12b; break; + case 6: resolution = ADC_RESOLUTION_6B; break; + case 8: resolution = ADC_RESOLUTION_8B; break; + case 10: resolution = ADC_RESOLUTION_10B; break; + case 12: resolution = ADC_RESOLUTION_12B; break; default: nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "resolution %d not supported", resolution)); } for (uint32_t channel = ADC_FIRST_GPIO_CHANNEL; channel <= ADC_LAST_GPIO_CHANNEL; ++channel) { - // Channels 0-16 correspond to real pins. Configure the GPIO pin in - // ADC mode. - const pin_obj_t *pin = pin_adc1[channel]; - mp_hal_gpio_clock_enable(pin->gpio); - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.Pin = pin->pin_mask; - GPIO_InitStructure.Mode = GPIO_MODE_ANALOG; - GPIO_InitStructure.Pull = GPIO_NOPULL; - HAL_GPIO_Init(pin->gpio, &GPIO_InitStructure); + // only initialise those channels that are selected with the en_mask + if (en_mask & (1 << channel)) { + // Channels 0-16 correspond to real pins. Configure the GPIO pin in + // ADC mode. + const pin_obj_t *pin = pin_adc1[channel]; + if (pin) { + mp_hal_gpio_clock_enable(pin->gpio); + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Pin = pin->pin_mask; + GPIO_InitStructure.Mode = GPIO_MODE_ANALOG; + GPIO_InitStructure.Pull = GPIO_NOPULL; + HAL_GPIO_Init(pin->gpio, &GPIO_InitStructure); + } + } } adcx_clock_enable(); @@ -464,7 +505,7 @@ void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution) { adcHandle->Init.DMAContinuousRequests = DISABLE; adcHandle->Init.EOCSelection = DISABLE; #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) - adcHandle->Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2; + adcHandle->Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; adcHandle->Init.ScanConvMode = DISABLE; adcHandle->Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1; #elif defined(MCU_SERIES_L4) @@ -490,9 +531,9 @@ int adc_get_resolution(ADC_HandleTypeDef *adcHandle) { uint32_t res_reg = __HAL_ADC_GET_RESOLUTION(adcHandle); switch (res_reg) { - case ADC_RESOLUTION6b: return 6; - case ADC_RESOLUTION8b: return 8; - case ADC_RESOLUTION10b: return 10; + case ADC_RESOLUTION_6B: return 6; + case ADC_RESOLUTION_8B: return 8; + case ADC_RESOLUTION_10B: return 10; } return 12; } @@ -508,6 +549,19 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) { } #if MICROPY_PY_BUILTINS_FLOAT +// correction factor for reference value +STATIC volatile float adc_refcor = 1.0f; + +float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) { + int32_t raw_value = adc_config_and_read_channel(adcHandle, ADC_CHANNEL_TEMPSENSOR); + + // constants assume 12-bit resolution so we scale the raw value to 12-bits + raw_value <<= (12 - adc_get_resolution(adcHandle)); + + float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 80.0; + return (((float)raw_value * adc_refcor - *ADC_CAL1) / core_temp_avg_slope) + 30.0f; +} + float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) { uint32_t raw_value = adc_config_and_read_channel(adcHandle, ADC_CHANNEL_VBAT); @@ -515,8 +569,16 @@ float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) { // be 12-bits. raw_value <<= (12 - adc_get_resolution(adcHandle)); - // multiplier is 3.3/4095 - return raw_value * VBAT_DIV * 0.8058608058608059e-3f; + #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) + // ST docs say that (at least on STM32F42x and STM32F43x), VBATE must + // be disabled when TSVREFE is enabled for TEMPSENSOR and VREFINT + // conversions to work. VBATE is enabled by the above call to read + // the channel, and here we disable VBATE so a subsequent call for + // TEMPSENSOR or VREFINT works correctly. + ADC->CCR &= ~ADC_CCR_VBATE; + #endif + + return raw_value * VBAT_DIV * ADC_SCALE * adc_refcor; } float adc_read_core_vref(ADC_HandleTypeDef *adcHandle) { @@ -526,29 +588,36 @@ float adc_read_core_vref(ADC_HandleTypeDef *adcHandle) { // be 12-bits. raw_value <<= (12 - adc_get_resolution(adcHandle)); - // multiplier is 3.3/4095 - return raw_value * 0.8058608058608059e-3f; + // update the reference correction factor + adc_refcor = ((float)(*VREFIN_CAL)) / ((float)raw_value); + + return (*VREFIN_CAL) * ADC_SCALE; } #endif /******************************************************************************/ /* Micro Python bindings : adc_all object */ -STATIC mp_obj_t adc_all_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t adc_all_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check number of arguments - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num(n_args, n_kw, 1, 2, false); // make ADCAll object pyb_adc_all_obj_t *o = m_new_obj(pyb_adc_all_obj_t); o->base.type = &pyb_adc_all_type; - adc_init_all(o, mp_obj_get_int(args[0])); // args[0] is the resolution + mp_int_t res = mp_obj_get_int(args[0]); + uint32_t en_mask = 0xffffffff; + if (n_args > 1) { + en_mask = mp_obj_get_int(args[1]); + } + adc_init_all(o, res, en_mask); return o; } STATIC mp_obj_t adc_all_read_channel(mp_obj_t self_in, mp_obj_t channel) { pyb_adc_all_obj_t *self = self_in; - uint32_t chan = mp_obj_get_int(channel); + uint32_t chan = adc_get_internal_channel(mp_obj_get_int(channel)); uint32_t data = adc_config_and_read_channel(&self->handle, chan); return mp_obj_new_int(data); } @@ -556,8 +625,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(adc_all_read_channel_obj, adc_all_read_channel) STATIC mp_obj_t adc_all_read_core_temp(mp_obj_t self_in) { pyb_adc_all_obj_t *self = self_in; + #if MICROPY_PY_BUILTINS_FLOAT + float data = adc_read_core_temp_float(&self->handle); + return mp_obj_new_float(data); + #else int data = adc_read_core_temp(&self->handle); return mp_obj_new_int(data); + #endif } STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_temp_obj, adc_all_read_core_temp); @@ -575,6 +649,13 @@ STATIC mp_obj_t adc_all_read_core_vref(mp_obj_t self_in) { return mp_obj_new_float(data); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_vref_obj, adc_all_read_core_vref); + +STATIC mp_obj_t adc_all_read_vref(mp_obj_t self_in) { + pyb_adc_all_obj_t *self = self_in; + adc_read_core_vref(&self->handle); + return mp_obj_new_float(3.3 * adc_refcor); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_vref_obj, adc_all_read_vref); #endif STATIC const mp_map_elem_t adc_all_locals_dict_table[] = { @@ -583,6 +664,7 @@ STATIC const mp_map_elem_t adc_all_locals_dict_table[] = { #if MICROPY_PY_BUILTINS_FLOAT { MP_OBJ_NEW_QSTR(MP_QSTR_read_core_vbat), (mp_obj_t)&adc_all_read_core_vbat_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_read_core_vref), (mp_obj_t)&adc_all_read_core_vref_obj}, + { MP_OBJ_NEW_QSTR(MP_QSTR_read_vref), (mp_obj_t)&adc_all_read_vref_obj}, #endif }; |