STM32 Externally triggered Regular group ADC conversions do not retrigger
Here's what appears to be a another defect in the STM32F373 (See
/a7981b0
/638d212
). 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 */
ADC_Config();
Timer_Init();
/* 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 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
/* DMA1 clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
/* DMA1 Channel1 Config */
DMA_DeInit(DMA1_Channel1);
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->CCR |= DMA_CCR_TEIE | DMA_CCR_HTIE | DMA_CCR_TCIE;
/* DMA1 Channel1 enable */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* ADC1 DeInit */
ADC_DeInit(ADC1);
/* Enable ADC_DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Initialize ADC structure */
ADC_StructInit(&ADC_InitStructure);
/* 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 5 Cycles as sampling time */
ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor ,1, ADC_SampleTime_55Cycles5);
ADC_TempSensorVrefintCmd(ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
}
void
Timer_Init(
void
) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// Enable peripheral clock for timer peripheral
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// 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 7 and 4.2.
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
// Enable generation of update event
TIM_UpdateDisableConfig(TIM3, DISABLE);
// Enable timer counter
TIM_Cmd(TIM3, ENABLE);
}
#adc #external-trigger #defect