Remove Text Formatting
Loading...

+ Reply to Thread
Page 1 of 3 123 LastLast
Results 1 to 40 of 94
  1. #1
    ice's Avatar
    ice Guest

    Default decoding quadrature encoders

    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

  2. #2
    Join Date
    Oct 2003
    Location
    holland
    Posts
    251

    Default

    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

  3. #3
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  4. #4
    ice's Avatar
    ice Guest

    Default

    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


    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
    with the above code...there is alot of fluctuation between the 2 directions..

    ..anything going wrong in my code?.i sat on it the whole day..still cant figure out the problem

  5. #5
    Join Date
    Oct 2003
    Location
    holland
    Posts
    251

    Default

    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
    Attached Files Attached Files

  6. #6
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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.

  7. #7
    ice's Avatar
    ice Guest

    Default

    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.

  8. #8
    Join Date
    Oct 2003
    Location
    holland
    Posts
    251

    Default

    No, this small programm reacts as a D flipflop.
    see attached drawing what happens
    Attached Images Attached Images  

  9. #9
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  10. #10
    Join Date
    Sep 2004
    Location
    montreal, canada
    Posts
    6,898

    Default

    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.

  11. #11
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    File attachment = Incremental quadrature encoder direction drawing.

    Luciano
    Attached Files Attached Files
    Last edited by Luciano; - 9th April 2005 at 15:03.

  12. #12
    ice's Avatar
    ice Guest

    Default

    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]
    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
    ...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..

    Im still working on it..and will keep u all posted...

    monitor the 'value' variable. or modify it easily to have CW and CCW flag in one shot....
    Thanks for the link M_e
    ..i'll be doing tht to take care of the above problem

  13. #13
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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.

  14. #14
    ice's Avatar
    ice Guest

    Default

    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.

  15. #15
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    I don't understand.
    If you need help post your code.

    Luciano

  16. #16
    ice's Avatar
    ice Guest

    Default

    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

  17. #17
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  18. #18
    ice's Avatar
    ice Guest

    Default

    ok,heres my program ,using interrupts..

    the problems i'm encountering are below the code
    ..
    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
    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..

    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.

  19. #19
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  20. #20
    ice's Avatar
    ice Guest

    Default

    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.

    Code:
    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
    Thank you for your responses
    Last edited by ice; - 4th May 2005 at 10:55.

  21. #21
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  22. #22
    ice's Avatar
    ice Guest

    Default

    Luciano,

    The version of the PIC used. (16F628, 16F628A, ...).
    im using the PIC16F873A

    If you are using a commercial board for the PIC, I need
    the URL where I can see the board.
    I made my own dev board..has an LCD,A/D..the usual features..
    works on a 4Mhz crystal..
    The board works perfectly as i have run accelerometers,D/A converters from the same board


    The brand and model number of the encoder and the URL where I can
    download the data sheet.
    Maxon 22578
    500 counts per turn
    http://www.maxonmotor.com/docsx/Down...f/05_238_e.pdf

    I also need to know how you have connected the encoder to
    PORTB.0 and PORTB.1. (Pull-up resistors? If yes, values).
    Pull up resistors
    with value 1.8K


    Thanks for all your help
    Last edited by ice; - 5th May 2005 at 07:39.

  23. #23
    ice's Avatar
    ice Guest

    Default progress

    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

  24. #24
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  25. #25
    ice's Avatar
    ice Guest

    Default

    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.

  26. #26
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  27. #27
    ice's Avatar
    ice Guest

    Default

    Thanks Luciano,
    Im read up the pbp manual and others for interrupts...
    ..however my test program isnt compiling..

    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
    1> compile error "DEFINE INTHAND myint"
    ....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.

  28. #28
    ice's Avatar
    ice Guest

    Default

    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..

  29. #29
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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

  30. #30
    ice's Avatar
    ice Guest

    Default

    Thanks Luciano,
    But it's the same program as in the datasheet...
    Im now trying to write the decoding program in assembly..

  31. #31
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    Rotary encoder in assembly for 16F84:
    http://emp.byui.edu/fisherr/EET255/encdr.asm

    Luciano

  32. #32
    ice's Avatar
    ice Guest

    Default interrupts are interrupting LCDout

    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.

  33. #33
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Thumbs down

    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

  34. #34
    ice's Avatar
    ice Guest

    Default

    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

  35. #35
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    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.

  36. #36
    ice's Avatar
    ice Guest

    Thumbs up Working!!

    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.

  37. #37
    Join Date
    Oct 2004
    Location
    Italy
    Posts
    695

    Default

    ====================================

    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

  38. #38
    ice's Avatar
    ice Guest

    Default

    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.

  39. #39
    mytekcontrols's Avatar
    mytekcontrols Guest

    Lightbulb Here is another Encoder/Counter example

    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:http://www.picbasic.co.uk/forum/showthread.php?t=1886
    (Scroll down towards the bottom)

  40. #40
    jmen's Avatar
    jmen Guest

    Default quadrature decoding with a 18F452

    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.

Similar Threads

  1. Quick thoughts: DT Ints with encoders
    By kevlar129bp in forum mel PIC BASIC Pro
    Replies: 19
    Last Post: - 7th January 2010, 01:01
  2. PMDC SERVO MOTOR WITH quadrature encoder DRIVE ?
    By phoenix_1 in forum Schematics
    Replies: 37
    Last Post: - 22nd November 2009, 19:45
  3. 32-bit Quadrature Counter With Serial Interface
    By precision in forum mel PIC BASIC Pro
    Replies: 4
    Last Post: - 10th June 2008, 02:49
  4. quad encoders
    By cpayne in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 13th March 2007, 17:49
  5. "momentary" IR decoding
    By sporker in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 20th June 2005, 01:53

Members who have read this thread : 45

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts