Rotary Quadrature Encoder

For Flowcode users to discuss projects, flowcharts, and any other issues related to Flowcode 6.

Moderator: Benj

Post Reply
viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Rotary Quadrature Encoder

Post by viki2000 »

I use PIC18F4550, a rotary quadrature encoder with a push button contact and a LCD 20x4 lines.
The program is based on original “Quad_Encode_Test” from here:
http://www.matrixtsl.com/wiki/index.php ... tronics%29

Here is the program that I use:
PIC18F4550_ENCODER.fcfx
Rotary Encoder
(12.46 KiB) Downloaded 590 times
And is working good, reading the encoder very fast, not missing a count when I rotate fast with my fingers. I tested the code on real circuit. Worth to mention that my rotary encoder gives 4 pulses at one indent, so that’s why I divide by 4.
The program displays on the first line of the LCD the text “PWM=xxx%” where xxx is a variable VAR which is only between 0 and 100. There is no actually PWM signal for the moment, it is just a text displayed on LCD. When I push the button, then the VAR value for PWM% is reset to 0.
My problem is that I need the PIC18F4550 to perform a lot of other tasks, the program will be written later, and the reading of rotary encoder to be only a secondary task.
In other words, I do not want the reading on the main loop of the program, but a subroutine, eventually using the interrupts, perhaps interrupts on change on port B (IOC).
But then, I notice that the reading is not so fast, I miss pulses.
The rotary encoder should be normally a secondary priority task, but when is rotated then should be read fast, eventually changing its subroutine priority for the PIC. I notice writing to LCD takes time and the PIC misses the reading of the rotary encoder pulses.
What suggestions, code example could you provide to help reading fast the rotary encoder, but in the same time to not let the PIC doing only that?

User avatar
QMESAR
Valued Contributor
Valued Contributor
Posts: 1287
Joined: Sun Oct 05, 2014 3:20 pm
Location: Russia
Has thanked: 384 times
Been thanked: 614 times
Contact:

Re: Rotary Quadrature Encoder

Post by QMESAR »

viki2000 wrote: eventually using the interrupts, perhaps interrupts on change on port B (IOC).But then, I notice that the reading is not so fast, I miss pulses.
I am not sure if the setting that is shown in your project code is the actual setting however when true you are running at Fosc =24Mhz then Tcy = 24/4 = 6 Mips you are running very slow this is 50% of the speed of the MCU
the 18F4550 can run at 48Mhz /4 = 12Mips that would speed the thing up quite a bit

Regards
QMESAR
Attachments
13.11.png
(19.54 KiB) Downloaded 7830 times

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

No.
It should work also with the internal oscillator 4MHz from a smaller PIC.
Let's make some calculations.
The average speed when turning the rotary encoder is about half of turn in 1 second and maximum 1 turn per second. This is what I tested for real.
If you read the datasheet of such encoders, then you may see that maximum speed is rated 60-100RPM, which is around 1 turn per second, as I tested, but in reality the max. speed is in fact half of it, around half rotation in 1 sec, around 1 full rotation in 2 sec.
http://www.mouser.com/ds/2/54/PEL12T-48721.pdf
http://www.mouser.com/ds/2/54/PEC11L-74340.pdf

My encoder is 24 dents with 4 pulses between 2 dents, so in total 96 pulse per revolution. Let's make it 100 for easy calculation.
Then we have 100 pulses for 1 revolution in max. 1 sec as the fastest turn.
That leads to 10ms between two pulses.
Well, 10ms is a lot of time to read and do something else with a simple 4MHz internal oscillator (1MIPS = 1 Million Instructions per Second).
Then with 24MhZ is 6 times faster, enough speed.
It is just a question how the code is written.

User avatar
QMESAR
Valued Contributor
Valued Contributor
Posts: 1287
Joined: Sun Oct 05, 2014 3:20 pm
Location: Russia
Has thanked: 384 times
Been thanked: 614 times
Contact:

Re: Rotary Quadrature Encoder

Post by QMESAR »

The average speed when turning the rotary encoder is about half of turn in 1 second and maximum 1 turn per second. This is what I tested for real.
If you read the datasheet of such encoders, then you may see that maximum speed is rated 60-100RPM, which is around 1 turn per second, as I tested, but in reality the max. speed is in fact half of it, around half rotation in 1 sec, around 1 full rotation in 2 sec.
Well yes I agree when you have rotation speed of 2 sec per rotation that leaves enough time at 6Mips
my comment was based on average as running at 50% of the capability could be a problem as you did not mention these specs in your previous post :D

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

