cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Externally triggered Regular group ADC conversions do not retrigger

iumez
Associate II
Posted on March 12, 2015 at 15:41

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
4 REPLIES 4
Posted on March 12, 2015 at 17:56

Replace

ADC_InitStructure.ADC_ScanConvMode = ENABLE;
..
// 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);

With

ADC_InitStructure.ADC_ScanConvMode = DISABLE; // Single Channel
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);

I don't have a F37x board, may be someone at ST can fix that?
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
iumez
Associate II
Posted on March 12, 2015 at 18:22

Are you saying that SCAN mode is not to be used for a regular group conversion? Because if so, how does the ADC know to convert a group? Here is what the reference manual says:

0690X00000605F5QAI.png

Making the change on the timer trigger configuration fixes the single-shot issue! Thanks clive1. Now I just need to figure out what was misunderstood about the timer configuration.

Posted on March 12, 2015 at 19:10

Making the change on the timer trigger configuration fixes the single-shot issue!

No problem, but I have stripped you of your IC Validation merit badge.

Are you saying that SCAN mode is not to be used for a regular group conversion? Because if so, how does the ADC know to convert a group?

You have a ''group'' with ONE member, I'd advise you not to scan it. If you have more than one channel, then yes scan them.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
iumez
Associate II
Posted on March 12, 2015 at 19:45

Sure - that's fine. This was a hold over from copy pasting code and deleting what wasn't necessary to generate a stripped down example that works.

But the SCAN bit is still. . . unclear. Reading that, I can see a couple interpretations:

1. Setting the SCAN bit will trigger a conversion of a group once. If I read the bit, it will read back 1, but if I ''set'' the bit again, it will trigger another conversion of that group, also once.

2. In order to convert a group, one must set the SCAN bit, then trigger conversions some other way.

You're correct to take away my IC Validation merit badge. I need to spend more time exhaustively experimenting with all possible meanings of the RM text before I declare the chip as defective.