Hello,
is there a sample code for interfacing quadrature encoders[a/b channels]
I need to determine position/direction...
i searchd for posts in the pbp forum,but they were all for 1 channel
Thank you
-Gerry
Hello,
is there a sample code for interfacing quadrature encoders[a/b channels]
I need to determine position/direction...
i searchd for posts in the pbp forum,but they were all for 1 channel
Thank you
-Gerry
Here is a small example how to count up and down a position counter.
How to initialise the position counter you must do by yourself.
A_INPUT VAR PORTA.0
B_INPUT VAR PORTA.1
HULP1 VAR BIT
HULP2 VAR BIT
COUNTER VAR WORD
Clear
COUNTER = 32768
START:
HULP2 = A_INPUT & ~ HULP1 'EVERY POSITIVE GOWING EDGE OF A_INPUT
HULP1 = A_INPUT 'GIVES A PULSE OF ONE PROGRAMM CYCLE
IF HULP2 = 1 AND B_INPUT = 1 THEN 'MOTOR TURNS RIGHT
COUNTER = COUNTER + 1
ENDIF
IF HULP2 = 1 AND B_INPUT = 0 THEN 'MOTOR TURNS LEFT
COUNTER = COUNTER - 1
ENDIF
Hi!
Maybe these two links will help you out.
Nuts and Volts:
http://www.parallax.com/dl/docs/cols...l1/col/nv8.pdf
See State machine: two-bit quadrature encoder
http://www.emesystems.com/BS2fsm.htm#twobit%20encoder
Luciano
mat janssen>>
Thank yoiu very much for the code snip..but i cant understand whats going on in the code ,esp the "&~" part
I tried using the code...however ,whatever maybe the direction the motor is running ....i get the same reading ,say always "left"
Luciano>>
Thanks for the links..
i ported the foll code from nv8 [http://www.parallax.com/dl/docs/col...ol1/col/nv8.pdf --pg7]
Code:let new = pins & %11000000 ' Mask off all but bits 6 & 7 start: let old = new & %11000000 ' Mask bits and copy new into old. again: let new = pins & %11000000 ' Copy encoder bits to new. if new = old then again ' If no change, try again. let directn = bit6 ^ bit15 ' XOR right bit of new w/ left bit of old. if directn = 1 then CW ' If result=1, encoder turned clockwise. serout 0,n2400,(I,left) ' If result=0, counterclock (scroll left). goto start ' Do it again. CW: serout 0,n2400,(I,right) ' Clockwise (scroll right). goto start ' Do it again.
To pbp
with the above code...there is alot of fluctuation between the 2 directions..Code:old VAR BYTE new VAR BYTE direct VAR BIT quad: new= PORTC && %00000011 start: old=new && %00000011 again: new= PORTC && %00000011 Pause 10 LCDOut $fe,1,"no movement" IF new=old Then again direct=new.bit1 ^ old.bit0 IF direct=0 Then GoTo cw LCDOut $fe,1,"one dir" Pause 10 GoTo start cw: LCDOut $fe,1,"2nd dir" Pause 10 GoTo start
..anything going wrong in my code?.i sat on it the whole day..still cant figure out the problem
Hallo,
I completed the programm for testing and it works ok.
With the & ~ look in the manual it takes the not value of the bit.
here is the complete source and also the hex file
'PIC 16F628A test
@ DEVICE PIC16F628A,INTRC_OSC
@ DEVICE PIC16F628A,MCLR_OFF
@ DEVICE PIC16F628A,BOD_OFF
@ DEVICE PIC16F628A,LVP_OFF
@ DEVICE PIC16F628A,CPD_OFF
@ DEVICE PIC16F628A,PROTECT_OFF
DEFINE OSC 4
CMCON = 7
VRCON = 0
OPTION_REG.7 = 0
TRISA = %11111111
TRISB = %00000000
A_INPUT VAR PORTA.0
B_INPUT VAR PORTA.1
HULP1 VAR BIT
HULP2 VAR BIT
COUNTER VAR WORD
Clear
COUNTER = 128
START:
HULP2 = A_INPUT & ~ HULP1 'EVERY POSITIVE GOWING EDGE OF A_INPUT
HULP1 = A_INPUT 'GIVES A PULSE OF ONE PROGRAMM CYCLE
IF HULP2 = 1 AND B_INPUT = 1 Then 'MOTOR TURNS RIGHT
COUNTER = COUNTER + 1
EndIF
IF HULP2 = 1 AND B_INPUT = 0 Then 'MOTOR TURNS LEFT
COUNTER = COUNTER - 1
EndIF
PORTB = COUNTER
GoTo START
Hello ICE!
You need the Bitwise Operator:
&
Not the logical Operator AND:
&& (AND and && are the same)
From the PBP manual:
4.17.14. Bitwise Operators
Bitwise operators act on each bit of a value in boolean fashion.
They can be used to isolate bits or add bits into a value.
4.19. Logical Operators
Logical operators differ from bitwise operations. They yield a true/false
result from their operation. Values of 0 are treated as false. Any other
value is treated as true.
Luciano
Last edited by Luciano; - 8th April 2005 at 11:33.
Luciano,
I tried it with & [bitwise operators]...actually everything..
still no go...
mat janssen>>
are u by any chance using a dual D f/f between the encoder and the PIC,because im not..
Thanks for ur code snip
Last edited by ice; - 8th April 2005 at 12:52.
No, this small programm reacts as a D flipflop.
see attached drawing what happens
Hello ICE,
From the PDF file page 3:
http://www.parallax.com/dl/docs/cols...l1/col/nv8.pdf
In the case of the encoder sequence, it turns out that for
any given sequence, XORing the righthand bit of the old value
with the lefthand bit of the new value tells you the direction
of rotation.
For example, take the clockwise sequence 01 00: 1 XOR 0 = 1.
Now the counter-clockwise sequence 01 11: 1 XOR 1 = 0.
This relationship holds for any pair of numbers in either direction.
* * *
The code for the above is:
direction_bit = old.bit0 ^ new.bit1 ' 1 if CW, 0 if CCW
* * *
In your code:
Pause 10 '= 10 milliseconds
* * *
This will help you to debug your code.
Remove the line
LCDOut $fe,1,"no movement"
Replace the lines
LCDOut $fe,1,"one dir" with LCDOut $fe,1,"Last direction was CCW"
LCDOut $fe,1,"2nd dir" with LCDOut $fe,1,"Last direction was CW"
Luciano
be sure you have pull-up or pull down attach to both output of your encoder.
depending of you rotary encoder model, you can try this thread and monitor the 'value' variable. or modify it easily to have CW and CCW flag in one shot....
Steve
It's not a bug, it's a random feature.
There's no problem, only learning opportunities.
File attachment = Incremental quadrature encoder direction drawing.
Luciano
Last edited by Luciano; - 9th April 2005 at 15:03.
Luciano>>
Thanks for the file..i figured out the logic of the "XOR"..
i included this in my code...[the counting up and counting down lines]
...I am able to see the counter going up and down as the motor changes it's direction..however the LCD line "last was cw/ccw"..keeps on flipping and never remains the same..even if the motor has moved in the same direction..Code:quad: new= PORTC & %00000011 start: old=new & %00000011 again: new= PORTC & %00000011 IF new=old Then again direction_bit=old.bit1 ^ new.bit0 IF direct=0 Then cw LCDOut $fe,1,"Last was CCW" counter=counter+1 LCDOut $fe,$c0,DEC counter Pause 5 GoTo start cw: LCDOut $fe,1,"Last was CW" counter=counter-1 LCDOut $fe,$c0,DEC counter Pause 5 GoTo start
Im still working on it..and will keep u all posted...
Thanks for the link M_emonitor the 'value' variable. or modify it easily to have CW and CCW flag in one shot....
..i'll be doing tht to take care of the above problem
Try this code. (Not tested).
(Problem in your code: you use 'direction_bit' but also 'direct').
Luciano
Code:old_val VAR BYTE new_val VAR BYTE direction_bit VAR BIT enc_counter VAR WORD enc_counter = 10000 new_val = PORTC & %00000011 start: old_val = new_val again: new_val = PORTC & %00000011 IF new_val = old_val Then again direction_bit = old_val.bit0 ^ new_val.bit1 IF direction_bit = 1 Then LCDOut $fe,1,"Last was CW" enc_counter = enc_counter + 1 LCDOut $fe,$c0,DEC enc_counter else LCDOut $fe,1,"Last was CCW" enc_counter = enc_counter - 1 LCDOut $fe,$c0,DEC enc_counter endif GoTo start
Last edited by Luciano; - 12th April 2005 at 11:04.
Thank you for all your help.
Theres a small problem though.
The encoder program counts faster in one direction and slower in the other...
is it because ...
When im counting in one of the directions ,im utilizing the ADC[ADCIN and an IF statement]
..whereas in the other direction,im not.
The program logic is the same as above.
Last edited by ice; - 18th April 2005 at 08:38.
before i go ahead with my code....ill explain wht i have to do
I need to control the position of a DC motor[geared],using a potentiometer
I have to first detect the endpoints of a gear assembly.
say, i start with a count of 32500 and the count at one end of the gear assembly is 32600
..now i rotate it to the other side ,say the count at this point is 32400
The above was just the calibration routine.Using the above values ,i have to
scale the 200 counts[32600-32400] to a potentiometer value[0 to 255]
How am i detecting the end points?
..im using the LMD18200T motor driver .Using the current sense feature of the chip,i detect wheteher the gear assembly has reached an end point.The current sense pin is connected to an ADC port..portA.1 namely
my program flow...
1> set counter to 32500
2> move motor in one direction
3> count pulses..if CW,increment.if CCW decrement
...say it is CW and counter increments to 32600 till the end
4>counter_1 has 32600
5>move the motor to the other side,count the pulses..it is decrementing now
6>so counter_2 has 32400
7>now i have to scale the gear assembly range to a potentiometer
difference=counter_1-counter_2
=32600-32400
=200
8> scale to a pot value
pot_val holds the pot value connected to analog portA.0
hence required count value=[difference/256]*pot_val+counter_2
=[difference*pot_val]/256+counter_2 (rearranging)
example...if potvalue in 128,then
required count value=(200*128)/256+32400
=32500
now i dont know where to go from here..how do i get the motor to move to position 32500,when the pot is moved to 128
...this is a long post..but its the best i could do to explain wht im trying to do.
-Gerry
If you are losing encoder counts, then use an interrupt service routine.
Four of PORTB’s pins, RB<7:4>, have an interrupt-onchange feature.
Use two of these pins for the A/B encoder signals.
Luciano
ok,heres my program ,using interrupts..
the problems i'm encountering are below the code
..
1>The interrupts are worng fine,however the code is still unable to distinguish between CW and CCW,because of which the counting goes awry..Code:DEFINE LOADER_USED 1 led VAR PORTB.7 old VAR BYTE new VAR BYTE direction_bit VAR BIT counter VAR WORD counter_1 VAR WORD counter_2 VAR WORD direction_store VAR BIT adval VAR WORD ' Create adval to store result potval VAR WORD servo_val VAR WORD difference VAR WORD counter=32500 HPwm 0,150,20000 OPTION_REG = $7f ON INTERRUPT GoTo myint ' Define interrupt handler INTCON = $90 ' Enable INTE interrupt loop: High led ' Turn LED on LCDOut $fe,1,"count:" LCDOut $fe,$c0,DEC counter 'Pause 10 GoTo loop ' Do it forever ' Interrupt handler Disable ' No interrupts past this point myint: Low led ' If we get here, turn LED off enc: new= PORTB & %00000011 start_1: old=new & %00000011 again: new= PORTB & %00000011 'load new value of A/B channels into new by anding with HEX3 'IF new=old Then again direction_bit=old.bit1 ^ new.bit0 'check direction IF direction_bit = 1 Then LCDOut $fe,1,"Last was CW" counter = counter + 1 'increment counter LCDOut $fe,$c0,DEC counter," ",BIN direction_bit Else LCDOut $fe,1,"Last was CCW" counter = counter - 1 'decrement counter LCDOut $fe,$c0,DEC counter," ", BIN direction_bit EndIF Pause 10 INTCON.1 = 0 ' Clear interrupt flag Resume ' Return to main program Enable
2>if i remove the "if then " statement for CW/CCW direction and only put a
counter=counter+1,it works well
The whole problem seems to be the direction decoding part..
..i cant seem to find a solution to it..
Last edited by ice; - 3rd May 2005 at 13:00.
Hi!
1.
You are using the wrong interrupt.
Four of PORTB’s pins, RB<7:4>, have an interrupt-onchange feature.
Use two of these pins for the A/B encoder signals.
2.
Use assembly code for the interrupt.
(See PicBasic Pro manual).
3.
In your code the variables "new" and "old" will have
most of the time the same values.
In your code you are doing that:
new= PORTB & %00000011 'get encoder A/B
old=new & %00000011 'old is now same as new
new= PORTB & %00000011 'get encoder A/B again
When you get A/B again, it is very likely that the encoder is
still in the same position, therefore the variables new and old
will have the same values.
* * * *
Did you try the code I posted the 12th April 2005, 11:00?
Does it work if you turn the encoder slowly?
Luciano
Luciano,
when i try the program u recommended [12th april] as below,
there are alot of instances when the program gets confused with direction,
so it sometime increments and decrements.Im not using interrupts
when i move the motor slowly,the problem still persists.
Thank you for your responsesCode:old_val VAR BYTE new_val VAR BYTE direction_bit VAR BIT enc_counter VAR WORD adval VAR WORD ' Create adval to store result potval VAR WORD servo_val VAR WORD difference VAR WORD TRISB=%11111111 enc_counter=32500 HPwm 0,100,20000 new_val = PORTB & %00000011 start: old_val = new_val again: new_val = PORTB & %00000011 'LCDOut $fe,1,"no move" IF new_val = old_val Then again direction_bit = old_val.bit0 ^ new_val.bit1 IF direction_bit = 1 Then LCDOut $fe,1,"Last was CW" enc_counter = enc_counter + 1 LCDOut $fe,$c0,DEC enc_counter Else LCDOut $fe,1,"Last was CCW" enc_counter = enc_counter - 1 LCDOut $fe,$c0,DEC enc_counter EndIF GoTo start
Last edited by ice; - 4th May 2005 at 10:55.
Hi!
I need more info.
The version of the PIC used. (16F628, 16F628A, ...).
If you are using a commercial board for the PIC, I need
the URL where I can see the board.
The brand and model number of the encoder and the URL where I can
download the data sheet.
I also need to know how you have connected the encoder to
PORTB.0 and PORTB.1. (Pull-up resistors? If yes, values).
Luciano
Luciano,
im using the PIC16F873AThe version of the PIC used. (16F628, 16F628A, ...).
I made my own dev board..has an LCD,A/D..the usual features..If you are using a commercial board for the PIC, I need
the URL where I can see the board.
works on a 4Mhz crystal..
The board works perfectly as i have run accelerometers,D/A converters from the same board
Maxon 22578The brand and model number of the encoder and the URL where I can
download the data sheet.
500 counts per turn
http://www.maxonmotor.com/docsx/Down...f/05_238_e.pdf
Pull up resistorsI also need to know how you have connected the encoder to
PORTB.0 and PORTB.1. (Pull-up resistors? If yes, values).
with value 1.8K
Thanks for all your help
Last edited by ice; - 5th May 2005 at 07:39.
ok,a little progress,
seems like i'm loosing counts ,because of the LCDout commands
if i make my code tighter[remove the LCDcommands],i am able to monitor direction ,but a bit of interference still persists.
I'm on a 4Mhz..i'll switch to a 20Mhz tomorrow and post my progress
When you try the example code with the LCD, turn the encoder very slowly
with your fingers.(About 1 degree per second).The example is just to see
that the decode technique works.
For the real code you need to use the interrupt-onchange feature, and the
code has to be written in assembly otherwise you will lose encoder counts.
(Read the PicBasic Manual about interrupts in Basic and assembly).
The encoder and pull-up resistors are OK.
Luciano
yes works beautifully at 3 degrres per second,im using PWM thru a motor driver...the decode program works well
ill have to work on the interrupts..it will take time...
is there an example or "guide" code in assembly..it's a bit confusing in assembly,.
Ill try it anyway,will keep you posted.
Hello,
Probably you already know that.An interrupt in PicBasic
language will not work for the rotary encoder.
You must use an interrupt in assembly.
The assembly code for the interrupt will be simple.
In addition of what you have to save and restore
for the correct functioning of PicBasic, the assembly
code for the rotary encoder is just detect its direction
and increase or decrease the counter variable and maybe set
a flag for the direction if you need this info in the basic program.
The rest is done by the basic program once you exit the assembly
interrupt routine.
Read section 29 Instruction Set of the Mid-Range MCU Family Reference Manual.
http://ww1.microchip.com/downloads/e...Doc/33023a.pdf
To do:
Understand the differences between interrupts in PicBasic and
interrupts in assembly and understand why you need an assembly
interrupt for your rotary encoder. (PicBasic Manual).
How to declare a variable in basic and use
it in the interrupt assembly code. (Share variables
between basic and assembly, see PicBasic Manual).
Understand what you need to save and restore when
you enter and exit the assembly interrupt. (PicBasic Manual).
In PicBasic language, try first the interrupt-onchange feature.
The A/B signals of the encoder will be connected of two of
these PINs that have the interrupt-onchange feature.
(This is not the interrupt on RB0).
Once the interrupt-onchange feature works in basic then
you will be ready to implement that in assembly.
RB<7:4>, have an interrupt-onchange feature.
Four of PORTB’s pins, RB<7:4>, have an interrupt-onchange
feature. Only pins configured as inputs can
cause this interrupt to occur (i.e., any RB<7:4> pin configured
as an output is excluded from the interrupt-onchange
comparison). The input pins (of RB7:RB4) are
compared with the old value latched on the last read of
PORTB. The “mismatch” outputs of RB7:RB4 are
OR’ed together to generate the RBIF interrupt (flag
latched in INTCON<0>).
INTCON = %10001000 'Enable RBIE
INTCON.0 = 0 'Clear Port Change Interrupt Flag bit
Read section 8 of the Mid-Range MCU Family Reference Manual.
http://ww1.microchip.com/downloads/e...Doc/33023a.pdf
AN566 - Using the PORTB Interrupt on Change as an External Interrupt.
http://ww1.microchip.com/downloads/e...tes/00566b.pdf
Best regards,
Luciano
Thanks Luciano,
Im read up the pbp manual and others for interrupts...
..however my test program isnt compiling..
1> compile error "DEFINE INTHAND myint"Code:wsave VAR BYTE $20 system 'bank0 wsave1 VAR BYTE $a0 system 'bank1 'wsave2 VAR BYTE $120 system 'bank2 'wsave3 VAR BYTE $1a0 system 'bank3 ssave VAR BYTE bank0 system psave VAR BYTE bank0 system led VAR PORTB.1 GoTo start_0 ' Skip around interrupt handler DEFINE INTLHAND myint ' DEFINE INTERRUPT handler Asm myint: bsf _led ; Turn on LED (for example) bcf intcon.0 retfie EndAsm ' Assembly language INTERRUPT handler start_0: TRISB=%11111101 Low led ' Turn LED off INTCON = %10001000 ' Enable INTERRUPT ON PORTB.0 loop: GoTo loop ' Wait here till interrupted
....undefined symbol myint
however if i relpace it with DEFINE INTLHAND myint,(which is only for th 18x series),it complies ok..
2>im using a 16F873A,which has a 4K flash,meaning 4 banks...
according to the PBP manual, i have to define wsave,wsave1,wsave2,wsave3...
it takes in wsave and wsave1...but when i declare wsave2 and wsave3..it gives a RAM_END 255 error
3>i cannot get the LED to blink either by using the int0 or the Interr On change<4:7>
Last edited by ice; - 6th May 2005 at 12:14.
Hmmm..
I got the program to compile and work with interrupts on portB<4:7>
Ill try to implement the quadrature decoding in assembly now..
Example from:
http://www.microengineeringlabs.com/...manual/9_0.htm
Luciano
Code:' Assembly language interrupt example led var PORTB.1 wsave var byte $20 system ssave var byte bank0 system psave var byte bank0 system Goto start ' Skip around interrupt handler ' Define interrupt handler define INTHAND myint ' Assembly language interrupt handler asm ; Save W, STATUS and PCLATH registers myint movwf wsave swapf STATUS, W clrf STATUS movwf ssave movf PCLATH, W movwf psave ; Insert interrupt code here ; Save and restore FSR if used bsf _led ; Turn on LED (for example) ; Restore PCLATH, STATUS and W registers movf psave, W movwf PCLATH swapf ssave, W movwf STATUS swapf wsave, F swapf wsave, W retfie endasm ' PICBASIC PRO™ program starts here start: Low led ' Turn LED off ' Enable interrupt on PORTB.0 INTCON = %10010000 loop: Goto loop ' Wait here till interrupted
Thanks Luciano,
But it's the same program as in the datasheet...
Im now trying to write the decoding program in assembly..
Rotary encoder in assembly for 16F84:
http://emp.byui.edu/fisherr/EET255/encdr.asm
Luciano
Luciano,
I added the code from the lin you posted..
I'm using interrupt on INT0 connected to channel A..
I'm able to count and detect directions..with 1 big problem
i cannot monitor or dispaly the counts either using LCDout or software serial to hyperterminal.
it's good for 2 seconds and then the whole LCD screen corrupts
...is it because the LCDout line is being interrupted by the encoder
...if i disable/enable interrupts within the LCDout line..i lose encoder counts
in the code..i use 2 leds connected to portB.6,7 to show direction.
LOOP is my main program
my code is below
Code:GoTo start ' Skip around interrupt handler DEFINE INTHAND display ' DEFINE INTERRUPT handler Asm display movwf wsave swapf STATUS, W clrf STATUS movwf ssave movf PCLATH, W movwf psave ;;;bsf _led ; Turn on LED (for example) ;new=PORTB & %11000000 ;Get intitial input from ENCODER & put the value in OLD. movf PORTB,W movlw _old ;Strip off all but the last 2 LSBs in OLD. movlw 0x03 ;Create bit mask for 2 LSBs (bits 6 & 7). 11000000 andwf _old,F ;Zero bits 0 thru 6. ;Read latest input from ENCODER & put the value in NEW. movf PORTB,W movwf _new ;Strip off all but the 2 LSBs in OLD. movlw 0x03 ;Create bit mask for 2 LSBs (bits 6 & 7). 11000000 andwf _new,F ;Zero bits 0 thru 6. movf _new,W ;Move the contents of NEW to TEMP and OLD to W movwf _temp ;in order to compare them with XOR. movf _old,W xorwf _temp,F ;XOR previous inputs (in W) with latest inputs ;(in TEMP) to see if they are equal. btfsc STATUS,Z ;Test result and skip next line if 0. GoTo rtrn ;Result is 0. Previous inputs equal latest ;inputs. Rotary encoder did not move. Return ;to loop ;Result is 1. Rotary encoder moved. Determine direction. bcf STATUS,C ;Clear the carry bit in the status register and rlf _old,F ;left shift it into OLD to align bit0 of OLD ;with bit 1 of NEW. movf _new,W ;Move the contents of NEW to W in order to XOR. xorwf _old,F ;XOR previous inputs (in OLD) with latest ;inputs (in W) to determine CW or CCW. btfsc _old,1 ;Test bit 1 of result (in OLD). Skip next line ;if it is 0 (direction is CCW). GoTo Up ;Bit is 1 (direction is CW). Go around Down Down ;Decrements COUNTER because encoder moved CCW. decf _enc_counter,F bsf _led GoTo rtrn ;Branch around UP. Up ;Increments COUNTER because encoder moved CW. incf _enc_counter,F bsf _led2 ;and increment counter. rtrn bcf intcon.1 ; clear interrupt flag ; Restore PCLATH, STATUS and W registers movf psave, W movwf PCLATH swapf ssave, W movwf STATUS swapf wsave, F swapf wsave, W retfie EndAsm start: 'SerOut PORTC.0,T9600,[#enc_counter,10,13] TRISB=%00111111 TRISC=%00000000 Low led Low led2 ' Turn LED off INTCON = %10010000 ' Enable INTERRUPT ON portb INT0 option_reg=%01110000 loop: 'INTCON = %00000000 'LCDOut $fe,1,DEC enc_counter 'Pause 4 'INTCON = %10010000 Low led Low led2 GoTo loop
Last edited by ice; - 11th May 2005 at 10:41.
Hi!
If you use INT0 you will lose encoder counts.
See my Incremental quadrature encoder direction drawing.
(File encoder.zip posted the 9th April 2005, 13:44).
The two encoder signals A/B have to be connected to two of
the PINs RB<7:4> which have an interrupt-onchange feature.
* * *
When I post a code sample, the code is well formatted and
there are no dead commented statements.(I do my best).
Do you think that people on this forum will spend time
reading your code if it is not formatted?
* * *
See thread LCDOUT & interrupts.
http://www.picbasic.co.uk/forum/show...dout+interrupt
* * *
Luciano
Luciano,
I switched to portB.6 and 7 for interrupt on change and modified the code
However i cannot decode direction now.I use LEDs to display direction..Now only one of them lights for both directions.Thanks.
**
i'm sorry abt my code being all garbled.Ive done my best this time.
**
Code:movf PORTB,W movlw _old ;Strip off all but the last 2 MSBs in OLD. movlw 0xc0 andwf _old,F ;Read latest input from ENCODER & put the value in NEW. movf PORTB,W movwf _new ;Strip off all but the 2 MSBs in NEW movlw 0xc0 andwf _new,F movf _new,W ;Move the contents of NEW to TEMP and OLD to W movwf _temp ;in order to compare them with XOR. movf _old,W xorwf _temp,F ;XOR previous inputs (in W) with latest inputs ;(in TEMP) to see if they are equal. btfss STATUS,Z ;Test result and skip next line if 0. GoTo rtrn ;Result is 0. Previous inputs equal latest ;inputs. Rotary encoder did not move. Return ;to loop bcf STATUS,C ;Clear the carry bit in the status register and rlf _old,F ;left shift it into OLD to align bit6 of OLD ;with bit7 of NEW. movf _new,W ;Move the contents ofNEW to W in order to XOR. xrwf _old,F ;XOR previous inputs (in OLD) with latest ;inputs (in W) to determine CW or CCW. btfss _old,7 ;Test bit 1 of result (in OLD). Skip next line ;if it is 0 (direction is CCW). GoTo Up ;Bit is 1 (direction is CW). Go around Down Down ;Decrements COUNTER because encoder moved CCW. decf _enc_counter,F bsf _led ;light CCW LED GoTo rtrn ;Branch around UP. Up ;Increments COUNTER because encoder moved CW. incf _enc_counter,F ;and increment counter. bsf _led2 ;light CW LED rtrn bcf intcon.0 ;clear interrupt flag on portBinT
Hi,
Below is one part of the interrupt assembly code.
(I have tested the code with the MPLAB simulator).
This is not the end of the difficulties.
I can help (the forum can help) if you do the following:
Write a PicBasic program that displays on the LCD the
rotary encoder count. The program will use an
interrupt-onchange in assembly for the encoder.
(Please write a new well formatted program).
The assembly code I have posted today is only one
part of the code you will need in the interrupt routine.
BEFORE my code you will have to save the processor context.
AFTER my code you will have to restore the processor context.
See PicBasic manual '9.3. Interrupts in Assembler' for
everything else you will need in order to use an interrupt
in assembly.
For now do not modify my code. Add everything, also the
two comment lines. (Cut & Paste).
(;====== BEGINNING OF THE ROTARY ENCODER CODE ========)
(;========= END OF THE ROTARY ENCODER CODE ===========)
If you like you can add the code for the two LEDs CCW and CW.
See comments in the code for the LEDs.
In my code I use 3 variables that you will have to declare
in your PicBasic program. These variables have new names.
(Not the same names you have used in your code).
I don't like names like 'new' because 'new' can be a
reserved word for some compiler.
The 3 variables are:
enc_new VAR BYTE
enc_old VAR BYTE
enc_counter VAR WORD
Set to 0 (zero) the variable enc_old when you power up
the PIC, before you enter the main loop of your PicBasic
program. This is not necessary but it will help you to
understand how this variable is used.
* * *
From your post of the 6th May 2005, 12:06
....im using a 16F873A,which has a 4K flash,meaning 4 banks...
according to the PBP manual, i have to define wsave,wsave1,wsave2,wsave3...
it takes in wsave and wsave1...but when i declare wsave2 and wsave3..it gives a RAM_END 255 error
From the PicBasic manual
2.2. Your First Program
If you don’t tell it otherwise, the PicBasic Pro Compiler defaults to
creating code for the PIC16F84. To compile code for PICmicro MCUs
other than the PIC16F84, simply use the -P command line option
described later in the manual to specify a different target processor. For
example, if you intend to run the above program, BLINK.BAS, on a
PIC16F877, compile it using the command:
PBP -p16f877 blink
So verify what you need for the 16F873A.
Luciano
Code:;====== BEGINNING OF THE ROTARY ENCODER CODE ======== ;The Rotary Encoder is connected to PORTB ;The A signal of the encoder connected to the PIN portB.7 ;The B signal of the encoder connected to the PIN portB.6 ; ;The 3 variables used are declared in the PicBasic code. ; ; enc_new VAR BYTE ; enc_old VAR BYTE ; enc_counter VAR WORD ; ;================================================ ;Read latest input from PORTB & put the value in _enc_new. movf PORTB,W movwf _enc_new ;Strip off all but the 2 MSBs in _enc_new. movlw B'11000000' ;Create bit mask (bits 7 & 6). andwf _enc_new,F ;Zero bits 5 thru 0. ;Determine the direction of the Rotary encoder. rlf _enc_old,F ;left shift it into _enc_old to align bit 6 of ;_enc_old with bit 7 of _enc_new. movf _enc_new,W ;Move the contents of _enc_new to W in order to XOR. xorwf _enc_old,F ;XOR previous inputs (in _enc_old) with latest ;inputs (in W) to determine CW or CCW. btfsc _enc_old,7 ;Test bit 7 of result (in _enc_old). Skip next line ;if it is 0 (direction is CCW). goto Up ;Bit is 1 (direction is CW). Go around Down ;and increment counter. Down ;Decrements _enc_counter because the rotary encoder moved CCW. ;Decrements _enc_counter (16 bit value), sets Z on exit. decf _enc_counter,F ; Decrement low byte incfsz _enc_counter,W ; Check for underflow incf _enc_counter+1,F ; Update decf _enc_counter+1,F ; Fixup movf _enc_counter,W iorwf _enc_counter+1,W ; Set Z bit ;Add here code for the CCW LED if needed. goto Continue ;Branch around UP. Up ;Increments _enc_counter because the rotary encoder moved CW. ;Increments _enc_counter (16 bit value), sets Z on exit. incfsz _enc_counter,W ; Add one to low byte decf _enc_counter+1,F ; No carry (negates next step) incf _enc_counter+1,F ; Add one to high byte movwf _enc_counter ; Store updated low byte back. iorwf _enc_counter+1,W ; Set Z flag ;Add here code for the CW LED if needed. Continue ;Assign the latest encoder inputs (in _enc_new) to _enc_old. movf _enc_new,W movwf _enc_old ;============ END OF THE ROTARY ENCODER CODE =====
Last edited by Luciano; - 12th May 2005 at 22:20.
Thank you so much Luciano...
The code is..
working good ,
direction decoding ok
counting on LCD[no garbage,dispays properly]
A few questions
1>You never clear the Int on PortB change flag in the interrupt.is it because your reading from portB,which is one of the ways to clear the flag.
2>retfie.. Neither is the retfie used to return from where the program left.
3>why didnt my program work ,when i used the INT0 interrupt.I cant figure it out.
4>I use CodeDesigner Lite as an IDE.I get t0 select the chip from a drop down menu.I select 16f873a.I even put the line PBP -p16F873A in the command line option,but the error on using wsave2 and wsave3,still persists.
Working with enc_counter on 16 bits is a bit difficult to work on the current project.I need 32 bits.so back to my code.
====================================
Question 1
(From data sheet PIC16F87XA).
INTCON REGITER
bit 0 RBIF: RB Port Change Interrupt Flag bit
1 = At least one of the RB7:RB4 pins changed state; a mismatch condition will continue to set
the bit. Reading PORTB will end the mismatch condition and allow the bit to be cleared
(must be cleared in software).
0 = None of the RB7:RB4 pins have changed state
* * *
Yes, you must clear RBIF in software.
======================================
Question 2
Yes, you need 'retfie' before 'endasm'.
Just do what is in the PicBasic Pro manual.
======================================
Question 3
The encoder outputs that in CW direction:
Step 1 A=0 B=0
Step 2 A=1 B=0
Step 3 A=1 B=1
Step 4 A=0 B=1
If you use the interrupt INT0, you can monitor
only one signal of the encoder. If you monitor
the encoder signal A with the PIN RB0 (INT0) the
interrupt is raised when the encoder goes from
step 1 to step 2. (0 to 1).
From step 2 to step 3 the interrupt is not raised
because the encoder signal A is '1' in step 2 and
also '1' in step 3.
======================================
Question 4
You will have to fix that. This is a ticking bomb.
I don't use PicBasic. I cannot answer this question.
Do what I told you in my last post and ask the forum
for help.
======================================
From your last post:
I need 32 bits.so back to my code.
* * *
The PicBasic Pro compiler only supports a maximum
variable size of 16 bits.
What kind of 32-bit operations do you need?
Best regards,
Luciano
Luciano,
Thanks for answering the questions
when i use "retfie" to return to the main program from where it left..the PIC "freezes" and both the LEDs light up.
It's all good when i dont use retfie
I can count only from 0 to 65535 with 16bits..which is not enough.hence i need 32 bit addition.
ICE
I may have another solution for you (or then again not). Here is a link to something I have been working on which utilizes a timer interrupt to sample an encoder, and then increment or decrement a 6 digit counter (includes prescaler). Since everything to do with sampling and updating are via the assembly interrupt there is no danger of missing pulses. Also picking the minimum sampling frequency necessary for a particular application tends to add additional debounce to the encoder inputs. And last but not least, using the byte per digit counter approach allows for any counter length desired (not limited to a 16 bit word to hold your value).
Check it out:https://www.picbasic.co.uk/forum/showthread.php?t=1886
(Scroll down towards the bottom)
Hello ICE, hi Luciano, hello everyone.
I try to use an PIC 18F452 with the Code posted from ICE and Luciano. The Code I have posted does not work and I dont understand wy but to tell the truth: I never tried to use Assembler and I dont have experience with Interrupts so maybe I´v done totaly... but maybe someone can help me?!
The Quad-Encoder is connected to portB.6 and 7.
Is the way I saved and restored the Registers right?
At the Assembler Code I changed the "rlf" statement to "rlcf" is this OK (I think the 18F452 do not support rlf)?
Is the declaration of the variables for the assembler code right?
Sorry for the spacing at the code, but I dont know how to remove it... :-(
Thanks in advance,
Jan
<div class="smallfont" style="margin-bottom:2px">Code:</div>
<pre style="margin:0px; padding:2px; border:1px inset; width:640px; height:434px; overflow:auto">DEFINE OSC 40
'hserout requires an rs-232 driver
'Set transmit register to transmitter enabled (normal value "20h" / highSpeed "24h")
DEFINE HSER_TXSTA 24h
'Set receive register to to receiver enabled
DEFINE HSER_RCSTA 90h
'Set baud rate
DEFINE HSER_BAUD 19200
'---VARIABLES required for the ASSAMBLER code
wsave var byte $20 system
wsave1 var byte $a0 system ' Necessary for devices with RAM in bank1
wsave2 var byte $120 system ' Necessary for devices with RAM in bank2
wsave3 var byte $1a0 system ' Necessary for devices with RAM in bank3
ssave var byte bank0 system
psave var byte bank0 system
enc_old VAR BYTE
enc_new VAR BYTE
enc_counter VAR WORD
'---Other Vriables
enc_counter_old VAR WORD
maxcount con 1800
startcount con 1000
led0 VAR PORTA.0 'leds for showing the direction
led1 VAR PORTA.1
led2 VAR PORTA.2
enc_counter = startcount
TRISA = %00000000 ' Makes the pins of PortA output
TRISB = %11111111 ' Makes the pins of PortB input
GOTO start
' Define interrupt handler
define INTHAND myint
' Assembly language interrupt handler
Asm
; Save W, STATUS and PCLATH registers, if not done previously
myint movwf wsave
swapf STATUS, W
clrf STATUS
movwf ssave
movf PCLATH, W
movwf psave
;====== BEGINNING OF THE ROTARY ENCODER CODE ========
;The Rotary Encoder is connected to PORTB
;The A signal of the encoder connected to the PIN portB.7
;The B signal of the encoder connected to the PIN portB.6
;
;The 3 variables used are declared in the PicBasic code.
;
; enc_new VAR BYTE
; enc_old VAR BYTE
; enc_counter VAR WORD
;
;================================================
;Read latest input from PORTB & put the value in _enc_new.
movf PORTB,W
movwf _enc_new
;Strip off all but the 2 MSBs in _enc_new.
movlw B'11000000' ;Create bit mask (bits 7 & 6).
andwf _enc_new,F ;Zero bits 5 thru 0.
;Determine the direction of the Rotary encoder.
rlcf _enc_old,F ;was rlf - left shift it into _enc_old to align bit 6 of _enc_old with bit 7 of _enc_new.
movf _enc_new,W ;Move the contents of _enc_new to W in order to XOR.
xorwf _enc_old,F ;XOR previous inputs (in _enc_old) with latest inputs (in W) to determine CW or CCW.
btfsc _enc_old,7 ;Test bit 7 of result (in _enc_old). Skip next line if it is 0 (direction is CCW).
goto Up ;Bit is 1 (direction is CW). Go around Down
;and increment counter.
Down
;Decrements _enc_counter because the rotary encoder moved CCW.
;Decrements _enc_counter (16 bit value), sets Z on exit.
decf _enc_counter,F ; Decrement low byte
incfsz _enc_counter,W ; Check for underflow
incf _enc_counter+1,F ; Update
decf _enc_counter+1,F ; Fixup
movf _enc_counter,W
iorwf _enc_counter+1,W ; Set Z bit
;code for the CCW LED.
bcf _led0 ; If interrupt, turn off LED
goto Continue ;Branch around UP.
Up
incfsz _enc_counter,W ; Add one to low byte
decf _enc_counter+1,F ; No carry (negates next step)
incf _enc_counter+1,F ; Add one to high byte
movwf _enc_counter ; Store updated low byte back.
iorwf _enc_counter+1,W ; Set Z flag
;code for the CW LED.
bcf _led1 ; If interrupt, turn off LED
Continue
;Assign the latest encoder inputs (in _enc_new) to _enc_old.
movf _enc_new,W
movwf _enc_old
; Restore saved registers
movf psave, W
movwf PCLATH
swapf ssave, W
movwf STATUS
swapf wsave, F
swapf wsave, W
retfie ; Return from interrupt
endasm
start:
INTCON = %10001000 ' Enable INTERRUPT "on change" ON PORTB and clears FLAGS
pause 30
portA = %11111111
HSEROUT [#enc_counter,10,13] 'only for debugging
loop:
if enc_counter_old <> enc_counter then 'looks if counter has changed
HSEROUT [#enc_counter,10,13]
enc_counter_old = enc_counter
endif
goto loop
END
</pre>
Last edited by jmen; - 4th August 2005 at 08:05.
Bookmarks