godfodder
- 2nd October 2008, 08:49
Hi all.
I've been lurking around here for about a month now learning my way to getting up to speed on pic programming, I have done numerous basicX and stamp projects in the past, but since discovering the speed and ability of direct programming, the time of stamps is in the way past now. Want to say it's amazing how helpful these forums are, armed with the power of search
there is no reason why anyone can't be up and running in a flash...
So that aside here's my first roadblock, I would like to get up to speed in using asm interrupts
decided to make a project that utilizes a rotary quadrature encoder, (panel type) it's not going to be machine driven, just for human interaction. after searching and discovering numerous threads about multiple ways to read the encoder, I wanted to try what looks like an elegant method of reading the port change interrupt on RB<7:4> to change a variable in the background letting me just check that variable in the main program loop, to display on a basic parallel lcd. so I will first describe my setup/pic/ect.. then explain what issues I am experiencing. Thanks in advance for any help or insight..
I am using a PIC16F876A
Here is my hardware wiring.
( 1) MCLR --> 4.7k --> 5v+
( 2) RA0 --> LCD Data
( 3) RA1 --> LCD Data
( 4) RA2 --> LCD Data
( 5) RA3 --> LCD Data
( 6) RA4 -->
( 7) RA5 -->
( 8) Vss --> Gnd
( 9) OSC1 --> 20mhz Osc
(10) OSC2 --> 20mhz Osc
(11) RC0 --> Status Led1
(12) RC1 -->
(13) RC2 --> Status Led2
(14) RC3 -->
(15) RC4 -->
(16) RC5 -->
(17) RC6 -->
(18) RC7 --> LCD Backlight
(19) Vss --> Gnd
(20) Vdd --> 5v+ --> .1uf Cap --> Gnd
(21) RB0 --> Encoder PushButton --> 10k --> 5v+
(22) RB1 --> LCD RS Bit
(23) RB2 --> LCD Enable Bit
(24) RB3 -->
(25) RB4 -->
(26) RB5 -->
(27) RB6 --> Encoder Phase A --> 10k --> 5v+
(28) RB7 --> Encoder Phase B --> 10k --> 5v+
I have been reading thru the following threads to learn as much as I can about using
various ways to read an encoder.
http://www.picbasic.co.uk/forum/showthread.php?t=1552
Ive based my ISR on Lucianos code from the above thread. (Thanks!!!)
also have looked at using timer interrupts for encoder reading
http://www.picbasic.co.uk/forum/showthread.php?t=1886
I have plans later for the timer, so going to try to stick with portB change for now. (baby steps)
I've also found some other code (non asm, just picbasic) that does a pretty good job
can't seem to find the post at the moment.. but it had a real wierd issue, going one direction
I would miss every 3rd step, but the other way it was perfect..
I know I don't really need the precision that an asm interrupt has for a slow turning encoder,
but would also like to use this oppertunity to learn how to do it efficiently.
so first here is my code...
'problem code
'setup PIC Config Fuses
@ Device pic16F876A, HS_OSC, BOD_OFF, PWRT_ON, WDT_OFF, PROTECT_OFF
'************************ DEFINES HERE *************************************
DEFINE OSC 20 ' set to 20mhz
DEFINE LCD_DREG PORTA ' Set Data Registers port
DEFINE LCD_DBIT 0 ' Set starting data bit
DEFINE LCD_RSREG PORTB ' Set LCD RS Port
DEFINE LCD_RSBIT 1 ' Set LCD RS Bit
DEFINE LCD_EREG PORTB ' Set LCD Enable Port
DEFINE LCD_EBIT 2 ' Set LCD Enable Bit
DEFINE LCD_BITS 4 ' Set LCD 4bit Mode
DEFINE LCD_LINES 2 ' Set number of LCD Lines
DEFINE LCD_COMMANDUS 2000 ' Set Command Delay time in uS
DEFINE LCD_DATAUS 60 ' Set Data delay time in uS
clear 'clear out variables
'*********************** CHIP REGISTER SETUP *******************************
ADCON0 = 7
ADCON1 = 7 ' turn off analog porta, set to digital IO
CMCON = 7 ' Turn off Port A Comparator
TRISA = 0 ' output ports
TRISB = %11111001 ' set input and output ports
TRISC = 0 ' output ports
'************************ PROGRAM VARIABLES HERE ***************************
symbol led = portc.2 ' status led
symbol led2 = portc.0 ' status led2
symbol lcdbkl = portc.7 ' lcd pannel backlight
symbol sw1 = portb.0 ' encoder switch
enc_old VAR BYTE
enc_new VAR BYTE
enc_counter VAR WORD
enc_counter_old VAR WORD
'*********************** ASSEMBLY INTERUPT VARIABLES ***********************
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
goto start 'skip over interupt handler
'*********************** ASSEMBLY INTERUPT HANDLER *************************
define INTHAND myint
Asm
myint
; BCF INTCON,GIE ;Should I have this????
; Save W, STATUS and PCLATH registers, if not done previously
movwf wsave
swapf STATUS, W
clrf STATUS
movwf ssave
movf PCLATH, W
movwf psave
movlw high 100 ; Wait 100us to debounce encoder
movwf R0 + 1
movlw low 500
call PAUSEUSL
;====== 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 0xc0 ;Create bit mask (bits 7 & 6). b'11000000' ?
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.
bsf _led ;turn on led
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.
bsf _led2 ;turn on 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
bcf INTCON, RBIF ; Clear the Interupt Flag
RETFIE ; Return from interrupt
endasm
'************************************************* **************************
'************************* PROGRAM STARTS HERE *****************************
'************************************************* **************************
START: ' Main Program starts here
LCDOUT $FE,1 'Init The LCD
pause 500 'wait for LCD to start
lcdout $FE,1,"ENCODER" 'display splash screen
LCDOUT $FE,$C0," TEST "
high lcdbkl ' turn on backlight
'************************** SET DEFAULT SETTINGS HERE **********************
pause 2000 ' just wait a bit to read splash screen
enc_counter = 1024 ' set default encoder value
enc_counter_old = 1024
lcdout $FE,1,"ENCODER" ' change display
INTCON = %10001000 ' Enable PortB Change Interupts
'************************** TESTING ROUTINES HERE **************************
test:
if enc_counter <> enc_counter_old then 'see if value has changed
enc_counter_old = enc_counter 'move new value to old
lcdout $fe,$C0, DEC5 enc_counter 'display enc_counter value
low led 'turn off CCW led
low led2 'turn off CW led
endif
goto test
ok, questions I have.. [oops post is getting too long... see next post]
I've been lurking around here for about a month now learning my way to getting up to speed on pic programming, I have done numerous basicX and stamp projects in the past, but since discovering the speed and ability of direct programming, the time of stamps is in the way past now. Want to say it's amazing how helpful these forums are, armed with the power of search
there is no reason why anyone can't be up and running in a flash...
So that aside here's my first roadblock, I would like to get up to speed in using asm interrupts
decided to make a project that utilizes a rotary quadrature encoder, (panel type) it's not going to be machine driven, just for human interaction. after searching and discovering numerous threads about multiple ways to read the encoder, I wanted to try what looks like an elegant method of reading the port change interrupt on RB<7:4> to change a variable in the background letting me just check that variable in the main program loop, to display on a basic parallel lcd. so I will first describe my setup/pic/ect.. then explain what issues I am experiencing. Thanks in advance for any help or insight..
I am using a PIC16F876A
Here is my hardware wiring.
( 1) MCLR --> 4.7k --> 5v+
( 2) RA0 --> LCD Data
( 3) RA1 --> LCD Data
( 4) RA2 --> LCD Data
( 5) RA3 --> LCD Data
( 6) RA4 -->
( 7) RA5 -->
( 8) Vss --> Gnd
( 9) OSC1 --> 20mhz Osc
(10) OSC2 --> 20mhz Osc
(11) RC0 --> Status Led1
(12) RC1 -->
(13) RC2 --> Status Led2
(14) RC3 -->
(15) RC4 -->
(16) RC5 -->
(17) RC6 -->
(18) RC7 --> LCD Backlight
(19) Vss --> Gnd
(20) Vdd --> 5v+ --> .1uf Cap --> Gnd
(21) RB0 --> Encoder PushButton --> 10k --> 5v+
(22) RB1 --> LCD RS Bit
(23) RB2 --> LCD Enable Bit
(24) RB3 -->
(25) RB4 -->
(26) RB5 -->
(27) RB6 --> Encoder Phase A --> 10k --> 5v+
(28) RB7 --> Encoder Phase B --> 10k --> 5v+
I have been reading thru the following threads to learn as much as I can about using
various ways to read an encoder.
http://www.picbasic.co.uk/forum/showthread.php?t=1552
Ive based my ISR on Lucianos code from the above thread. (Thanks!!!)
also have looked at using timer interrupts for encoder reading
http://www.picbasic.co.uk/forum/showthread.php?t=1886
I have plans later for the timer, so going to try to stick with portB change for now. (baby steps)
I've also found some other code (non asm, just picbasic) that does a pretty good job
can't seem to find the post at the moment.. but it had a real wierd issue, going one direction
I would miss every 3rd step, but the other way it was perfect..
I know I don't really need the precision that an asm interrupt has for a slow turning encoder,
but would also like to use this oppertunity to learn how to do it efficiently.
so first here is my code...
'problem code
'setup PIC Config Fuses
@ Device pic16F876A, HS_OSC, BOD_OFF, PWRT_ON, WDT_OFF, PROTECT_OFF
'************************ DEFINES HERE *************************************
DEFINE OSC 20 ' set to 20mhz
DEFINE LCD_DREG PORTA ' Set Data Registers port
DEFINE LCD_DBIT 0 ' Set starting data bit
DEFINE LCD_RSREG PORTB ' Set LCD RS Port
DEFINE LCD_RSBIT 1 ' Set LCD RS Bit
DEFINE LCD_EREG PORTB ' Set LCD Enable Port
DEFINE LCD_EBIT 2 ' Set LCD Enable Bit
DEFINE LCD_BITS 4 ' Set LCD 4bit Mode
DEFINE LCD_LINES 2 ' Set number of LCD Lines
DEFINE LCD_COMMANDUS 2000 ' Set Command Delay time in uS
DEFINE LCD_DATAUS 60 ' Set Data delay time in uS
clear 'clear out variables
'*********************** CHIP REGISTER SETUP *******************************
ADCON0 = 7
ADCON1 = 7 ' turn off analog porta, set to digital IO
CMCON = 7 ' Turn off Port A Comparator
TRISA = 0 ' output ports
TRISB = %11111001 ' set input and output ports
TRISC = 0 ' output ports
'************************ PROGRAM VARIABLES HERE ***************************
symbol led = portc.2 ' status led
symbol led2 = portc.0 ' status led2
symbol lcdbkl = portc.7 ' lcd pannel backlight
symbol sw1 = portb.0 ' encoder switch
enc_old VAR BYTE
enc_new VAR BYTE
enc_counter VAR WORD
enc_counter_old VAR WORD
'*********************** ASSEMBLY INTERUPT VARIABLES ***********************
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
goto start 'skip over interupt handler
'*********************** ASSEMBLY INTERUPT HANDLER *************************
define INTHAND myint
Asm
myint
; BCF INTCON,GIE ;Should I have this????
; Save W, STATUS and PCLATH registers, if not done previously
movwf wsave
swapf STATUS, W
clrf STATUS
movwf ssave
movf PCLATH, W
movwf psave
movlw high 100 ; Wait 100us to debounce encoder
movwf R0 + 1
movlw low 500
call PAUSEUSL
;====== 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 0xc0 ;Create bit mask (bits 7 & 6). b'11000000' ?
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.
bsf _led ;turn on led
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.
bsf _led2 ;turn on 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
bcf INTCON, RBIF ; Clear the Interupt Flag
RETFIE ; Return from interrupt
endasm
'************************************************* **************************
'************************* PROGRAM STARTS HERE *****************************
'************************************************* **************************
START: ' Main Program starts here
LCDOUT $FE,1 'Init The LCD
pause 500 'wait for LCD to start
lcdout $FE,1,"ENCODER" 'display splash screen
LCDOUT $FE,$C0," TEST "
high lcdbkl ' turn on backlight
'************************** SET DEFAULT SETTINGS HERE **********************
pause 2000 ' just wait a bit to read splash screen
enc_counter = 1024 ' set default encoder value
enc_counter_old = 1024
lcdout $FE,1,"ENCODER" ' change display
INTCON = %10001000 ' Enable PortB Change Interupts
'************************** TESTING ROUTINES HERE **************************
test:
if enc_counter <> enc_counter_old then 'see if value has changed
enc_counter_old = enc_counter 'move new value to old
lcdout $fe,$C0, DEC5 enc_counter 'display enc_counter value
low led 'turn off CCW led
low led2 'turn off CW led
endif
goto test
ok, questions I have.. [oops post is getting too long... see next post]