I just measured the rectangular signal at the rotary encoder pins with oscilloscope on the real application.
I tried to turn it as fast as I could with my fingers for a normal operation.
I measured around 2ms for "1" logic and almost similar for "0" logic as the narrowest pulses, meaning the fastest speed.
That's about 4ms one period for the signal at one pin, almost 50% less than my theoretical estimation from yesterday.
I think is still plenty of time for the PIC to do also something else.
Last edited by viki2000 on Fri Nov 14, 2014 11:49 am, edited 3 times in total.

User avatar
Benj
Matrix Staff
Posts: 15312
Joined: Mon Oct 16, 2006 10:48 am
Location: Matrix TS Ltd
Has thanked: 4803 times
Been thanked: 4314 times
Contact:

Re: Rotary Quadrature Encoder

Post by Benj »

Hello,

Ideally you would use a fairly fast timer interrupt or an interrupt on change to trigger the call to the CheckForChanges macro. This should guarantee that you don't miss any changes and you can get on with your other tasks as part of the main loop.

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Could you provide an example for such interrupts application with quadrature encoder?

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

I have tried to test the interrupt on change on port B, in simulation and also with the real PIC+Encoder.
The update of the LCD is fast and the PIC reads fast the Encoder, no pulses are missed.
I have this time an increment of 2 pulses at each indent. That should be not a problem, because I can divide by 2.
There is only one problem: it gives only negative numbers, no matter if the encoder turns right or left.
Where is the mistake?
PIC18F4550_ENCODER_IOCB_WRONG.fcfx
(8.55 KiB) Downloaded 365 times

User avatar
Benj
Matrix Staff
Posts: 15312
Joined: Mon Oct 16, 2006 10:48 am
Location: Matrix TS Ltd
Has thanked: 4803 times
Been thanked: 4314 times
Contact:

Re: Rotary Quadrature Encoder

Post by Benj »

Hello,

Aha, the negative numbers are occurring because the IOC interrupt only works for pins RB4-RB7 and you have one of the encoder pins set to RB3 so it was missing this change. If you really need to use RB3 then take my example and change the IOC interrupt for a fairly high speed timer interrupt and it should work.

I've changed the program slightly so that the interrupt macro is left as free as possible so it can be fired again when needed and changed the pin connections to RB4 and RB5 and hopefully it should be working correctly for you now.
PIC18F4550_ENCODER_IOCB.fcfx
(8.06 KiB) Downloaded 402 times

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Many thanks for this observation. I just missed it.
Indeed, on page 111 from datasheet of the PIC18F4550 is written:
“9.9 PORTB Interrupt-on-Change
An input change on PORTB<7:4> sets flag bit, RBIF (INTCON<0>).”

http://ww1.microchip.com/downloads/en/d ... 39632c.pdf

I tested the proposed program and works. The only thing is that 200ms delay, which I replaced with an IF decision, and works better for me.
PIC18F4550_ENCODER_IOCB_v2.fcfx
(8.57 KiB) Downloaded 345 times

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Now, after the correction above, I have again the 4 pulse for 1 indent, therefore I divide with 4 to get 1 increment seen on the display.
I have only one more question related with the rotary encoder.
Practically, I need to make some calculations with the number obtained from the encoder.
For example, in my case I need to limit the number in the range 0-100, so it can display PWM in percent 0% to 100%.
Then I tried the next code, which works good but not very good.
PWM_ENCODER_IOCB.fcfx
(12.2 KiB) Downloaded 343 times
Just for my curiosity, I decided to see how is it working with Timer interrupts, so I tried Timer0.
As I know the encoder signal is 2ms on and 2ms off at fastest turn with my fingers, measured with oscilloscope, I considered an internal interrupt of TMR0 under 1ms fast enough, in my case around 682us (1/1464.844).
PWM_ENCODER_TMR0.fcfx
(12.2 KiB) Downloaded 312 times
Last edited by viki2000 on Mon Nov 17, 2014 10:21 am, edited 2 times in total.

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Basically, in the first code with interrupt on change on port B I have next settings for the interrupt, where we can see also RB4-RB7 only as pins for IOC on PORTB:
IOC.jpg
IOC.jpg (96.57 KiB) Viewed 13381 times
Then for the second code, with interrupt in Timer0 I have next setting for the interrupt, the rest of the code remain the same:
TMR0.jpg
TMR0.jpg (104.24 KiB) Viewed 13381 times
By testing the both codes on real device I noticed a difference.
The difference is not noticeable when I rotate the encoder fast with my fingers, but if I rotate slowly, as for example 1 indent, 1 mechanical step only, seen as 1 increment on display, then the TMR0 code reacts perfect and the IOC code does not catch the change sometimes. One more time: only sometimes and only at low rotation of the encoder, the IOC is not so good as TMR0 code.
My question is: why? What can be done for improvement, so the IOC code will react fast, the same way as TMR0 code reacts?
I would still love to use IOC, because TMR0 interrupt will be used later for other functions/subroutines.
Last edited by viki2000 on Mon Nov 17, 2014 12:19 pm, edited 1 time in total.

