cancel
Showing results for 
Search instead for 
Did you mean: 

How to read 4 fast pulses

P S
Associate II
Posted on March 16, 2018 at 16:55

Hi All,

I am just getting started with stm32. I am using a stm32f031 mcu and am trying to read an input signal from a flight controller. The protocol used puts out a series of 4 pulses that vary from 1us to 2.6us in width with a 1us in between ( proshot protocol). I have used input capture with a timer for slower speed pulses but I have no luck with reading such short pulse durations. I am looking for clues as to how to read this? It seems input capture with interrupts is just too slow? Is DMA the solution here? can DMA read the 4 pulses one after another like this and store in an array? I am really new at this and I have spent a lot of time trying things than find out it could never work so this time I am asking first. Thanks for your help!

1 ACCEPTED SOLUTION

Accepted Solutions
Posted on March 16, 2018 at 18:08

DMA should be quite capable of sub-microsecond acquisition.

You'd look to maximally clock a TIM (ie PSC=0, ARR=0xFFFF), use the TIMx_CHx pin in input capture mode, where the same TIMx_CCx trigger was available to a DMA controller, and have a large enough time stamp capture buffer to hold all events on the time line.

The F0 being somewhat constraining as it only has 16-bit TIM

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

10 REPLIES 10
Posted on March 16, 2018 at 18:08

DMA should be quite capable of sub-microsecond acquisition.

You'd look to maximally clock a TIM (ie PSC=0, ARR=0xFFFF), use the TIMx_CHx pin in input capture mode, where the same TIMx_CCx trigger was available to a DMA controller, and have a large enough time stamp capture buffer to hold all events on the time line.

The F0 being somewhat constraining as it only has 16-bit TIM

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
P S
Associate II
Posted on March 16, 2018 at 20:17

Thanks for your quick reply Clive One. Your posts have already helped me out hugely in this project! I have a brushless motor control working but I have never used DMA before. I am unclear about the 'large enough time stamp capture buffer'. Would that mean if I am detecting on the rising and falling edges..  would I need a buffer to hold 8 events?  What does the DMA capture, the duration of a pulse or the timer count at each rise fall event?

henry.dick
Senior II
Posted on March 16, 2018 at 21:17

'

can DMA read the 4 pulses one after another like this and store in an array?'

sounds like you are trying to read the times of input transitions.

two approaches:

1) dma, as you had suggested. ideally you can couple it with input filtering so there is an interrupt at end of each set of such transitions.

2) find a chip with input capture buffers so the transitions are stored locally.

the first approach is more universal but can be hard to pull-off. the 2nd one requires a new/different chip.

P S
Associate II
Posted on March 17, 2018 at 03:16

Thanks , its working as expected with DMA

Posted on March 18, 2018 at 00:55

How did you arrange it ?

DMA from the port J (for example) ? dumped into a 4k buffer type arrangement ?

How do you calculate the time of start and time of end of each bit ?

I thought the DMA had to fight to get hold of the bus, so you would not have a guaranteed time byte stream if it was interrupted.

Posted on March 18, 2018 at 09:26

You can use timer in 'PWM input mode'. Configure timer to reset with rising edge and to capture time with falling edge of your signal. Configure timer to generate DMA request with capture event. It creates only one time stamp for one pulse. Probably there will be some 'delay' in timer reset, like one or two cycles, but it will be negligible error if you want distinguish 1 and 2.6us pulses.

Posted on March 18, 2018 at 19:44

This is my most likely terrible way of getting the intervals. I just have the dma_buffer big enough that it will contain at least one full cycle. When i find the blank space I store then difference between the next few time stamps. Its rough so I need to further process the output. I need to figure out when its safe to read the dma_buffer or that i am not reading a section that's in the middle of being changed.

void computeProshotDMA(){

int lastnumber = dma_buffer[0];

for ( int j = 1 ; j < input_buffer_size; j++){

if(((dma_buffer[j] - lastnumber) > 8000) && ((dma_buffer[j] - lastnumber) < 20000)){ // blank space

propulse[0] = dma_buffer[j+1] - dma_buffer[j];

propulse[1] = dma_buffer[j+3] - dma_buffer[j+2];

propulse[2] = dma_buffer[j+5] - dma_buffer[j+4];

propulse[3] = dma_buffer[j+7] - dma_buffer[j+6];

break;

}

lastnumber = dma_buffer[j];

}

}

dudka.michal.001 wrote:

