Using the Encoder Sample
I used the encoder sample and attached it to Two US Digital· Optical Encoders.. Everything works as expected with the following code which is the sample code that came with the SX/B Software.. I striped the LCD Code and the non Essentials and made it work for two seperate encoders.. I some what understand the code, but was wondering if there was a way to make this code more efficient.· Take a Look.. Oh yeah I am out putting the Encoder counts to a 74HC595.
Thanks for anyhelp.
And then What I am looking for is to be able to read this information from a Stamp or Propeller.. What would be the best way. Serial Output??
Regards,
Eric
DEVICE SX28, OSC4MHZ, TURBO, STACKX, OPTIONX FREQ 4_000_000,4_480_000 ID "ENCODER"
EncPort VAR RA ' encoder port TRIS_Enc VAR TRIS_A
MaxVal CON 255
'encCheck VAR Byte Encoder_A_Old VAR Byte Encoder_A_New VAR Byte Encoder_A_Count VAR byte Encoder_B_Old VAR Byte Encoder_B_New VAR Byte Encoder_B_Count VAR byte
tmpB1 VAR Byte tmpB2 VAR Byte tmpB3 VAR Byte
' ========================================================================= INTERRUPT ' =========================================================================
' Runs every 64 uS @ 4 MHz (no prescaler)
ISR_Start:
Encoder_A_New = EncPort & %00001100
TmpB3 = Encoder_A_Old XOR Encoder_A_New
IF tmpB3 > 0 THEN
Encoder_A_Old = Encoder_A_Old << 1
Encoder_A_Old = Encoder_A_Old XOR Encoder_A_New
IF Encoder_A_Old.3 = 1 THEN
INC Encoder_A_Count
ELSE
DEC Encoder_A_Count
ENDIF
Encoder_A_Old = Encoder_A_New
ENDIF
Encoder_B_New = EncPort & %00000011
TmpB3 = Encoder_B_Old XOR Encoder_B_New
' =========================================================================
IF tmpB3 > 0 THEN
Encoder_B_Old = Encoder_B_Old << 1
Encoder_B_Old = Encoder_B_Old XOR Encoder_B_New
IF Encoder_B_Old.1 = 1 THEN
INC Encoder_B_Count
ELSE
DEC Encoder_B_Count
ENDIF
Encoder_B_Old = Encoder_B_New
ENDIF
'ISR_Exit:
RETURNINT
' ========================================================================= PROGRAM Start ' =========================================================================
Start:
Encoder_A_New = EncPort & %00001100
Encoder_A_Old = Encoder_A_New
Encoder_A_Count = 0
Encoder_B_New = EncPort & %00000011
Encoder_B_Old = Encoder_B_New ' copy
Encoder_B_Count = 0
OPTION = $88 ' interrupt, no prescaler
Main: DO ShiftOut RC.7,RC.6,1,Encoder_A_Count Pulsout RC.0,1 ShiftOut RC.7,RC.6,1,Encoder_B_Count Pulsout RC.0,1 LOOP
END
Thanks for anyhelp.
And then What I am looking for is to be able to read this information from a Stamp or Propeller.. What would be the best way. Serial Output??
Regards,
Eric