User avatar
Benj
Matrix Staff
Posts: 15312
Joined: Mon Oct 16, 2006 10:48 am
Location: Matrix TS Ltd
Has thanked: 4803 times
Been thanked: 4314 times
Contact:

Re: Rotary Quadrature Encoder

Post by Benj »

Hello,

The problem could potentially be contact bounce which is giving you problems. Have you fitted the resistors and caps to the encoder input signals like shown on the schematic for the quad encoder E-block.

I've linked the datasheet below if you want to look at our recommended circuit.

http://www.matrixtsl.com/resources/getr ... php?id=610

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

I had installed something like:
Encoder debounce.jpg
Encoder debounce.jpg (27.8 KiB) Viewed 13372 times
But I will try also your recommended filter.

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Here is an update with my last trials.
I tried the proposed filter from E-block and additional next filter:
http://tunafishsandwich.wordpress.com/2 ... ebouncing/
which has a nice explanation with calculations here:
http://www.eng.utah.edu/~cs5780/debouncing.pdf
Then I have tried 2 more encoders, 1 similar with dents and 4 pulses per dent and one without any mechanical dents.
The results are basically the same and they are definitely related with what you suggested: the debouncing in conjunction with external interrupt on change.
Probably additional circuits, a bit more complex, with gates, trigger-Schmidt or other dedicated IC for debouncing would solved the problem and would make the IOC with similar response as TMR0 code.
I also believe that more complex code, with digital filtering and eventually writing different code for reading the encoder instead of using the ready-made encoder component, would solve the problem.
Another solution would be the usage of more expensive encoders as magnetic encoders or better optical encoders.
As it is now, the IOC code is working but not so good as TMR0 code.
I also noticed that using encoders with mechanical dent gives a very nice feedback to the user when rotate one mechanical dent to obtain one increment on display. The encoder without mechanical dents seems that react better for IOC, or at least let the impression when interact with the fingers and we look at display for the results, but not having a feedback for 1 pulse/increment does not always feels nice, but bouncing seems less.
Regarding the TMR0 interrupt code for encoder, I have tried several frequencies for interrupt.
At least in my case, the 1500HZ interrupt seems very good. Of course works very well with higher frequency as 2900Hz, but is not needed. Then I have tried the 700Hz and is OK, but seems not so good as 1500Hz. The 300Hz is not good.
These interrupt frequencies are related with prescaler rate of TMR0.
TMR0 prescaler - interrupt frequency.jpg
TMR0 prescaler - interrupt frequency.jpg (106.52 KiB) Viewed 13355 times
As conclusion, the difference between IOC interrupt and TMR0 interrupt used to read the component encoder is definitely related with bouncing as suggested.

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

There are 2 more observations/requests related with the present subject:
1) It would be very nice if the component encoder would permit to set its counter value. Now, we can only ReadCounter and ResetCounter. But are situations when we want the counter value to be suddenly different or to have limits as in my case with 0-100%. Then a kind of “SetCounter” would be very helpful.
I know this was discussed also in the past here:
http://www.matrixtsl.com/mmforums/viewt ... +variables
and additional variable was suggested as solution, but it would be nice to have everything embedded in the component encoder, by adding this “SetCounter” function.
2) For situations with external interrupts, interrupts on change (IOC) and mechanical encoders with bounces, no matter if we have external RC filters or not, it would be very helpful to have the possibility to add a digital filter, a bit of code, between real input pins and the inputs of the component encoder. Such digital filters are for example here:
http://www.gooligum.com.au/tutorials/ba ... se_C_2.pdf
http://www.gooligum.com.au/tutorials/mi ... id_C_1.pdf

