cancel
Showing results for 
Search instead for 
Did you mean: 

Spurious SAR trigger by Disabled timer

iumez
Associate II
Posted on March 11, 2015 at 14:48

I'm setting up a process where a regular group of SAR conversions will be triggered by a timer. The SAR is configured to use DMA to transfer that data into a buffer. The buffer memory is actually organized into two buffers with the DMA half transfer and transfer complete interrupts used to signal main code which is currently the ''read'' buffer.

I've set up the SAR and verified that the conversions and DMA work correctly when software triggered. However, when I started testing automatic triggering from a timer, I ran into some strange behavior.

1. The first time, I accidentally specified the wrong trigger source (timer 19 TRGO instead of timer 3). My initialization code enables the timer 3 peripheral clock. I then set up the SAR and DMA, then configure timer 3 to trigger the SAR. In my SAR configuration, I noticed that as soon as I executed the line of code to write the trigger source, my buffer filled up with samples, with values that I'd expect, indicating that the regular group SAR conversions were occuring and as configured, DMA was transferring them to the buffer.

As a first step, I added code in the DMA ISR for that channel to toggle a pin to try and get an idea of what was going on. An LED validates it's running. I need to scope it and see if I can correlate the triggering to my timer config.

2. I correct the mistake (change configuring the SAR trigger to timer 3), and get to the timer 3 configuration. At the start of timer 3 configuration, I set the UDIS bit to guard against spurious triggers of the SAR while I make changes. However, when I use the CMSIS function to write the prescaler immediately (which generates an update event), I notice the same thing behavior - SAR conversions and DMA start running. However, according to the scope, the DMA ISR isn't running.

If I let the code finish the timer 3 configuration and enable timer 3, I'm then able to hit the DMA ISR.

3. As a temporary work around, I decided to try configuring the SAR trigger *after* timer 3 is up and running. Using the timer 3 ISR. Since the ISR doesn't trigger until the timer is actually enabled, I can use it to enable external triggering on the SAR and configure the trigger source. This time, the SAR conversions are never triggered(almost), even though the ADC registers all have the correct values. Why almost? Well, if I set a break point, and start poking around the registers in the debugger, at after some random interval, I'll notice the ADC status register value change. And if I go and take a look at the buffer, I'll see that conversions and DMA transfers are happening as expected. If I  then run, I'll hit the DMA ISR.

As a first pass, has anyone encountered something like this and has ideas on what's going wrong. I've confirmed this behavior on multiple prototype boards so I'm relatively sure it's not just an isolated defect with my microcontroller.

#spurious-triggering
8 REPLIES 8
iumez
Associate II
Posted on March 11, 2015 at 18:27

So, I've got a GPIO pin that also toggles on the timer 3 update event. Monitoring that pin, I see the frequency is 1.5kHz (timer 3 is configured for 3kHz). This is expected, as it takes two update events for the output pin to go from high to low.

Based on the pin being toggled in the DMA ISR, I've back calculated that the ADC must be getting triggered at around 6.649 Mhz. It's about the same for both the timer 19 case where the ADC is misconfigured for a timer whose peripheral clock is never enabled and timer 3.

Note, even after timer 3 is enabled, the DMA is still toggled at the same rate. So, either the ADC trigger is picking up spurious signals or something else weird is going on. Unfortunately, that 6.649Mhz doesn't really correspond to anything I can think of.

Edit: I just realized that I have the SAR prescaler configured for a 6Mhz ADC clock. So there's no way the ADC is generating those DMA events. After discussing with a colleague, we realized that the DMA is behaving as if it's free running - like it's configured for a memory to memory transfer. So, it's time to really dig into the CMSIS initialization and see if something jumps out at me.

Edit: More evidence supporting the DMA being triggered by some other event. Enabled the SAR ISR and confirmed that it's being triggered correctly (pin is toggled from the ISR with correct timing).
iumez
Associate II
Posted on March 11, 2015 at 20:52

To simplify things, I configured the SAR for 1 channel and the DMA for a buffer length of 1.

The DMA ISR is configured to set a pin at the start of the ISR, clear the ISR flags, and then clear the pin. Looking at the scope, the ISR runs for 1.332us and is triggered ~1us after it finishes. At 48Mhz, 1us is 48 instructions. As the ISR is barely doing anything (and takes just slightly longer), it would seem that the DMA ISR is being triggered almost as soon as it is done.

Since I'm pretty confident I'm doing everything right, I have to conclude that the stm32f373 is defective in the way it handles DMA. Anecdotal to this, I just learned that STM is revising the silicon for this very chip.
Posted on March 11, 2015 at 21:17

Since I'm pretty confident I'm doing everything right, I have to conclude that the stm32f373 is defective in the way it handles DMA. Anecdotal to this, I just learned that STM is revising the silicon for this very chip.

And is this defective behaviour described in the errata, because otherwise in 10-12 weeks you'll have silicon that also doesn't work as you'd hoped? And you'll have to tell your boss ST screwed it up again.

