AnsweredAssumed Answered

STM32 Externally triggered Regular group ADC conversions do not retrigger

Question asked by iumez on Mar 12, 2015
Latest reply on Mar 12, 2015 by iumez
Here's what appears to be a another defect in the STM32F373 (See ).

When you configure the SAR for externally triggered group conversions (the code example provided here is for regular group, but I've verified this also happens with injected groups), the group only gets triggered the first time. After that, conversions never run. If you examine the timer (trigger) and ADC registers, you'll see that they're all configured correctly, even though no conversion is occurring.

Here is a minimal amount of code to replicate the issue. Processor is an STM32F373CBT6. I started from the STM32F37x DSP_StdPeriph_Lib project at Project/StdPeriph_Examples/ADC/ADC_DMA. I removed the LCD code, modified the ADC for an externally triggered regular group and added the timer initialization. If you read the description of the linked defect, you'd see that the first DMA transfer actually occurs in the timer code before the timer is initialized.

Anyway, to see this particular defect, you can set a breakpoint on the while loop in main, and watch the MicroTemperature variable. As you can see, it never changes from the value after the first transfer.

For those who are unsure whether this observed behavior is enough to confirm the ADC is not getting triggered, here's what to try: Enable the ADC interrupt and set a breakpoint in the ISR. You'll see the breakpoint get hit once and only once.

/* Private variables ---------------------------------------------------------*/
__IO uint16_t MicroTemperature;
/* Private function prototypes -----------------------------------------------*/
static void ADC_Config(void);
static void Timer_Init(void);
/* Private functions ---------------------------------------------------------*/
  * @brief  Main program.
  * @param  None
  * @retval None
int main(void)
  /*!< At this stage the microcontroller clock setting is already configured,
       this is done through SystemInit() function which is called from startup
       file (startup_stm32f37x.s) before to branch to application main.
       To reconfigure the default setting of SystemInit() function, refer to
       system_stm32f37x.c file
  /* ADC1 channel9, TempSenso, Vrefint and Vbat with DMA configuration */
  /* Infinite loop */
  while (1);
  * @brief  ADC1 channel with DMA configuration
  * @param  None
  * @retval None
static void ADC_Config(void)
  ADC_InitTypeDef     ADC_InitStructure;
  DMA_InitTypeDef     DMA_InitStructure;
  /* ADCCLK = PCLK2/4 */
  /* DMA1 clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
  /* DMA1 Channel1 Config */
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&MicroTemperature;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = 1;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  // Enable the DMA transfer error, half transfer, and transfer complete interrupts
  /* DMA1 Channel1 enable */
  DMA_Cmd(DMA1_Channel1, ENABLE);
  /* ADC1 Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  /* ADC1 DeInit */ 
  /* Enable ADC_DMA */
  /* Initialize ADC structure */
  /* Configure the ADC1 in continuous mode */
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);
  // Enable external conversions on regular channels
  ADC_ExternalTrigConvCmd(ADC1, ENABLE);
  /* Convert the ADC1 temperature sensor  with 55.5 Cycles as sampling time */
  ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor ,1, ADC_SampleTime_55Cycles5); 
  /* Enable ADC1 */
void Timer_Init(void) {
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    // Enable peripheral clock for timer peripheral
    // Disable generation of update event while we configure the timer
    TIM_UpdateDisableConfig(TIM3, ENABLE);
    // The TIM3CLK is derived from the SystemCoreClock. So to get a input timer clock
    // rate of 6 MHz the Prescaler is computed below to determine the prescaler divisor.
    const uint32_t TIMER_CLOCK_HZ = 6000000U;
    // Want to trigger ADC at 3kHz
    const uint32_t FREQ = 3000;
    const uint16_t prescalerDivisor = (uint16_t) (SystemCoreClock / TIMER_CLOCK_HZ) - 1U; // parasoft-suppress CODSTA-CPP-82 "SystemCoreClock is defined by CMSIS as an unencapsulated global variable"
    // This period count and the prescaler divisor below determine the timer output frequency
    const uint32_t periodCount = TIMER_CLOCK_HZ / FREQ;
    // Time base configuration
    TIM_TimeBaseStructure.TIM_Period = periodCount - 1U;
    TIM_TimeBaseStructure.TIM_Prescaler = prescalerDivisor;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0U;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    // Configure Capture/Compare Mode Register (CCMR1) fields to produce a
    // free-running square wave on pin 16 (PA6) for monitoring with a scope.
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0U;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
    // Configure the timer trigger output as a master so it can trigger the ADC as its slave
    // via TRGO each time there's an update event. See reference manual sections 12.7 and 16.4.2.
    TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
    // Enable generation of update event
    TIM_UpdateDisableConfig(TIM3, DISABLE);
    // Enable timer counter
    TIM_Cmd(TIM3, ENABLE);