The idea would be to have the possibility to connect the input of the component encoder to an internal bit, a variable in memory defined by the user. Then the user may apply his own code with a digital filter that considers good for his application, between real pins and that bit variable defined as input for component encoder.
I think that will help a lot, especially for situation with IOC.
I never tried to build a component by following the guide from the forum, but maybe the development of the existing encoder component, or perhaps making another new from scratch, and then adding the above 2 functions-properties will generate an improved version of the component encoder.
What do you think?

User avatar
Benj
Matrix Staff
Posts: 15312
Joined: Mon Oct 16, 2006 10:48 am
Location: Matrix TS Ltd
Has thanked: 4803 times
Been thanked: 4314 times
Contact:

Re: Rotary Quadrature Encoder

Post by Benj »

Hello,

I've added the WriteCounter macro so you can set and bound the count value.
quadenc.fcpx
(4.15 KiB) Downloaded 347 times
The changes can be loaded by copying the attached file into your "C:\Program Files (x86)\Flowcode 6\components" directory before starting Flowcode.

As for digital filtering this would be simple enough if we were calling the macro on say a timer tick but as demonstrated this is already working ok. I think the issue is that the IOC interrupt is not firing on all edges due to the bounce which is causing the problem. Therefore even if we added filtering the macro wouldn't be called to actually do the filtering unless the debounce problem is solved outside of the micro, therefore allowing the IOC interrupt to fire as and when an actual change is detected.

Do you have your encoders on a lead or long traces? If so then it might be worth moving the filters so they are as close to the micro pins as possible. Do you have access to a scope so you can see the signals at the microcontroller pins so you can confirm what is happening.

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Thank you for the improved encoder component. It works very well for me, allowing me to simplify the code, using less variables.
I tried 2 codes, both working, both implemented with TMR0 interrupt. The difference is in the “Display” subroutine/macro, where I had to add some conditions for the boundaries of 0% and 100% in order to avoid “flickering” on LCD display when I reach one end or another of the range 0-100 and then the LCD line is written once for short time with numbers "<0" or ">100" instead of staying still 0% or 100%, and that creates the effect of flickering in the real application.
PWM_ENCODER_TMR0_New_v1.fcfx
(11.08 KiB) Downloaded 287 times
PWM_ENCODER_TMR0_New_v2.fcfx
(11.15 KiB) Downloaded 322 times

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

The encoder have very short connections and the capacitors for filtering the bouncing are very close to the pins of the PIC.
Here are the screenshots from oscilloscope:
Encoder_Fast_No_Capacitors.png
Encoder rotated fast - no capacitors for bouncing
(39.9 KiB) Downloaded 7693 times
Encoder_Slow_No_Capacitors.png
Encoder rotated slow - no capacitors for bouncing
(34.65 KiB) Downloaded 7693 times
Encoder_Fast_With_Capacitors.png
Encoder rotated fast - with capacitors for bouncing
(29.66 KiB) Downloaded 7693 times

viki2000
Posts: 190
Joined: Mon Jul 07, 2014 9:38 am
Has thanked: 30 times
Been thanked: 77 times
Contact:

Re: Rotary Quadrature Encoder

Post by viki2000 »

Encoder_Slow_With_Capacitors.png
Encoder rotated slow - with capacitors for bouncing
(29.91 KiB) Downloaded 7693 times

“I think the issue is that the IOC interrupt is not firing on all edges due to the bounce which is causing the problem. Therefore even if we added filtering the macro wouldn't be called to actually do the filtering unless the debounce problem is solved outside of the micro, therefore allowing the IOC interrupt to fire as and when an actual change is detected.”


I am not yet convinced.
Especially for the situation interrupt on change on port B (IOC) combined with very low speed of the encoder, as 0.5s-1s for one dent change, when the bouncing of the encoder input are negligible compared with “normal” rotation of the encoder with my fingers. We can see the less noise signal (bounces) as almost negligible at low speed also from the above screenshots.
I still believe a customized digital filter together with component encoder having as inputs a bit variable would solve the problem. As it is now, I cannot control what is happening between real pins, IOC and input/reading of the component encoder.

Post Reply