You can use timer in 'PWM input mode'. Configure timer to reset with rising edge and to capture time with falling edge of your signal. Configure timer to generate DMA request with capture event. It creates only one time stamp for one pulse. Probably there will be some 'delay' in timer reset, like one or two cycles, but it will be negligible error if you want distinguish 1 and 2.6us pulses.

I have tried this and am having no luck, It seems every way I try and configure it i am only getting the rising or falling intervals. Ie I am expecting pulse durations of 80 80 80 80 after a 10,000 count blank. But am seeing 10250, 250 , 250, 250 for example.

Posted on March 18, 2018 at 21:38

It's probably wrong timer configuration. May be some image can help - this is one configuration fo ''PWM input capture'':

0690X00000604UPQAY.jpg

Because you are not interesed in capturing period, you will not use Capture 2. Configure Capture 1 to catch falling edge from ''indirect'' source. Next configure TI2FP2 to detect rising edge. Set up Slave Mode Controller to reset timer. Example follows:

icinit.TIM_Channel = TIM_Channel_2; icinit.TIM_ICFilter = 0x0;  icinit.TIM_ICPolarity = TIM_ICPolarity_Rising;  icinit.TIM_ICPrescaler = TIM_ICPSC_DIV1; icinit.TIM_ICSelection = TIM_ICSelection_DirectTI; // TI2FP2 TIM_ICInit(TIM3, &icinit);

icinit.TIM_Channel = TIM_Channel_1; icinit.TIM_ICFilter = 0x0;  icinit.TIM_ICPolarity = TIM_ICPolarity_Falling;  icinit.TIM_ICPrescaler = TIM_ICPSC_DIV1;  icinit.TIM_ICSelection = TIM_ICSelection_IndirectTI; // signal from channel 2 (TI2FP1) TIM_ICInit(TIM3, &icinit);

TIM_SelectInputTrigger(TIM3,TIM_TS_TI2FP2); // Event for Reset Controller from rising edge TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); // Reset timer with that event

next configure DMA to transfer data from Capture 1 register to memory (i think you want use circular mode). Enable DMA ''transfer complete'' interrupt (which will indicate that burst is over and collected data waits for you). Enable DMA request in timer (request from Capture1 event). 

There will be one more problem with enabling timer. You must do it in ''pause'' between pulse bursts. Bursts have to be separated by some time (roughly more then ~5-10us). You can identify this ''pause''  by few clever or stupid ways. Stupid (but sufficient) one is:

1. init, but not start, some timer (TIM6 for example)

2. wait for falling edge at line (wait for log.1 then wait for log.0)

3. (re)start that timer (TIM6)

4. wait until timer counts to ~5us or to falling edge, if falling edge comes soon, go to point 3.

5. if your timer counts to 5us without detecting next falling edge, you are in ''pause'' time and you can start main timer (TIM3 in this example). If you are worry that two bursts can rarely comes without sufficient ''pause'' you can set up DMA to take 8 values and enable also ''half transfer'' interrupt. Then you can processing old data and capturing new in same time. 
Posted on March 20, 2018 at 13:30

Thanks so much for your help, I have it working pretty well by just using the DMA conversion complete interrupt and processing the intervals then. I am still having no luck configuring the timers in PWM input mode. Even though its working fairly well its bugging me that i can't get it to work. Your explanation makes perfect sense as to how to set up the timers. It seems logical and simple but..

I am using HAL library, maybe this is my problem. The two issues are

1) Cubemx does not allow for selecting TI2FP2 as a trigger. Only TI1FP1. Nor does it allow for setting channel 2 for Direct mode. I have tried this on TIM2 and TIM15 and TIM3.

2) I set up the timers manually ignoring what cubemx generates and found that I could not get TI2FP2 to trigger a timer reset. TI1FP1 works on the rising or falling edge as expected.

Is this a limitation with HAL drivers? The timers limitations on this MCU ( tried a stm32f302 as well)?

EDIT : I can select these setting by using channel two as the pwm input but then I cannot set up a dma request from channel one through cubemx. I only allows adding a channel two dma request.

I am starting the DMA transfer with this. Do I need to start channel two similarly?

HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1, dma_buffer , input_buffer_size);

This is the config code. ( that doesn't work) and cubemx wont generate anyway anything to do with ti2fp2. What am i missing here?

sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;

sSlaveConfig.InputTrigger = TIM_TS_TI2FP2;

sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;

sSlaveConfig.TriggerPrescaler = TIM_ICPSC_DIV1;

sSlaveConfig.TriggerFilter = 0;

if (HAL_TIM_SlaveConfigSynchronization(&htim2, &sSlaveConfig) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;

sConfigIC.ICFilter = 0;

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;

sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;

if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;

sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;

if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}