Question about STM32 timer usage

More
17 Oct 2017 07:46 #65092 by mwm
Not really a deviation question so much as an STM32 one, but I'm hoping someone here may be able to shed some light for me. Protocol/pwm.c seems to indicate that this doesn't work on deviation hardware, so no help.

Anyway, reading the manual for the STM32, it looks like timers can be set up to provide PWM pulse widths via the capture register - there's a recipe for it in the manual. But I couldn't seem to find any examples that did that - they all fell back on the ATMEGA-like interrupt handler to calculate the width.

I'm using micropython on an STM32F4 Discovery card, so I just followed the recipe in that, and got:
from pyb import Timer
from stm import mem16, TIM2, TIM_SMCR, TIM_CCER

# Configure timer2 as a microsecond counter.
tim = Timer(2, prescaler=84, period=0x3fffffff)

# Configure channel1 on PA0 for timer IC.
ch = tim.channel(1, Timer.IC, pin=pyb.Pin('PA0'), polarity=Timer.FALLING)

# Reset the timer counter on rising edge.
mem16[TIM2 + TIM_SMCR] = (5 << 4) + 4

# Capture counter on falling edge.
mem16[TIM2 + TIM_CCER] = 11

Seems to work fine - calling ch.capture() returns the width of the last pulse in microseconds, which is exactly what I want. All the work is done by the timer hardware - I don't need any code in my main or an interrupt handler, I just need to configure the timer and then read the values.

Is there some reason people don't do this? If not, could we use this for protocol/pwm.c instead of bitbanging things?

Thanks,
Mike

Do not ask me questions via PM. Ask in the forums, where I'll answer if I can.

My remotely piloted vehicle ("drone") is a yacht.

Please Log in or Create an account to join the conversation.

More
10 Nov 2017 22:22 #65721 by victzh
Replied by victzh on topic Question about STM32 timer usage
As far as I understand we're doing exactly this. Just look at deviation/pwm.c

Please Log in or Create an account to join the conversation.

More
12 Nov 2017 13:02 #65762 by mwm
Replied by mwm on topic Question about STM32 timer usage
Who is "we"? The only pwm.c in the deviation source tree is for the the pwm output protocol, which does output, not input.

Do not ask me questions via PM. Ask in the forums, where I'll answer if I can.

My remotely piloted vehicle ("drone") is a yacht.

Please Log in or Create an account to join the conversation.

More
13 Nov 2017 23:46 #65808 by victzh
Replied by victzh on topic Question about STM32 timer usage
Then you probably mean src/target/common/devo/ppmin.c, not protocol/pwm.c you mentioned. I did not realized you're talking about input not output.

If you look at ppmin.c you'll see some timer logic, a bit more complicated than you presented because it needs to decode the whole PPM package, not just last pulse's width. So it uses interrupts not to miss a pulse.

Please Log in or Create an account to join the conversation.

More
16 Nov 2017 08:21 #65879 by mwm
Replied by mwm on topic Question about STM32 timer usage
The code above won't miss a pulse. But ppmin.c doesn't use the facilities I'm asking about. I'm not sure it's not using the edge-triggered capture, as I don't want to grovel through the libopencm3 docs to figure it out. It would just improve the accuracy a bit in any case.

It's clearly not using the auto-preload facility, though. Using that would mean you could skip saving the counter between interrupts and calculating the pulse width.

Do not ask me questions via PM. Ask in the forums, where I'll answer if I can.

My remotely piloted vehicle ("drone") is a yacht.

Please Log in or Create an account to join the conversation.

More
16 Nov 2017 22:22 #65910 by victzh
Replied by victzh on topic Question about STM32 timer usage
This auto reset is theoretically more reliable than handling every pulse in interrupt - the interrupt can be in turn interrupted, but interrupt-based procedure allows to organize more complex logic needed to synchronize package boundaries. And in practice I suspect this does not happen, and even if it happens there is re-synchronization procedure.

So for PWM decoding your proposed procedure works fine, I can't see a way to adapt it to PPM. If you can, please tell - we can implement it.

Please Log in or Create an account to join the conversation.

More
22 Nov 2017 04:23 - 22 Nov 2017 04:36 #66040 by hexfet
Replied by hexfet on topic Question about STM32 timer usage
Got interested in this and spent too much time on it. The summary is that I don't think a change would make a noticeable performance improvement. There is a change needed in the PPMOUT protocol, but it can't be made without versioned model files.

The code in the first post works fine to capture a single PWM signal. When you read ch.capture() it returns the width of the last pulse captured, but says nothing about how many captures may have been missed. For PPM the pulse length must be measured for each of a sequence of pulses, with additional logic to determine which pulse represents the first channel. (Note the code in the first post measures time the pulse is high whereas PPM is total pulse length. With PPM neither the signal polarity nor which edge is measured matters for measuring the pulses.)

The current ppmin.c code sets up an interrupt on the PPM input signal, reads a free-running counter value on interrupt, and subtracts from the previous counter value to determine the pulse length. A timer-based alternative would route the PPM input signal to the timer capture input, set up an interrupt on timer capture, and read the capture value on interrupt. All the other code in ppmin.c is still required - only a subtraction is saved. The precision improves by capturing the counter value automatically, removing any variablility caused by interrupt latency.

