From d76f8ea04aaba3ce9096fb54e12086db732d5ce0 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Wed, 9 Jun 2021 08:41:18 +0200 Subject: [PATCH] arch/arm/src/stm32/stm32_foc.c: add workaround for an issue found in STM32G4 family From G4 erratas: "ADC channel 0 converted instead of the required ADC channel" --- arch/arm/src/stm32/Kconfig | 15 ++++ arch/arm/src/stm32/stm32_foc.c | 139 +++++++++++++++++++++++++++------ 2 files changed, 130 insertions(+), 24 deletions(-) diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index 070928a4b7..ad6a5783ce 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -10902,4 +10902,19 @@ config STM32_FOC_USE_ADC4 select STM32_ADC4 select STM32_ADC3_JEXTSEL +config STM32_FOC_G4_ADCCHAN0_WORKAROUND + bool "FOC G4 ADC channel 0 unwanted conversion workaround" + default n + ---help--- + Some STM32G4 family chips have an issue that causes unwanted ADC channel 0 + conversion when a regular conversion is interrupted by an injected conversion. + This FOC implementation uses injected conversion to sample phase currents + and allows user to use regular conversion as an auxiliary analog conversion. + In this case, there is a certain probability that regular conversion will be + interrupted by an injected conversion that will lead to an incorrect reading + of phase currents. + + This workaround inserts a dummy conversion at the beginning of the injected + sequence. For more details look at the chip errata documents. + endif #STM32_FOC diff --git a/arch/arm/src/stm32/stm32_foc.c b/arch/arm/src/stm32/stm32_foc.c index f7d116a12a..f6cf7c03a6 100644 --- a/arch/arm/src/stm32/stm32_foc.c +++ b/arch/arm/src/stm32/stm32_foc.c @@ -377,6 +377,14 @@ # endif #endif +/* The number of required injected channels */ + +#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND +# define FOC_ADC_INJ_CHAN_REQUIRED (CONFIG_MOTOR_FOC_SHUNTS + 1) +#else +# define FOC_ADC_INJ_CHAN_REQUIRED (CONFIG_MOTOR_FOC_SHUNTS) +#endif + /* Validate ADC configuration: * 1. ADC must be supported by chip, * 2. ADC support for injected channels must be enabled, @@ -393,7 +401,7 @@ # if CONFIG_STM32_ADC1_ANIOC_TRIGGER != 1 # error CONFIG_STM32_ADC1_ANIOC_TRIGGER must be 1 # endif -# if CONFIG_STM32_ADC1_INJECTED_CHAN != CONFIG_MOTOR_FOC_SHUNTS +# if CONFIG_STM32_ADC1_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED # error Invalid configuration for ADC1 injected channles # endif #endif @@ -407,7 +415,7 @@ # if CONFIG_STM32_ADC2_ANIOC_TRIGGER != 1 # error CONFIG_STM32_ADC2_ANIOC_TRIGGER must be 1 # endif -# if CONFIG_STM32_ADC2_INJECTED_CHAN != CONFIG_MOTOR_FOC_SHUNTS +# if CONFIG_STM32_ADC2_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED # error Invalid configuration for ADC2 injected channles # endif #endif @@ -421,7 +429,7 @@ # if CONFIG_STM32_ADC3_ANIOC_TRIGGER != 1 # error CONFIG_STM32_ADC3_ANIOC_TRIGGER must be 1 # endif -# if CONFIG_STM32_ADC3_INJECTED_CHAN != CONFIG_MOTOR_FOC_SHUNTS +# if CONFIG_STM32_ADC3_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED # error Invalid configuration for ADC3 injected channles # endif #endif @@ -435,11 +443,19 @@ # if CONFIG_STM32_ADC4_ANIOC_TRIGGER != 1 # error CONFIG_STM32_ADC4_ANIOC_TRIGGER must be 1 # endif -# if CONFIG_STM32_ADC4_INJECTED_CHAN != CONFIG_MOTOR_FOC_SHUNTS +# if CONFIG_STM32_ADC4_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED # error Invalid configuration for ADC4 injected channles # endif #endif +/* Max 3 shunts supported if STM32G4 ADC CHAN0 workaround enabled */ + +#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND +# if CONFIG_MOTOR_FOC_SHUNTS > 3 +# error +# endif +#endif + /* Combine JEXTSEL with JEXTEN default */ #ifdef CONFIG_STM32_FOC_FOC0 @@ -767,6 +783,8 @@ static int stm32_foc_worker_handler(FAR struct foc_dev_s *dev); /* Helpers */ +static void stm32_foc_curr_get(FAR struct foc_dev_s *dev, + FAR int16_t *curr, int shunts); static int stm32_foc_notifier_cfg(FAR struct foc_dev_s *dev, uint32_t freq); static int stm32_foc_pwm_cfg(FAR struct foc_dev_s *dev, uint32_t freq); static int stm32_foc_adc_cfg(FAR struct foc_dev_s *dev); @@ -1521,21 +1539,16 @@ static int stm32_foc_ioctl(FAR struct foc_dev_s *dev, int cmd, static int stm32_foc_adc_calibration_handler(FAR struct foc_dev_s *dev) { FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev); - FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev); int i = 0; DEBUGASSERT(dev); DEBUGASSERT(priv); - DEBUGASSERT(adc); if (priv->data.adcint_cntr < CAL_SAMPLES) { - /* Get raw currents */ + /* Get raw current samples */ - for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1) - { - priv->data.curr_raw[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, i); - } + stm32_foc_curr_get(dev, priv->data.curr_raw, CONFIG_MOTOR_FOC_SHUNTS); /* Get sum */ @@ -1677,7 +1690,6 @@ static int stm32_foc_worker_handler(FAR struct foc_dev_s *dev) FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev); FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev); FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev); - int i = 0; int ret = OK; DEBUGASSERT(dev); @@ -1689,16 +1701,9 @@ static int stm32_foc_worker_handler(FAR struct foc_dev_s *dev) if (priv->data.adcint_cntr % priv->data.notifier_div == 0) { - for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1) - { - /* Get raw current samples. - * We have ADC offset enabled for injected channels so this - * gives us signed values. - * NOTE: ADC value is 11 bits + sign. - */ + /* Get raw current samples */ - priv->data.curr_raw[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, i); - } + stm32_foc_curr_get(dev, priv->data.curr_raw, CONFIG_MOTOR_FOC_SHUNTS); /* Get phase currents */ @@ -1922,6 +1927,44 @@ static void stm32_foc_hw_config_get(FAR struct foc_dev_s *dev) dev->info.hw_cfg.pwm_max = board->data->duty_max; } +/**************************************************************************** + * Name: stm32_foc_curr_get + * + * Description: + * Get current samples from ADC + * + ****************************************************************************/ + +static void stm32_foc_curr_get(FAR struct foc_dev_s *dev, + FAR int16_t *curr, int shunts) +{ + FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev); + FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev); + int i = 0; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + DEBUGASSERT(adc); + DEBUGASSERT(curr); + + for (i = 0; i < shunts; i += 1) + { + /* Get raw current samples. + * We have ADC offset enabled for injected channels so this + * gives us signed values. + * NOTE: ADC value is 11 bits + sign. + */ + +#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND + /* Ignore first channel */ + + curr[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, (i + 1)); +#else + curr[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, i); +#endif + } +} + /**************************************************************************** * Name: stm32_foc_notifier_cfg * @@ -2112,7 +2155,11 @@ stm32_foc_initialize(int inst, FAR struct stm32_foc_board_s *board) uint8_t pwm_inst = 0; uint8_t adc_inst = 0; uint32_t pwmfzbit = 0; - int j = 0; + int i = 0; +#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND + FAR uint8_t *adc_chan = NULL; + uint8_t adc_nchan = 0; +#endif DEBUGASSERT(board != NULL); DEBUGASSERT(board->ops != NULL); @@ -2237,9 +2284,9 @@ stm32_foc_initialize(int inst, FAR struct stm32_foc_board_s *board) DEBUGASSERT(adc_cfg->pins != NULL); DEBUGASSERT(adc_cfg->chan != NULL); - for (j = 0; j < adc_cfg->nchan; j++) + for (i = 0; i < adc_cfg->nchan; i++) { - stm32_configgpio(adc_cfg->pins[j]); + stm32_configgpio(adc_cfg->pins[i]); } /* Make sure that we are using the appropriate ADC interface */ @@ -2252,11 +2299,55 @@ stm32_foc_initialize(int inst, FAR struct stm32_foc_board_s *board) goto errout; } + /* STM32G4 ADC channel 0 unwanted conversion workaround */ + +#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND + /* Add one dummy channel to conversion */ + + adc_nchan = (adc_cfg->nchan + 1); + + /* Allocate memory for the extended list of channels */ + + adc_chan = zalloc(adc_nchan); + if (adc_chan == NULL) + { + goto errout; + } + + /* Copy regular channels first */ + + for (i = 0; i < adc_cfg->regch; i += 1) + { + adc_chan[i] = adc_cfg->chan[i]; + } + + /* Add dummy channel at the beginning of injected channels */ + + adc_chan[adc_cfg->regch] = 0; + + /* Copy injected channels */ + + for (i = (adc_cfg->regch + 1); i < adc_nchan; i += 1) + { + adc_chan[i] = adc_cfg->chan[i - 1]; + } + +#endif /* CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND */ + /* Get the ADC interface */ +#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND + foc_dev->adc_dev = stm32_adcinitialize(adc_inst, + adc_chan, + adc_nchan); + + free(adc_chan); +#else foc_dev->adc_dev = stm32_adcinitialize(adc_inst, adc_cfg->chan, adc_cfg->nchan); +#endif + if (foc_dev->adc_dev == NULL) { mtrerr("Failed to get ADC%d interface\n", adc_cfg->intf);