cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F1x periodic external pulse counter

cameron
Associate II
Posted on October 17, 2012 at 01:49

Hi All,

I’m using an STM32F103x.

 

I need to count the number of external pulses from a hardware detector, and take a snap shot of the pulse count at a precise frequency of 100,000 kHz.

My current setup uses TIM5 CH2 configured to count the external pulses.

 

This counts the pulses ok.  I also use TIM4 as my sampling time base so every 10uS (update interrupt) I read TIM5->CCR2 and calculate the delta and copy it to external SRAM.

 

This consumes 20% of the CPU.

I would like to DMA the TIM5 CCR2 into external SRAM rather than doing it in the TIM4_IRQHandler to save CPU cycles and prevent the sampling experiencing jitter.

If the CCR2 could be reset upon reading it, that would be better than the cumulative value, but that is not as important as reducing the CPU load.

I have attempted to use TIM4 (Master) as the trigger of TIM5 (Slave) but I have not been able to synchronise TIM5 with TIM4 and still count the pulses on TI2. Is this possible?

Alternatively, is it possible to setup one 100,000kHz timer that also counts external pulses?

Thanks,

Cameron
5 REPLIES 5
Posted on October 17, 2012 at 02:53

Problem is that the CCRx registers don't count.

Basically you'd want one timer ticking with an update rate of 100 KHz pacing DMA on a second counting external pulses. Say TIM1, configured to trigger DMA1_Channel5, and reading the TIM5->CNT register into your SRAM array. The counts would accumulate, but I don't see a reasonable way to reset, so compute delta between subsequent entries. Use the HT/TC interrupts to ping-pong between a large enough buffer to decimate the interrupt loading. Sampling the CNT would look at bit like this

#include ''stm32f10x.h''
uint16_t Buffer[32];
void RCC_Configuration(void);
void GPIO_Configuration(void);
void DMA_Configuration(void);
int main(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* System Clocks Configuration */
RCC_Configuration();
/* GPIO Configuration */
GPIO_Configuration();
/* DMA Configuration */
DMA_Configuration();
/* Add your code to init TIM5 to count external pulses */
/* TIM1 DMA Transfer example */
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = (SystemCoreClock / 100000) - 1; // 100 KHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* TIM1 Update DMA Request enable */
TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
/* TIM1 counter enable */
TIM_Cmd(TIM1, ENABLE);
while (1)
{}
}
void RCC_Configuration(void)
{
/* TIM1 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* DMA clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
}
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* DMA1 Channel5 Config */
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM5->CNT;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 32;
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_Channel5, &DMA_InitStructure);
/* DMA1 Channel5 enable */
DMA_Cmd(DMA1_Channel5, ENABLE);
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
cameron
Associate II
Posted on October 17, 2012 at 08:03

Hi Clive,

Thanks very much for the fast response and the example.  I used it acheive the result I'm looking for.

I do have a follow on question.

I would also like to synchronise a memory to memory DMA transfer with the DMA transfer of the TIM5->CNT.  I propose to use TIM1 as a master and another timer as a slave with a period of 1 and then use the update event on the slave timer to perform the second DMA transfer.  Does this sound reasonable?

Regards,

Cameron
Posted on October 17, 2012 at 15:38

What kind of frequency is this secondary copy occurring, and how much data are you looking to copy.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
cameron
Associate II
Posted on October 17, 2012 at 23:57

The same frequency which is 100 kHz not 100,000 kHz as previously posted.

The data will be either 16 or 32 bit.

Posted on October 18, 2012 at 09:13

I would set up TIM5 to count from ETR pin (external clock mode 2) and TIM1 to count from the internal clock reloading at the 100kHz pace. I would set the update event of TIM1 as TRGO and in TIM5, set the slave controller to input this signal from TIM1, and then set some of the capture channels' input to TRC. I would then trigger the DMA from the capture event to transfer the respective capture register.

For the additional memory-to-memory DMA, a compare event from TIM1 could then be used.

JW