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"
This commit is contained in:
raiden00pl 2021-06-09 08:41:18 +02:00 committed by Alan Carvalho de Assis
parent 7c47f32a19
commit d76f8ea04a
2 changed files with 130 additions and 24 deletions

View File

@ -10902,4 +10902,19 @@ config STM32_FOC_USE_ADC4
select STM32_ADC4 select STM32_ADC4
select STM32_ADC3_JEXTSEL 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 endif #STM32_FOC

View File

@ -377,6 +377,14 @@
# endif # endif
#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: /* Validate ADC configuration:
* 1. ADC must be supported by chip, * 1. ADC must be supported by chip,
* 2. ADC support for injected channels must be enabled, * 2. ADC support for injected channels must be enabled,
@ -393,7 +401,7 @@
# if CONFIG_STM32_ADC1_ANIOC_TRIGGER != 1 # if CONFIG_STM32_ADC1_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC1_ANIOC_TRIGGER must be 1 # error CONFIG_STM32_ADC1_ANIOC_TRIGGER must be 1
# endif # 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 # error Invalid configuration for ADC1 injected channles
# endif # endif
#endif #endif
@ -407,7 +415,7 @@
# if CONFIG_STM32_ADC2_ANIOC_TRIGGER != 1 # if CONFIG_STM32_ADC2_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC2_ANIOC_TRIGGER must be 1 # error CONFIG_STM32_ADC2_ANIOC_TRIGGER must be 1
# endif # 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 # error Invalid configuration for ADC2 injected channles
# endif # endif
#endif #endif
@ -421,7 +429,7 @@
# if CONFIG_STM32_ADC3_ANIOC_TRIGGER != 1 # if CONFIG_STM32_ADC3_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC3_ANIOC_TRIGGER must be 1 # error CONFIG_STM32_ADC3_ANIOC_TRIGGER must be 1
# endif # 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 # error Invalid configuration for ADC3 injected channles
# endif # endif
#endif #endif
@ -435,11 +443,19 @@
# if CONFIG_STM32_ADC4_ANIOC_TRIGGER != 1 # if CONFIG_STM32_ADC4_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC4_ANIOC_TRIGGER must be 1 # error CONFIG_STM32_ADC4_ANIOC_TRIGGER must be 1
# endif # 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 # error Invalid configuration for ADC4 injected channles
# endif # endif
#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 */ /* Combine JEXTSEL with JEXTEN default */
#ifdef CONFIG_STM32_FOC_FOC0 #ifdef CONFIG_STM32_FOC_FOC0
@ -767,6 +783,8 @@ static int stm32_foc_worker_handler(FAR struct foc_dev_s *dev);
/* Helpers */ /* 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_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_pwm_cfg(FAR struct foc_dev_s *dev, uint32_t freq);
static int stm32_foc_adc_cfg(FAR struct foc_dev_s *dev); 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) 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_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; int i = 0;
DEBUGASSERT(dev); DEBUGASSERT(dev);
DEBUGASSERT(priv); DEBUGASSERT(priv);
DEBUGASSERT(adc);
if (priv->data.adcint_cntr < CAL_SAMPLES) if (priv->data.adcint_cntr < CAL_SAMPLES)
{ {
/* Get raw currents */ /* Get raw current samples */
for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1) stm32_foc_curr_get(dev, priv->data.curr_raw, CONFIG_MOTOR_FOC_SHUNTS);
{
priv->data.curr_raw[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, i);
}
/* Get sum */ /* 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_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_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev); FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev);
int i = 0;
int ret = OK; int ret = OK;
DEBUGASSERT(dev); 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) if (priv->data.adcint_cntr % priv->data.notifier_div == 0)
{ {
for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1) /* Get raw current samples */
{
/* 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.
*/
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 */ /* 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; 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 * 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 pwm_inst = 0;
uint8_t adc_inst = 0; uint8_t adc_inst = 0;
uint32_t pwmfzbit = 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 != NULL);
DEBUGASSERT(board->ops != 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->pins != NULL);
DEBUGASSERT(adc_cfg->chan != 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 */ /* 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; 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 */ /* 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, foc_dev->adc_dev = stm32_adcinitialize(adc_inst,
adc_cfg->chan, adc_cfg->chan,
adc_cfg->nchan); adc_cfg->nchan);
#endif
if (foc_dev->adc_dev == NULL) if (foc_dev->adc_dev == NULL)
{ {
mtrerr("Failed to get ADC%d interface\n", adc_cfg->intf); mtrerr("Failed to get ADC%d interface\n", adc_cfg->intf);