Comments
The best way is to forget the 74HC595, and keep it all inside the SX or Propeller. Why would you need to send it out, only to retrieve it again ??
Cheers,
Peter (pjv)
Post Edited (SailerMan) : 10/5/2006 12:12:38 PM GMT
This saves a couple bytes, at the cost of·using a second temp in the interrupt. Since you have both encoders on the same port, there is no reason you can't save both channels in one oldbyte and save space.· You can also do the shifting and xoring of both channels at the same time, since you only look at one bit of the results to determine direction.
This also lends itself to extension to 4 channels on one 8 bit port, by adding the appropriate bit tests for additional channels.· Or make the thing a loop with a bit mask to test the change and direction bits·and increment or decrement the positions in an array.
'encCheck VAR Byte EncodersOld VAR byte EncodersNew VAR byte Encoder_A_Count VAR byte Encoder_B_Count VAR byte tmpB1 VAR byte tmpB2 VAR Byte tmpB3 VAR Byte ' ========================================================================= INTERRUPT ' ========================================================================= ' Runs every 64 uS @ 4 MHz (no prescaler) ISR_Start: EncodersNew = EncPort TmpB1 = EncodersOld XOR EncodersNew ' compare old with new ' to work out which channels have changed TmpB2 = TmpB1 << 1 ' make single bit that shows change on A or B phase TmpB1 = TmpB1 OR TmpB2 EncodersOld = EncodersOld << 1 ' make direction bits by comparing old A with new B EncodersOld = EncodersOld XOR EncodersNew ' xor old A with new B IF TmpB1.3 = 1 THEN IF EncodersOld.3 = 1 THEN INC Encoder_A_Count ELSE DEC Encoder_A_Count ENDIF ENDIF ' ========================================================================= IF tmpB1.1 = 1 THEN IF EncodersOld.1 = 1 THEN INC Encoder_B_Count ELSE DEC Encoder_B_Count ENDIF ENDIF EncodersOld=EncodersNew' ========================================================================= PROGRAM Start ' ========================================================================= Start: EncodersNew = EncPort EncodersOld = EncodersNew Encoder_A_Count = 0 Encoder_B_Count = 0 OPTION = $88 ' interrupt, no prescaler Main: DO ShiftOut RC.7,RC.6,1,Encoder_A_Count Pulsout RC.0,1 ShiftOut RC.7,RC.6,1,Encoder_B_Count Pulsout RC.0,1 LOOP[/code][/size]
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
Thanks for any help.
Eric
Encoders 101 Most encoders generate two signals, generally square waves 90 degrees out of phase. I say most, because you can get ones that also put out an index pulse once per revolution, and there are some that output analog sine waves instead of digital signals. The basic idea is that with the two phases 90 degrees apart, you can distinguish the direction of rotation. Position comes from keeping track of the number of pulses and the direction. This is also known as a Gray or Grey code. The key feature there is that only one bit can change at a time when moving from one state to another adjacent state. You can list them like this, I did two cycles. I labeled the phases A and B. If you look at it in sequence, each two bits from top to bottom, you can see each signal goes 0 to 1 then back again, but overlapped. Looking at it from top down might represent the output sequence of the encoder moving in a clockwise direction, looking at it from bottom up counterclockwise. AB 00 10 11 01 00 10 11 01 Side ways: A01100110011001100110011 B00110011001100110011001 If the encoder isn't moving, it outputs the same state continuously. To detect a change in position, we sample the encoder outputs at regular intervals and compare the current sample to the previous one. Using an XOR to do this works nicely because when you XOR two bits you get a one only if they are different. Truth table for XOR: XOR A B O 0 0 0 0 1 1 1 0 1 1 1 0 So if we went from encoder state 00 to state 01, and XOR'd the two, we would get 01. If we went from 01 to 11 we would get 10. So for some changes we get a result of 01, for others we get 10. If we only want to do one test to see if either bit has changed, we can do that by ORing the two bits together. I did it by shifting a copy of the original XOR result up one bit, and ORing it with the original. This gives me a single bit that will be a 1 if either channel of the encoder changed state. Consider your situation where you have two encoders as two pairs of bits next to each other in the same byte. Bits 0 and 1 are one encoder, bits 2 and 3 are the next: Encoder: AABB Phase: ABAB Bit # 76543210 00001100 :encoders sitting at rest, old state 00000100 :encoder A moved one tick clockwise, new state XOR 00001000 :result of XOR old and new << 00010000 :shifted copy of the XOR, low bit of comparison shifted into position to line up with the high bit. OR 00011000 :result of oring the two together Now, we have to be careful to pick the correct bits to test, we want the bit that is the OR of the two change bits for each encoder. In this case, since we shifted up, we want to pick the higher bit of each pair, because we OR'd the high bit of the pair with the lower bit. The low bit of each pair is now meaningless since the lower bit of the pair got OR'd with the high bit of the pair to its right. So we test bits 1 and 3 and we see we got a 1 in bit 3, which indicates encoder B moved, and a 0 in bit 1 which indicates encoder A didn't move. We need to work out how to detect the direction based on getting any two states in sequence. If we take the old sample B phase and XOR it with the new sample A phase, we get a one when the encoder is moving CW, and a zero when the encoder is moving CCW. You can see from the diagrams below that moving CW, the old B and new A are always different, moving CCW the old B and new A are always the same. I've used the slashes to indicate the two bits getting XORd, with the result to the right of the slash. I've labeled the phases A and B, keep in mind both sequences are the same, moving along them top to bottom represents clockwise, moving bottom to top is counter clockwise, the slashes indicate which bits get compared in each direction: XOR old B new A moving CW: Time Time moves moves down up Start End A B CW A B CCW 0 0 0 0 / 1 \ 0 1 0 1 0 / 1 \ 0 1 1 1 1 / 1 \ 0 0 1 0 1 / 1 \ 0 0 0 0 0 / 1 \ 0 1 0 1 0 / 1 \ 0 1 1 1 1 / 1 \ 0 0 1 0 1 End Start Referring to the previous example: Encoder: AABB Phase: ABAB Bit # 76543210 00001100 :encoders sitting at rest, old state 00000100 :encoder A moved one tick clockwise, new state << 00011000 :old state shifted left, old B now lined up with new A XOR 00011100 :result of XOR shifted old and unshifted new Again, since we shifted to the left one bit, we want to use the higher bit of each pair, the A phase bit of the result, to indicate direction. Encoder A bit A is a 1 in the result, which indicates clockwise movement. The direction bit is only meaningful if the encoder actually moved, so in the code we test for the change bit, and then the direction bit. Hopefully this shows how you could do 4 encoders on one 8 bit port, using one set of operations to calculate the change and direction bits.▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
Post Edited (Michael Chadwick) : 10/6/2006 5:42:11 PM GMT
Nice explanation, but I believe bits 3 and 2 of the "XOR" line in the first sequence you posted are reversed.
Cheers,
Peter (pjv)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
MRC
I ran into variable space limits.
Is there any way to process that many encoders with a single sx? I am I running into territory that would be eaiser if I went with a PROPELLER?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Meh. Nothing here, move along.
The SX can easily read 6 encoders. Not a problem. Not an issue. Piece of cake. The fact that you tried to modify some existing code and ran into problems in no way changes the ability of the SX to do the job.
I have no idea how you modified the code since no code was posted, but it's quite likely an erroneous or inefficient modification that is causing you problems.
Thanks,
PeterM
The problem I ran into is that when all is said and done, you have too many variables.
I thought about some method of data storage, but don't see how that is different from declaring variables.
I might be able to eliminate 6 variables, but I don't see any way to do more.
I think the problem is, I need to maintain a WORD cound for every encoder.
thats six word variables. on Top of everything else.
Right now its set up for 3 encoders. If I add 1 more byte variable I am over my ram.
My fear isn't that it can't be done, unless done in a way that removes the simplicity of sxb.
I would prefer to keep it fairly simple. And am not going to bother with assembly.
' ------------------------------------------------------------------------- ' Device Settings ' ------------------------------------------------------------------------- DEVICE SX28, OSC4MHZ, TURBO, STACKX, OPTIONX FREQ 4_000_000 ID "ENCODER" ' ------------------------------------------------------------------------- ' IO Pins ' ------------------------------------------------------------------------- EncPort3 VAR RC ' encoder port EncPort2 VAR RB ' encoder port TRIS_Enc3 VAR TRIS_C TRIS_Enc2 VAR TRIS_B ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- encCheck VAR Byte 'MaxVal VAR Byte enc1old VAR Byte ' previous encoder bits enc2old VAR Byte ' previous encoder bits enc3old VAR Byte ' previous encoder bits 'enc4old VAR Byte ' previous encoder bits 'enc5old VAR Byte ' previous encoder bits 'enc6old VAR Byte ' previous encoder bits enc1 VAR Byte ' new encoder bits enc2 VAR Byte ' new encoder bits enc3 VAR Byte ' new encoder bits 'enc4 VAR Byte ' new encoder bits 'enc5 VAR Byte ' new encoder bits 'enc6 VAR Byte ' new encoder bits encCount1 VAR Byte ' encoder value encCount2 VAR Byte ' encoder value encCount3 VAR Byte ' encoder value 'encCount4 VAR Byte ' encoder value 'encCount5 VAR Byte ' encoder value 'encCount6 VAR Byte ' encoder value change1 VAR Byte change2 VAR Byte change3 VAR Byte 'change4 VAR Byte 'change5 VAR Byte 'change6 VAR Byte Pot1 VAR Word Pot2 VAR Word Pot3 VAR Word 'Pot4 VAR Word 'Pot5 VAR Word 'Pot6 VAR Word '-------------------------------------------------------------------------- ' Interrupt Start ' ------------------------------------------------------------------------- INTERRUPT GOTO Handle_Int ' ------------------------------------------------------------------------- ' Subroutines / Jump Table ' ------------------------------------------------------------------------- ProcessPot1 SUB ' ------------------------------------------------------------------------- ' Interrupt Code ' ------------------------------------------------------------------------- Handle_Int: ' Runs every 64 uS @ 4 MHz (no prescaler) enc1 = EncPort2 & %00000011 ' get econder bits enc2 = EncPort2 & %00001100 enc3 = EncPort2 & %00110000 'enc4 = EncPort2 & %00001100 'enc5 = EncPort2 & %00110000 'enc6 = EncPort2 & %11000000 change1 = enc1old XOR enc1 ' test for change1, put result into change1 change2 = enc2old XOR enc2 ' test for change1, put result into change1 change3 = enc3old XOR enc3 ' test for change1, put result into change1 'change4 = enc4old XOR enc4 ' test for change1, put result into change1 'change5 = enc5old XOR enc5 ' test for change1, put result into change1 'change6 = enc6old XOR enc6 ' test for change1, put result into change1 GoSub ProcessPot1 ISR_Exit: RETURNINT ' ========================================================================= PROGRAM Start ' ========================================================================= ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- Start: encCount1 = 8 enc1 = EncPort2 & %00000011 ' read encoder pos enc1old = enc1 ' copy Toggle RB.7 Pot1 = 32768 OPTION = $88 ' interrupt, no prescaler Main: Watch Pot1, 16, UDEC GoTo Main ' ------------------------------------------------------------------------- ' Subroutine Code ' ------------------------------------------------------------------------- ProcessPot1: IF change1 > 0 THEN ' change1? enc1old = enc1old << 1 ' adjust old bits enc1old = enc1old XOR enc1 ' test direction IF enc1old.1 = 1 THEN IF encCount1 < 12 THEN ' if max, no change1 INC encCount1 If encCount1 = 12 Then Toggle RC.0 encCount1 = 8 Inc Pot1 ' increase value Endif Endif Else IF encCount1 > 4 THEN ' if 0, no change1 DEC encCount1 ' decrease value If encCount1 = 4 Then Toggle RC.0 encCount1 = 8 DEC Pot1 ' decrease value Endif Endif Endif enc1old = enc1 ' save last input Endif Return▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Meh. Nothing here, move along.
I’m using a mechanical encoder and battling with debouncing the input. This approach will solve the problem. I was using a complex If-ElseIf routine with a flag but occasionally the encoder will count 1 byte backwards when turned clockwise (and visa versa). The problem is that while the contact bounce is usually under 100ns, it’s occasionally over 30ms (30X). Set the debounce for 50ms and you can’t spin the encoder fast, set the debounce for 10ms and the miss-counting occasionally occurs.
If I use this routine, contact bounce won’t matter and I’ll get much faster code.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