CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

2-bit quadrature encoder ISR
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Zer0flag
Guest







2-bit quadrature encoder ISR
PostPosted: Wed May 02, 2007 9:35 am     Reply with quote

Hi all!

I am programming a device which uses a 2-bit quadrature encoder for setting values. The encoder signal A is connected to Port B Pin 4 (this one supports interrupts on change) while encoder signal B is on Port B Pin 3 (no interrupts on change). Port B Pins 5 to 7 are display outputs so they can't generate an interrupt as far as I know.

I have written an ISR to read the direction of the encoder which seems to work well although it is very simple. I just wonder if there are some problems that may occur which I did overlook.

Code:

#INT_RB
void encoder_isr()
{
   enc_a = input(PIN_B4);
   enc_b = input(PIN_B3);
   if (enc_b!=enc_a)
   {
      enc_direction = 1;   // clockwise (turning right)
   }
   else
   {
      enc_direction = -1;   // counter-clockwise (turning left)
   }
   
   portb_int_fired = TRUE;
}


Another guy had programmed the ISR before. The difference was that he reads the encoder A pin when initializing things in the main program (before enabling interrupts) and then he reads this Pin every time at the end of the ISR. In contrast I do everything in the ISR and I read encoder A signal first in the ISR.

Thanks for any tips!

Regards,
Zer0flag
Ttelmah
Guest







PostPosted: Wed May 02, 2007 10:05 am     Reply with quote

