• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

STM32 Encoder Speed Measurement via Input Capture Interrupts

dknguyen

Well-Known Member
Most Helpful Member
Thread starter #1
Hey all y'all:

So the STM32s have this feature where the quad encoder signals at the MCU pins can not only be sent to the typical timer configured as a quad encoder peripheral to keep count of the encoder position, but also to an input capture peripheral that can measure the speed of the encoder tick-by-tick. The input capture can be set to trigger every event up to every 8 events.

[EDIT: IT'S A GENERIC INPUT CAPTURE, NOT ONE SPECIFIC THE ENCODER MODULE]

My question is...even if the input capture ISR is dead simple (just writing the input capture value to memory so that the main program loop can read it), it completely bogs down the processor with the same interrupt firing at 10kHz to >100kHz. Even if it's only set to trigger every 8 events. It's not surprising I guess but what's the point of this mode then? I am using an encoder with 4096 transitions per revolution so was this feature this just intended for much lower resolution encoders or much lower speeds? Or am I missing something?

Let's ignore alternative methods of speed measurement right now, such as anything using the index pulse.
 
Last edited:

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
#4
I use a small pic1840 to read a quadrature signal... I use a 64PPR but it can go MUCH faster.... I then allow a serial read to access the actual count when I need to... It is a slave SPI unit...
 

dknguyen

Well-Known Member
Most Helpful Member
Thread starter #5
Are you assuming there's an ISR? Could it all be hardware?

Mike.
There is an ISR that fires whenever enough capture events have occurred and I coded it to push data to a variable so the main program loop can use it to calculate speed when required since it's wasteful to calculate RPM every time the interrupt fires. If only that prescaler went up to 128 or 256 instead of just 8.

If there is a full hardware method for a generic input capture module to do it, that would be great but I doubt it. That is why I needed to use the input capture's ISR.
 
Last edited:
#6
As long as the quadrature counter itself has enough bits to track movement for a decent period, why not just do the speed calculation at a fixed interval based on a different timer interrupt?

You can adjust that rate to ensure you have adequate time for the calculations and sufficient delay between interrupts for the normal program functions, whatever they are.

With slow movement the calculations may be repeated faster than necessary - but you could always add a software scaling type function to only calculate one in ten, twenty five or a hundred interrupts etc. for more precise calculations at low speeds.

eg. Wait for either a certain time (number of interrupts has passed) so a new reading is required, or more than a specific movement has accumulated, before you need to re-calculate, so it automatically stretches the interval for better accuracy with low speed movement.
 
#7
I've had problems with the input capture before on the STM32's. I was using it to capture framing breaks for data packets at 250kbps, however I had major jitter problems and my capture timings were whack.

I ended up changing methods to utilising a regular EXTI gpio interrupt routine instead, and run an independent 32bit timer at the timing resolution I need in the background - just a free running counter. Then simply saving that CNT value inside the interrupt. It works bang on every time.

I always thought I was doing something wrong with the input capture, and I meant to investigate it further afterwards but never got around to it. Still not sure what the problem was to be honest.
 

dknguyen

Well-Known Member
Most Helpful Member
Thread starter #9
I ended up changing methods to utilising a regular EXTI gpio interrupt routine instead, and run an independent 32bit timer at the timing resolution I need in the background - just a free running counter. Then simply saving that CNT value inside the interrupt. It works bang on every time.
That's what I am doing right now with the a generic GPIO interrupt tied to the index that uses a long long variable that accumulates using the SysTick timer which acts as a general absolute background clock. It works. Unfortunately, I did not make the index pulse go to a pin that also contains an input capture or I would test the index RPM measurement with an input-capture as well.

I've had problems with the input capture before on the STM32's. I was using it to capture framing breaks for data packets at 250kbps, however I had major jitter problems and my capture timings were whack.

I always thought I was doing something wrong with the input capture, and I meant to investigate it further afterwards but never got around to it. Still not sure what the problem was to be honest.
Interesting you bring that up. I noticed that too. I was getting RPM readings that varied wildly from sample to sample. At first I attributed it to speed ripple between encoder ticks that was getting extrapolated over the entire revolution. But that didn't make sense since I would get readings that would get many zero readings in addition to readings that were many times higher than the actual RPM. I might connect a signal generator up to produce a "perfectly constant speed" encoder signal and see what it thinks the RPM is.

As long as the quadrature counter itself has enough bits to track movement for a decent period, why not just do the speed calculation at a fixed interval based on a different timer interrupt?

You can adjust that rate to ensure you have adequate time for the calculations and sufficient delay between interrupts for the normal program functions, whatever they are.

With slow movement the calculations may be repeated faster than necessary - but you could always add a software scaling type function to only calculate one in ten, twenty five or a hundred interrupts etc. for more precise calculations at low speeds.

eg. Wait for either a certain time (number of interrupts has passed) so a new reading is required, or more than a specific movement has accumulated, before you need to re-calculate, so it automatically stretches the interval for better accuracy with low speed movement.
Currently I do have a "every index pulse calculate RPM based on time between the two most recent index pulses) method using generic background timer and generic interrupt as outlined by Cicero. I could do, as you suggest, throw in a fixed time interval sampling compares the encoder position at the beginning and at the end of the interval. However, there are some rare instances where you need instantaneous speed measurements throughout the revolution and preferably at every X degrees of rotation (though you could get away with a temporal sampling rather than spatial sampling). Like if you wanted a motor's speed to pulse sinusoidally throughouts each revolution.
 
Last edited:
#10
However, there are some rare instances where you need instantaneous speed measurements throughout the revolution
But; if the microcontroller you are using is not capable of doing the calculations at above some particular rate, that's a fundamental limit.

You have a choice of that rate, however fast it may be, or use a faster hardware system.

I'm using DSPIC33 series devices as standard; the 33EP256MC502 has a hardware 32 bit quadrature counter.
(With a velocity measurement facility of some sort. I've not used that part of it).

One of my present projects has the timing interrupt running at 100KHz to poll multiple I/O & comms systems. I'd expect one of these to handle what you need easily.
 

dknguyen

Well-Known Member
Most Helpful Member
Thread starter #11
I was just browsing the manual for the dsPIC QEI. It is a lot simpler to use than the STM32 because the STM32's is a subset of the massive do-all timer with dozens of registers, only a small portion of which actually apply to any one operating mode and many of which are irrelevant to the operating mode but will break it if in the wrong state. It is pretty nice to have a hardware indexing function. The STM32 requires a generic GPIO ISR and the appropriate code to be used for that.

But I noticed that there seemed to be no interrupts that trigger on specific encoder values, like a compare interrupt. That seems like a fairly serious feature to be missing?

EDIT: It seems this is only true in the older dsPIC33Fs. The newer ones have position comparators. Not just a spot value comparison but greater than and less than comparisons as well. Plus hardware speed measurement that both measures duration between consecutive ticks as well as distance travelled in a pre-specified time interval. And an index counter. 32-bits too, though I don't know why you would need a 32-bit encoder count. Pretty snazzy. On the STM32, since there is only trigger-on-index or trigger-on-value, direction-bit, background timer, I have to do this this all into the ISRs for the GPIO pin the index running to, or the encoder's compare interrupts (which is really just a timer compare interrupt since the encoder function is a subset of timer functionality). It does let me do some weird things though like index to somewhere in the middle of the encoder rather than zero and max in order to correct mechanical misalignment, rather applying an offset to all position values whenever they are read or worked with.

Now that I think about it, the issue might not actually be that the MCU is being generally bogged down by all the interrupts, but the other interrupts are being missed because so the per-tick speed measurement interrupt fires so often. The other interrupt is a commutation interrupt so it occurs frequently but obviously not every tick. Commutation interrupts were being missed even with nested interrupts and the commutation interrupt having a significantly higher priority level which is kinda of strange now that I think about it.
 
Last edited:
#12
Have a look at the "Peripheral trigger generator" module in the DSPIC33E as well - that's a hardware sequence controller than can be used for interactions between various peripherals without direct CPU involvement.
http://ww1.microchip.com/downloads/en/DeviceDoc/70000669b.pdf

It's something like a limited programmable logic controller using a state machine system & quite a few peripherals have configurable connections to it. That may be suitable for the commutation inputs on your setup.

A 32 bit encoder count is sufficient for such as position control on a machine tool axis; quite useful.
 

dknguyen

Well-Known Member
Most Helpful Member
Thread starter #13
Have a look at the "Peripheral trigger generator" module in the DSPIC33E as well - that's a hardware sequence controller than can be used for interactions between various peripherals without direct CPU involvement.

It's something like a limited programmable logic controller using a state machine system & quite a few peripherals have configurable connections to it. That may be suitable for the commutation inputs on your setup.

A 32 bit encoder count is sufficient for such as position control on a machine tool axis; quite useful.
I just wish there was a simpler way to interface FPGAs to microcontrollers. When coding a lot of this stuff I think "it'd be a lot simpler and more flexible to do in an FPGA" except for the MCU interfacing since there are a lot of things you just don't want to do in an FPGA. There's also the hardware requirements/difficulties for implementing an FPGA. Luckily there are more modules available now, but the MCU interface is still an issue.
 
#14
That's what I am doing right now with the a generic GPIO interrupt tied to the index that uses a long long variable that accumulates using the SysTick timer which acts as a general absolute background clock. It works. Unfortunately, I did not make the index pulse go to a pin that also contains an input capture or I would test the index RPM measurement with an input-capture as well.
.
Yeah sometimes you just have to make a work around...never ideal though.

I'm not sure of the accuracy you're after, but I'd be concerned with using a variable in Systick, as you're adding in more non-deterministic elements into your timings. The Systick interrupt isn't the quickest, it usually isn't the highest priority, and you're using a variable set inside that interrupt which opens you up to potential race conditions depending on the interrupt priority (accessing an interrupt variable from another interrupt) - all of which add jitter to your timing calculations. If you just let a free counter run independently in the background, you could run that at 1us resolution or quicker, and pull the counter value directly from the register in your first interrupt eg: cntVal = (uint32_t)(TIM5->CNT);
 

Latest threads

EE World Online Articles

Loading

 
Top