My bosses really hate these types of excuses for why stuff isn't working, so perhaps you can formulate a clean and concise example that deftly demonstrates the problem in a reproducible manner.

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 11, 2015 at 23:48

I'm not sure. The info on the silicon revision comes from an FAE, so I don't have official documentation that describes what's being changed.

At the moment, it doesn't matter what happens in 12-14 weeks. If there is this much wrong with the chip now, not to mention the numerous mistakes I've already discovered in the reference manual, I won't be using it 2 weeks from now.

I'll put together a short enough example to demonstrate this behavior and post either by end of day or tomorrow.

As far as bosses, mine have had this issue communicated to them, seen this behavior in person, and reviewed the source code (as well as other team members). While I can work around using DMA (via a more complex ISR) so we can proceed with other evaluation work on this prototype, I'm definitely not moving forward with this chip if I can't be confident this issue will be resolved (whether that is through official confirmation through published errata or figuring out what it that I either misunderstand from the RM or am just completely doing wrong).

iumez
Associate II
Posted on March 12, 2015 at 02:03

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 modified it to configure the ADC for external trigger, add the timer setup, and remove the LCD code and other stuff in the main while loop. That's all it takes to showcase this bug/defect/whatever. Set a breakpoint on the line indicated indicated in Timer_Init(). Take a look atRegularConvData_Tab. Depending on your startup code, this is either all zero or random values. Step through the line of code with the breakpoint (or step in and then execute the line of code which generates the update event to load the timer registers). You will see the values inRegularConvData_Tab change - indicating that DMA transfer has begun BEFORE you've even enabled the timer. BTW, I checked the errata document currently published for this family. It's dated March 2015 which suggests this covers the changes the FAE hinted at. None of the items listed match this issue.

/* Private define ------------------------------------------------------------*/
#define ADC1_DR_Address 0x4001244C
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
__IO uint16_t RegularConvData_Tab[4];
/* 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;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/* 
ADCCLK
= 
PCLK2
/4 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
/* GPIOB Periph clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
/* Configure ADC Channel9 as analog input */
GPIO_InitStructure.GPIO_Pin
= 
GPIO_Pin_1
;
GPIO_InitStructure.GPIO_Mode
= 
GPIO_Mode_AN
;
GPIO_InitStructure.GPIO_PuPd
= 
GPIO_PuPd_NOPULL
;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* DMA1 clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
/* DMA1 Channel1 Config */
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RegularConvData_Tab;
DMA_InitStructure.DMA_DIR
= 
DMA_DIR_PeripheralSRC
;
DMA_InitStructure.DMA_BufferSize
= 
4
;
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 = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 4;
ADC_Init(ADC1, &ADC_InitStructure);
// Enable external conversions on regular channels
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
/* Convert the ADC1 Channel 9 with 5 Cycles as sampling time */ 
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_55Cycles5);
/* Convert the ADC1 temperature sensor with 5 Cycles as sampling time */ 
ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor ,2, ADC_SampleTime_55Cycles5); 
/* Convert the ADC1 Vref with 5 Cycles as sampling time */ 
ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint , 3, ADC_SampleTime_55Cycles5); 
/* Convert the ADC1 Vbat with 5 Cycles as sampling time */ 
ADC_RegularChannelConfig(ADC1, ADC_Channel_Vbat ,4, ADC_SampleTime_239Cycles5); 
ADC_TempSensorVrefintCmd(ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE); 
/* ADC1 reset calibration register */ 
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
/* ADC1 calibration start */
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)); 
}
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;
// 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;
// ********** SET BREAKPOINT HERE ************* //
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);
}

Amel NASRI
ST Employee
Posted on March 12, 2015 at 11:26

Hi Iumez,

When using an external trigger, the continuous conversion mode has to be disabled:

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;

Then, there is no need for this call:

// via TRGO each time there's an update event. See reference manual sections 7 and 4.2.
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

This is used for timers synchronization. I don't think there is an issue on the chip... There will be soon a new revision of RM0313 fixing some documentation issues. So it will be helpful if you may share the errors you faced; it is possible that we are not aware of some of them. -Mayla-

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

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

I commented out that line, the issue still occurs.

If there is no issue with the chip, why do DMA transfers start before the timer is enabled on the indicated line?

In any case, as it turns out, if you comment out that line (placing the ADC in continuous mode), then only the first group of conversions actually occur. Conversions stop after the first group and don't get re-triggered. Don't worry, I will post a separate thread with minimal code to duplicate that defect as well.

Edit: Here's a thread that focuses on the trigger once defect: 

/fbbcaa51

iumez
Associate II
Posted on March 12, 2015 at 22:14

As pointed out by clive1 in another thread, I was incorrectly using the SCAN bit with externally triggered group conversions.

It appears it is more accurate to say that the SCAN bit is used to trigger a group (not place the ADC in a ''scan mode'') and that setting the external trigger bit places the ADC in a group ''mode'' where subsequent triggers by the source cause conversions.

Apparently, setting the SCAN bit while also enabling external trigger and DMA puts the ADC in an invalid state, resulting in the spurious DMA transfers.