Rotary Encoder. Need help very much!

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
Hello technicians,

I worked only with analog projects for a long time and now want to change my direction and make some stuff from digital section.

Some mentions:
I have PIC/AVR programmer;
Have some experience with “C” programming and no deal with ASM;
I already made single up/down counter with buttons;

And now I want to make single up/down counter with rotary (mechanical) encoder. This will be my the first section of preamp

I use legendary AVR chip AT90S2313 :D
If it is possible I would like to avoid using INT’errupt interface however, my rotary encoder is connected to INT pins just in case if there are no other ways

So, can you give me an idea or advices how can I do this task

Thanking you in anticipation

There it is my inoperative “C” code and schema:
:confused:
 

Attachments

  • rotary.zip
    98.9 KB · Views: 114
This can be done in a simple way:

1. You need a task/interrupt or whatever, which is called every 5ms or so (longer than the bouncing time).
2. In the task look for a 0 -> 1 transition of pin A
3. if pin B is low the rotary switch is moved clockwise, if pin B is high the rotary switch is moved C.C.W

I think your program hangs in the while(!(PIND&pin6)&&!(PIND&pin7)) {} loop. At least it's not a good program style.
 
Thanks for replay Bocka,

You are right! In my case I have 2 way of problem solution:

1. Using interrupts, and checking the state of the pins before and after rotation of encoder;
2. Sampling the pins every n microseconds and see if their state has changed.

I’ll try to do this thing, but it seems to me it will take a long time to the paradise.
And thank you for critical of my programming style. I need this.
:)
 
What is typically done for these sorts of problems is to code a finite state automata to do the work. Doing it in the style you have is fraught with potential problems, and gets messy beyond belief as soon as any real complexity is needed.

A quadrature encoder is trivial to express as a FSA.

You code up a general FSA, which simply wakes up, looks at the input token, looks at the current state, and then does the transition. Then goes back to sleep. The input token can be triggered by any sort of interrupt - so you could even code the delays with a timer rather than a loop. If you can get an interrrupt on each input transition it all becomes very easy.
 
I prefer to use "interupt on pinchange"
That way i never mis a rotation.

Although extreem fast turning tends to get misread, it's no real problem because i could never know if i made 30 or 40 turns.
So the software may be inaccurate aswell.

But missing a turn cause the software wasn't focussed. Not ideal!

Regards Simon
 
Yup, do the lot with interrrups. When you get an interrupt you first look about to see where it came from - i.e. timer, pin change. Then you simply step the FSA. Then go back to sleep an await the next interrupt (or in reality, return from interrrupt and allow the code to do the main stuff.) Coded like this you would have to connect your rotary encoder to a power drill before you got enough latency into the interrupt service routine to drop a step.

Whose C compiler are you using?
 
Hi vytas,

although this is the code for the Mega8 it should work with minor changes with your AVR

// interrupt initialisation
// External Interrupt 0 initialization
// INT0: On
// INT0 Mode: Falling Edge

#define fInitInt0() { GICR |= 0x40; \
MCUCR |= 0x02; \
GIFR &= ~0x40; }


/*****************************************************
External Interrupt 0 service routine
*****************************************************/
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// insert your FSM code here
}

/*****************************************************
main skeleton
*****************************************************/
void main(void)
{
fInitInt0() ;
asm("cli")
while (1)
{
// insert your main application code here, i.e. switch relays
// turn LED on/off and so on
}
}

I prefer to use "interupt on pinchange"

This works fine for optical encoders. With mechanical rotary switches always signal bouncing occurs. Using a timer is a elegant solution to solve this problem. But this I would call step 2 for interrupt beginners ;)
 
When using 'interupt on pinchange' (NOT the same as INT0 )
you only know when to reed the pins.
Thats all.
But you have to service the interuptflag.

External interupts a commonly only active on a 'low to high' edge or 'high to low' edge. Rearly both! So this interupt is more intended for time measument.

The interupt on pinchange or keyboard interupt is intended for userinterface handling.

I use a electrical encoder and i only have bouce problems when spinning extremly fast. Let say a step/ 2ms. :hot:
Some RC filtering will improve that.
But maybe the decoding itselve has a large invluence on that aswell.


Here my encoder routine!
Comments in dutch. Sorry :rolleyes:
Written with mplab asm for the pic16f876
It also has boundery checks.
So the encoder value cannot exceed its min and max value.
A keypin is checked to speed up rotation.

Feel free to translate to C and other processors

Regards Simon
 

Attachments

  • encoder.txt
    3.2 KB · Views: 95
Another comment!

The wakeup on pin change is only tripped once even after a a series of bounces.
It will only reactivate after a read or write to that port!
The int0 will trip on every signal transition.
Maybe that why i don't seem to have problems with keybouncing.

Just a thought
 
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.