2012-10-01 12:15 AM
Hi guys,
I'm trying to synchronise timers and wondering what is the best way to do this. I need 3 set time points for my project; A continuous interrupt at 5uS intervals, an interrupt excatly 2uS after the first, and then a further interrupt 1uS after the second. i.e 5uS, 7uS, 8uS, 10uS, 12uS, 13uS, 15uS, 17uS,18uS... and so on.. Obviously i could implement this with 1 timer with 1uS interrupt and some sort of counter, however some of the code executed in the first and last trigger cannot be limited to 1uS. It still could work, but i have started now developing the code with 3 timers. Currently i have set up 3 timers which i just enable and disable to start and stop them, but i think this is not correct and may be causing inaccuracies to my timing. I see in the reference manual that TIM9, and 12 have 'one-pulse mode' , should i be using these? Overview of my code: Timer3 interrupt should occur every 5uS, this enables TIM5 (2uS - 500khz) and triggers an ADC reading (triple simultaneous - 3 cycle sample time). Once TIM5 interrupt is called, TIM5 is disabled and TIM2 (1uS - 1Mhz) is enabled. Once TIM2 interrupt is called, TIM2 is disabled. This then repeats when TIM3 is triggered again (every 5uS) as it was never disabled. Code: // Timer3 (every 5uS) void TIM3_IRQHandler(void) { TIM_Cmd(TIM5, ENABLE); //ENABLE TIMER5 ADC_SoftwareStartConv(ADC1); // Start ADC conversion - will trigger DMA2 when complete GPIOE->BSRRL = GPIO_Pin_8; //set if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } } // INTERRUPT AFTER ADC READINGS void DMA2_IRQHANDLER(void) { GPIOE->BSRRH = GPIO_Pin_8; //reset GPIOE->BSRRL = GPIO_Pin_10; //set if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); // Reset Flag } } // Timer5 - 2uS void TIM5_IRQHandler(void) { TIM_Cmd(TIM2, ENABLE); //ENABLE DEADTIME TIMER TIM_Cmd(TIM5, DISABLE); //DISABLE current timer GPIOE->BSRRH = GPIO_Pin_10; //reset GPIOE->BSRRL = GPIO_Pin_12; //set if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM5, TIM_IT_Update); } } // Timer2 - 1uS void TIM2_IRQHandler(void) { TIM_Cmd(TIM2, DISABLE); //DISABLE current timer GPIOE->BSRRH = GPIO_Pin_12; //reset if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } I've hooked up a Saleae logic analyzer (dont have CRO with me atm) to pins PE.8, PE.10 and PE.12 but i'm not getting the expected output. The Saleae is sampling at 24MHz... PIN8 is high from anywhere between 5.2uS to 5.95uS. PIN10 comes on 41.7ns after PIN8 goes low, and stays on for 1.95-2.05uS - sometimes even exactly 2.0uS. PIN12 comes on at the the same time PIN10 goes low, and stays high for either exactly 1.1250uS or 1.0833uS, or about every fourth time two short pulses of 0.4-0.5uS with a ~0.7uS low inbetween. Whats happening!!?? Is it my Saleae giving me this weird result?2012-10-01 01:03 AM
My first advice would be: if you want better pin-high-low setting, use it as first line after interrupt triggers (first you set gpio, then you move to disable timers and init of something else). Also second line would be to enable the next timer, then you'd go and do other things - preferably outside interrupt. Next you need to ask yourself how fast is your Cortex, because some commands (like ADC init) take a bit longer to perform (I dont know for f4 version exactly since I'm on f2). Then you might want to also consider deducting the length of this commands from timers but it is dependant on various factors.
Pin8: In interrupt handler you are first initializing timers and ADC, which does take some time (if I remember right ADC init might take the difference you spot between 5.2 and 5.95) Pin10: that 41.7 ns comes from 1/24MHZ sampling of Saleae, so apparently that is fast enough to be simultaneous? Pin12 again in interrupt handler you use some command before pin12 reset, which does take some time and there might be additional difference.2012-10-01 01:44 AM
Hi crt, thanks for your reply.
My issue is not so much with the exact times the pins are high/low, its the fact that it is not consistent! I wouldn't care if it was always 5.3432uS, as long as i can be guaranteed it will be the same each cycle. Any thoughts on why the time lengths appear to be random?2012-10-01 01:59 AM
oh and its worth noting, setting pins high and low was only so i could check when the interrupts were triggering and measure the timing.
2012-10-01 06:56 AM
They appear to be random because you initialize something before you set pin to desired value. From my experience initialization of something is not necessary always the same length.
That is why my idea was to put gpios in first place and use only timers to start with - without ADC init, etc. Also imagine such scenario - you set timer to 5us, then because of cycle lengths last 1us timer just starts as ADC finishes conversion and triggers DMA interrupt, then right after you clear that flag the 1us timer gets interrupt, disables itself and sets pin to 0, goes back to ADC stack etc. Try to eliminate other variables to see how ''exact'' timers appear and also check cortex's clock as if it runs at lower frequency that just might not be enough to catch that short timings.2012-10-01 04:48 PM
Hi crt,
thanks again for your reply! I have fixed the main issue - it was because my code was not within the ''if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)'' if statements! It perplexes me why the interrupt would be called if the flag is not set, but anyway. This has fixed that problem. Another problem is that the DMA interrupt doesn't trigger each time I do a ADC conversion. It was only triggering 2 out of 4 times, but then I realised my DMA buffer size was still 6 instead of 3 (now only doing 3 conversions), and now it triggers 3/4 times. Bit confused why its not triggering each conversion!? - here is my DMA code. void DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCTripleConvertedValue; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = 3; 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_FIFOMode = DMA_FIFOMode_Enable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure); /* DMA2_Stream0 enable */ DMA_Cmd(DMA2_Stream0, ENABLE); } Here is the code for 1 of the ADCs (replicated for ADC1,2&3) /* ADC Common configuration -- ADC_CCR Register */ ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_RegSimult ; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1 ; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; ADC_CommonInit(&ADC_CommonInitStructure); /* ADC1 regular channel 6 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */ ADC1_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC1_InitStructure.ADC_ScanConvMode = ENABLE; ADC1_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC1_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC1_InitStructure.ADC_ExternalTrigConv = 0; ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC1_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC1_InitStructure); /* ADC1 regular channel6 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */ ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_3Cycles); //PC1 - DC BUS I have tried ScanConvMode as enable and disable with no difference. Also, I have increased all the delays by factor of 10 (i.e. 50uS, 20uS, 10uS), so the ADC has plenty of time to convert. The micro is clocked at 168mhz. Thanks for your help!2012-10-01 04:59 PM
It seems this guy (forum member Ronan) had a similar problem
https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2FSTM32Discovery%2FTriple%20ADC%20mode%20with%20DMA%20[STM32F4%20discovery]&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987D47BE638E0F¤tviews=632 In the thread he states ''To verify that there is an interrupt (DMA) every 100 ms, I use a Led. Every DMA interrupt, I make a flashing led. Normally the led must flash at 20 Hz (if there is a conversion every 100ms). But when I tested my program the led don't flash regulary and i don't see where does the problem ?'' And then later ''Hi Hal,I observe
signals
with
a scope so
not need to change
interrupt timer (every 100ms). I verified the differents interrupts : -> ADC interrupt : every 100 ms -> DMA interrupt : the cycle is 100 ms, 100ms, 200 ms, 200ms So the problem comes DMA Handler but I don't see where ? Maybe the FIFOMode or MemoryBurst ???'' and lastly ''I believe that I will not find this error !!'' :( Is this a common problem?2012-10-02 01:42 AM
Disabling FIFO on DMA seems to have fixed my problem