On the output side the code in pwm.c is mostly unused when BITBANG_PPM is defined, which it always is. The PPM output pin is controlled directly in protocol/ppmout.c which requires two clock timer interrupts (protocol callbacks) per channel. The commented-out timer code connects the PPM output pin to the timer compare ouput. The timer is configured for PWM with the notch length set in the compare register and the pulse length determined by the reload register. An interrupt is generated when reloading so the next channel value can be loaded, so this method requires half the interrupts of bit-banging (ignoring the callback interrupt). The precision improves by removing interrupt latency in the current bit-bang method.

The change needed in ppmout.c is at line 90, where the notch width should be subtracted from the desired pulse width. This line is the reason the Center and Notch PW default values of 1100 and 400 are needed to produce a pulse length of 1500 for a zero channel value. I consider this a bug because changing the notch width affects the center value. However fixing this would require adding the model files notch width to the center pw value in existing model files, so need model file versioning before a change can be made. Doubt many people change these options anyway.

Most of the interrupts could be eliminated by hooking the timer up to DMA. For output the pulse lengths would be updated in memory by the protocol and transferred to the timer by DMA on timer reloads. For PPM input the capture values would be transferred to a buffer, with a DMA interrupt triggering the channel processing. Some startup code would be needed to implement ppmSync and adjust the number of DMA transfers to the number of channels.

Sidenote: In ppmin.c, because all the variables involved are 16-bit, the line t =
(t1>=t0) ? (t1-t0) : (65536+t1-t0); is exactly the same as t = t1 - t0; . I was impressed that gcc generates the same code for both.

======================

Decided before posting the above I should follow through with some code and see if jitter is really improved by using the timer capture/compare. The PPM output was easiest to implement. Code for devos is here (except 12 - needs generalization).

The previously commented-out code in pwm.c had a couple issues with register defines and didn't set the master output enable. But spent most debugging time figuring out that if more than one peripheral shares an AF pin it's not enough to disable the peripherals not in use - their clock must also be turned off.

I tested the timer-based ppmout.c versus the bit-bang version, measuring jitter as the difference between mininum and maximum measured PPM values for fixed channel values. The timer-based version had jitter of 1-2 microseconds while the bit-bang version is about 4-5 microseconds.

I don't think that's a compelling case for change but don't mind cleaning up the code for a PR if there's a consensus.
Last edit: 22 Nov 2017 04:36 by hexfet.

Please Log in or Create an account to join the conversation.

More
22 Nov 2017 07:36 #66043 by Moeder
Replied by Moeder on topic Question about STM32 timer usage

hexfet wrote: Got interested in this and spent too much time on it.


I like your summary :lol:

Please Log in or Create an account to join the conversation.

More
23 Nov 2017 22:48 #66111 by hexfet
Replied by hexfet on topic Question about STM32 timer usage
:) Well then you probably won't be surprised by this post.

After a sleep and further consideration it didn't seem right to leave pwm.c with commented-out incorrect code. Went ahead and cleaned things up and made a PR. Test builds available . I've tested on devo10 and t8sg. Would like volunteers for devo12 and modular build (7e or F series) as those builds are slightly different.

With the cleanup the pulse widths are spot-on and jitter not measurable at 500ns resolution.

Please Log in or Create an account to join the conversation.

More
05 Dec 2017 08:41 - 05 Dec 2017 08:42 #66344 by FDR
Replied by FDR on topic Question about STM32 timer usage
I want to achieve something similar with an STM32 "pill" board, i.e. to measure a pulse width without using two interrupts for that.
God, I hate libopencm3 so much!!!! :evil:
It is the worse documented library I've ever seen! I have to search it's source code for everything...

BTW I couldn't read an input pin from the EXTI interrupt handler. Is that normal?
Last edit: 05 Dec 2017 08:42 by FDR.

Please Log in or Create an account to join the conversation.

More
06 Dec 2017 01:26 #66357 by hexfet
Replied by hexfet on topic Question about STM32 timer usage
Not normal. Should be able to read pins inside the isr.

Many times looked at the libopencm3 source to find the functions are just setting register bits. Then go to the ST docs to find what the function does. Seems the advantage of the library is the ability to reuse the source code across multiple mcu models.

Please Log in or Create an account to join the conversation.

More
06 Dec 2017 05:12 - 07 Dec 2017 09:16 #66360 by FDR
Replied by FDR on topic Question about STM32 timer usage
Thanks!
I usually do the same: search for the names given by STM, the constants are usually named similar, then search for those constants (or rather their generic counterparts in their defines) in the functions to identify the function I need.
Of course I want to use the library, so the compiler could do the rest, but IMO it's not normal, that one should investigate the source code just to identify what a function is good for...
I find the timer configuration is the most complicated, but I was struggling with DMA as well, not to mention how difficult was to find out how an interrupt handler should named. They simply forgot to document that... :S (Now they are listed in the help, but without a single word.)

Regarding the pin read: I'm not sure it couldn't read, but I defined an interrupt to trigger on both rising and falling edge of a pin, but inside the interrupt handler it's value was never high, despite the handler was called continuously...
Last edit: 07 Dec 2017 09:16 by FDR.

Please Log in or Create an account to join the conversation.

Time to create page: 0.053 seconds
Powered by Kunena Forum