You should read the input port before enabling the interrupt, otherwise you can get a 'spurious' first interrupt.
INT_RB, occurs, whenever the state of an internal latch, does not match the signal on the pins. The internal latch 'wakes up' in an indeterminate state. If the stored value, does not match the actual value on the pins from the external circuit, an interrupt will be triggered, and you will 'see' one extra movement on the decoder.
The sequence for 'no extra interrupts', is:
Read portB
Clear the interrupt (it will have already been 'set' if the values didn't match
Enable the interrupt
Your logic, doesn't seem to be 'right' to decode the signal. Obviously, triggering on only one line, you will only get half the possible quadrature states, but the test for 'difference' between the two signals, does not represent the right test to work out what is happening.
The test you show, would be correct fo a system only firing on one edge of the signal (using one of the dedicated interrupt lines, rather than the port change line), but gives the wrong result when you trigger on both edges.
Basically, you will enter the code, on both edges of the 'A' signal. Then, for a falling edge on 'A', a high level on 'B' will represent one direction, and a low on 'B' will represent the other. These states are reversed for the rising edge. I suspect this is why the original coder, reads the second bit at the end of the routine. The correct logic would be:
Code:

#INT_RB
void encoder_isr()
{
   enc_a = input(PIN_B4);
   enc_b = input(PIN_B3);
   if (enc_a) {
       //Here we must have a rising edge on A
      if (enc_b) enc_direction = 1;   // clockwise (turning right)
      else enc_direction=-1; //counter clock
   }
   else {
      //Here falling edge
      if (enc_b) enc_direction = -1;   // counter-clockwise (turning left)
      else enc_direction=1;
   }
   
   portb_int_fired = TRUE;
}

Do you really need to use a plus, or -ve number?. if not (can accept just a 0 or 1), then you can simply use the state of the other pin, like:
Code:

#INT_RB
void encoder_isr()
{
   enc_a = input(PIN_B4);
   enc_b = input(PIN_B3);
   if (enc_a) enc_direction=enc_b;
   else enc_direction=enc_b^1;
   portb_int_fired = TRUE;
}

There is even no point really in reading the pins separately, so:
Code:

#INT_RB
void encoder_isr()
{
   if (input(PIN_B4)) enc_direction=input(PIN_B3);
   else enc_direction=input(PIN_B3)^1;
   portb_int_fired = TRUE;
}



Best Wishes
Zer0flag
Guest







PostPosted: Wed May 02, 2007 10:56 am     Reply with quote

Hi!

Many thanks for your reply! The code you sent me works fine. BTW the encoder directions have changed now. I just wonder why my code worked at all without looking at the rising and falling edges...

Ttelmah wrote:

The sequence for 'no extra interrupts', is:
Read portB
Clear the interrupt (it will have already been 'set' if the values didn't match
Enable the interrupt


I believe with "no extra interrupts" you mean "no spurious interrupts".
I don't quite understand what you mean by read Port B. Do you mean an input_b() ? I am not sure because some pins are outputs on that port.
Clearing the interrupt is done by clear_interrupt(INT_RB) I suppose before calling enable_interrupts(INT_RB). Right?

Thanks a lot!

Zer0flag
Ttelmah
Guest







PostPosted: Wed May 02, 2007 2:26 pm     Reply with quote

Setup the output pins to the required values, then read bit4. The latch bits are set by a read of the whole port, or of a single bit.
Yes the clear_interrupt call.
You can't though use 'clear_interrupt', till the latch is correctly set, or it'll just keep triggering. If you are using fast_io (quicker), then you can just read the whole port, and the other bits will remain as outputs.
Yes, no 'extra' spurious bits.
Obviously, you can change the encoder direction, in either set of the code I sent, by changing which way the result is set.

Best Wishes
Zer0flag
Guest







PostPosted: Wed May 02, 2007 3:35 pm     Reply with quote

Ttelmah, I have reviewed once again your code and my ISR (the one that I initially posted). Then I looked again at a quadrature code diagramm.

Now I again have the impression that my ISR is ok and works as expected. As far as I can see from the quadrature code picture it does not really matter if the edge of A is rising or falling. On both types of edges the following applies: if A != B then the encoder has turned CW, else i.e. when A == B the encoder has turned CCW. Or am I wrong?

Thanks for the explanation on setting the latch.

BTW: Does it really matter where the detent point of the encoder is?

Regards,
Zer0flag
Zer0flag
Guest







PostPosted: Fri May 04, 2007 10:22 am     Reply with quote

BTW: I found out that the encoder I have to program is an ALPS EC11B series and its A signal has only one edge between two detent points.

So it still looks like the ISR I posted initially is correct, at least from my point of view Smile

Zer0
Zer0flag
Guest







Strange problem with encoder
PostPosted: Fri Jul 27, 2007 9:48 am     Reply with quote

Hi all!

Now I thought that everything is working until I suddenly noticed a very strange behavior: After turning the ALPS encoder many times in one direction, for one turn the program suddenly reads a wrong direction. When I continue to turn everything is ok and then at some point again a wrong direction for one turn. This looks to be independent of the speed of turning.

I tested this on another device and the same problem exists.

Any idea why this happens? I am really confused. I am even considering that the code on the encoders is not ok or something...

Thanks for any help!

Zer0flag
Ttelmah
Guest







PostPosted: Fri Jul 27, 2007 10:04 am     Reply with quote

Check the signal levels.
It sounds as if sometimes one edge is getting 'lost', so the decoded direction gets reversed... :-(
What pull-ups are you using?.
How are you handlng debounce?.
The latter in particular may be causing you problems. Note the spec for 2mSec maximum 'chatter'. If (for instance), several bounces happened on both edges, and the handler _misses_ one edge, since both bits (say B3), change after B4 is read, the two levels won't agree, and the wrong direction will be decoded. The latch will have been cleared by the second read, so the real 'final' edge will be missed...

Best Wishes
Zer0flag
Guest







PostPosted: Fri Jul 27, 2007 10:35 am     Reply with quote

Hi Ttelmah!

Thank you very much for the fast reply! I am just programming the device but on Monday I will ask the electronics guy to tell me how they have debounced the encoder.

I wanted to make sure first if it is not a software problem but as it looks at the moment it is not. I also tried your suggestion in another thread to use fast_io(B) because the encoder interrupts at RB4 (port on change interrupt) but this does not change anything :(

Just a thought: Do you think that another technique like checking ENC_A and ENC_B inputs periodically (via timer or in the main loop) instead of waiting for the ENC_A interrupt would solve the problem?

Thank you very very much again!

Zer0flag
Ttelmah
Guest







PostPosted: Fri Jul 27, 2007 2:08 pm     Reply with quote

Key thing I'd suggest is reading the two bits at the same time. Read the whole port, just once, and then do the tests on this value. This would avoid the risk of a bit changing between the reads.

Best Wishes
Zer0flag
Guest







PostPosted: Fri Jul 27, 2007 2:19 pm     Reply with quote

Fantastic idea! I looked at the docs and input_b() should do the job, right? I will put it as the first command in my ENC_A on change interrupt routine.
I'll try this out as soon as possible and give feedback :-)

Ttelmah wrote:
Key thing I'd suggest is reading the two bits at the same time. Read the whole port, just once, and then do the tests on this value. This would avoid the risk of a bit changing between the reads.

Best Wishes
Ttelmah
Guest







PostPosted: Sat Jul 28, 2007 2:29 am     Reply with quote

Yes, and use 'bit_test' on the value you read to derive enc_a, and enc_b from this.

Best Wishes
Guest








PostPosted: Sat Jul 28, 2007 10:47 am     Reply with quote

Unfortunately the problem remains :-(

Here is what I do at the beginning of the prog:

Code:

#use fast_io(B)

set_tris_b(0b00011001);


Ad here is my ISR:

Code:

#INT_RB
void encoder_ISR()            // Encoder ISR, reacts on port change (rising AND falling edges), in our case it affects only Pin B4 (Encoder A Pin)
{
   port_b_content = input_b();
   enc_a_state = bit_test(port_b_content, 4);
   enc_b_state = bit_test(port_b_content, 3);
   
   if (enc_a_state != enc_b_state) // compare the inputs (this happens on rising and on falling edges of ENC_A)
      enc_dir = 1;   // CW
   else
      enc_dir = -1; // CCW
   
   encoder_int_fired=TRUE;
}
Ttelmah
Guest







PostPosted: Sat Jul 28, 2007 2:44 pm     Reply with quote

The only remaining thing you can try, is to set the ISR, to 'no_clear', and clear the interrupt manually on the very next instruction after the port read. This way the interrupt should trigger again, if there are two edges really close together.
Unfortunately, I'd say you should be cleaning the edges of the signals in hardware, and if you get some really nasty transitions, oddities become increasingly likely... :(

Best Wishes
Guest








PostPosted: Sat Jul 28, 2007 5:14 pm     Reply with quote

I'm afraid I didn't exactly understand the benefit of having the interrupt trigger a second time :(

Ttelmah wrote:
The only remaining thing you can try, is to set the ISR, to 'no_clear', and clear the interrupt manually on the very next instruction after the port read. This way the interrupt should trigger again, if there are two edges really close together.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group