Here's a simple interrupt driven routine to increment and decrement a counter using an Optical Rotary Encoder, the one used for this example is a Bournes 256 cycle per revolution unit. The code is not complex, but I have added a lot of symbol declares for explanation. Many can be removed, see comments. Also, I added lots of comments to the code to assist. Examples are provided for a volume type control that counts up and down to a predetermined value and stops at the limits until the direction is reversed. The other is for a clock over type of counter.
Hope you find it useful. I am sure it can be used with many encoders.
'************************************************* ************************************************
'* Bournes Optical Encoder with PICŪ 18F452 or possibly ANY 18F PICŪ. *
'* Uses Hardware Interrupt on PortB.1 with a Bournes 256 Cycle per rev encoder *
'* Joe Hanley (Captainslarty at yahoo.co.uk) *
'* Feb 2009 *
'* WE ARE USING PORTB.1 for the interupt, and PORTB.3 for the other signal *
'* THIS FREES UP THE OTHER INTERRUPT PINS FOR OTHER USES.. *
'************************************************* ************************************************
Device 18F452
XTAL 20
Include "proton18_g20joe.int" ' GLCD Routines only... add your own here.
' Here are our register symbols that include the Interrupts in use
' we are only using a limited few, but the COMPLETE register contents are listed for information
' If you are new to Interrupts, dont be put of by this huge list. !
' It could be written as just ....
'Symbol IPEN = RCON.7 ' Interrupt Priority Enable bit
'Symbol PEIE = INTCON.6 ' Peripheral Interrupt Enable
'Symbol GIE = INTCON.7 ' Global Interrupt Enable
'Symbol INT1IF = INTCON3.0 ' INT1 External Interrupt Flag 1 = The INT1 external interrupt occurred (must be cleared in software)
'Symbol INT1IE = INTCON3.3 ' INT1 External Interrupt Enable 1 = Enables the INT1 external interrupt PortB1
'Symbol INT1IP = INTCON3.6 ' INT1 External Interrupt Priority bit 1 = High priority PortB1
' and nothing more is needed - you could delete all the other symbol declares.
' But for a fuller explanations, here are the contents of all the register used, see the datasheet for your device.
Symbol NOT_BOR = RCON.0 ' Brown-out Reset Status bit
Symbol NOT_POR = RCON.1 ' Power-on Reset Status bit
Symbol NOT_PD = RCON.2 ' Power-down Detection Flag bit
Symbol NOT_TO = RCON.3 ' Watchdog Time-out Flag bit
Symbol RI = RCON.4 ' RESET Instruction Flag bit
Symbol IPEN = RCON.7 ' Interrupt Priority Enable bit
'1 = Enable priority levels on interrupts
Symbol RBIF = INTCON.0 ' RB Port Interrupt Flag
Symbol INT0IF = INTCON.1 ' INT0 External Interrupt Flag
'1 = The INT0 external interrupt occurred (must be cleared in software) PortB0 ALways HIGH
'0 = The INT0 external interrupt did not occur PortB0
Symbol TMR0IF = INTCON.2 ' TMR0 Overflow Interrupt Flag
Symbol RBIE = INTCON.3 ' RB Port Change Interrupt Enable
Symbol INT0IE = INTCON.4 ' INT0 External Interrupt Enable
'1 = Enables the INT0 external interrupt PortB0 Alwyas HIGH
'0 = Disables the INT0 external interrupt PortB0
Symbol T0IE = INTCON.5 ' TMR0 Overflow Interrupt Enable
Symbol PEIE = INTCON.6 ' Peripheral Interrupt Enable same as GIEL
Symbol GIEL = INTCON.6 ' Low priority peripheral interrupt
'When IPEN = 1:
'1 = Enables all low priority peripheral interrupts
'0 = Disables all low priority peripheral interrupts
Symbol GIEH = INTCON.7 ' Enable all high priority interrupts same as GIEH
Symbol GIE = INTCON.7 ' Global Interrupt Enable
'When IPEN = 1:
'1 = Enables all high priority interrupts
'0 = Disables all interrupts
Symbol RBIP = INTCON2.0 ' RB Port Change Interrupt Priority bit 1 high 0 low
Symbol INT3IP = INTCON2.1 ' unimplemented - read as 0
Symbol TMR0IP = INTCON2.2 ' TMR0 Overflow Interrupt Priority bit 1 High 0 Low
Symbol INTEDG3 = INTCON2.3 ' Unimplemented - read as 0
Symbol INTEDG2 = INTCON2.4 ' External Interrupt2 Edge Select bit PortB2 edge Rising If 1 falling if 0
Symbol INTEDG1 = INTCON2.5 ' External Interrupt1 Edge Select bit PortB1 edge Rising If 1 falling if 0
Symbol INTEDG0 = INTCON2.6 ' External Interrupt0 Edge Select bit PortB0 edge Rising if 1 falling if 0 Always High Priority
Symbol NOT_RBPU = INTCON2.7 ' PORTB Pull-up Enable bit - 1 disabled - 0 PORTB pull-ups are enabled by individual port latch values
Symbol INT1IF = INTCON3.0 ' INT1 External Interrupt Flag 1 = The INT1 external interrupt occurred (must be cleared in software)
Symbol INT2IF = INTCON3.1 ' INT2 External Interrupt Flag 1 = The INT2 external interrupt occurred (must be cleared in software)
Symbol INT3IF = INTCON3.2 ' unimplemented - Read As 0
Symbol INT1IE = INTCON3.3 ' INT1 External Interrupt Enable 1 = Enables the INT1 external interrupt PortB1
Symbol INT2IE = INTCON3.4 ' INT2 External Interrupt Enable 1 = Enables the INT2 external interrupt PortB2
Symbol INT3IE = INTCON3.5 ' unimplemented - Read As 0
Symbol INT1IP = INTCON3.6 ' INT1 External Interrupt Priority bit 1 = High priority PortB1
Symbol INT2IP = INTCON3.7 ' INT2 External Interrupt Priority bit 1 = High priority PortB2
' Interrupt on port b 0 to 2 - we will use portb.1
' use pin b1 (we can set priorities on B1 and B2 , portB0 is ALWAYS high priority.)
' to set up the interrupt... for PortB.1 high priority..
INT1IP = 1 ' High Priority interrupt on PortB.1
INTEDG1 = 1 ' rising edge on PortB.1
' enable the Interrupt 1
INT1IE = 1
' Flags for the interrupts are ...
INT1IF = 0 ' make sure its clear to start with.
'set up any variables
' If you use a Byte for the 2 items below, then the MAX count is 0 to 255, a word allows 0 to 65535
' with the conditioning in the Interrupt service routine you can limit the count to any vlaue within the range - see notes there.
Dim Count_Encoder As Word ' encoder counter - use byte word or anything to suit your purpose. !
Dim Encoder_Now As Word ' this is the interim storage for the value from the interrupt, must be same type as above
' We have hardware Interrupts
ON_INTERRUPT GoTo ISR ' hardware interrupt
' Jump over the Interrupt Service Routine
GoTo Over_Interrupt
'Interrupt Routine
ISR: ' Interrupt Service Routine
Context SAVE ' we are using an 18 series. SOMETIMES you do not HAVE
' to use this command with 18, check the datasheet
' Here yo DO, not using it will cause GLCD corruption at high rotation speed
' check PortB1 INT1IF, if = 1 then THIS was the interrupt
If INT1IF<>1 Then Get_Out ' it wasn't us ! so clear off
If PORTB.3=1 Then Count_Up ' if the other pin is 1 then up, otherwise down....
Dec Count_Encoder
' Here is some conditioning for the total value of the counter.
' lets say we want a maximum reading of 1024.... a Word is 0 to 65535
' so when re reach 65535 we have clocked down from 0, so reset to your desired max count
' this will act like a tuning knob, clocking over when the count is reached.
' in other circumstance you may want to use the encoder like a volume control
' where a stop is reached, so if its 0 and you continue turning anticlcokwise it stays at 0
' and when the count is reached, and you continue turning clockwise, it remains at that count.
' here it is set as an example for the volume control type with a range of 0 to 1024
' un rem the other lines to try the clockover type, and rem out the volume lines.
'If Count_Encoder = 65535 Then Count_Encoder= 1024 ' clock over counter type
If Count_Encoder <= 0 Then Count_Encoder= 0 ' volume control type of count
GoTo End_Interrupt
Count_Up:
Inc Count_Encoder
' the other part of the conditioning, we can use any value from 0 to 65535
' make sure that both parts of the conditioning relate to the same value !!!!
' If you wanted a maximum count of 10000 then change the 1024 value to 10000
'If Count_Encoder > 1024 Then Count_Encoder = 0 ' clock over counter type
If Count_Encoder >=1024 Then Count_Encoder = 1024 ' volume control type
End_Interrupt:
INT1IF = 0 ' clear flag
Get_Out:
Context Restore
Over_Interrupt: ' setup the ports, initialise variables and display, enable the interrupts.
' the encoder A - B output is A = PortB.1 , B = PortB.3
' we dont need a trisb becuase the startup is all input
' leaving the whole ports as inputs is safer so you dont accidently
' connect your encoder output to a PICŪ output !
Count_Encoder = 0 ' reset the variable to zero to start with
' you can preset this to a value to use if you like, to form a starting point
DelayMS 200 ' settle period
Cls ' Clear the lcd display
IPEN = 1 ' Enable Priorities for interrupts
PEIE = 1 ' Enable Peripheral interrupts
GIE = 1 ' Global Interrupt Enable
GoTo Display_0_at_startup ' make sure we have something on the display at startup
' Main Program Loop
While 1=1
If Encoder_Now <> Count_Encoder Then ' no need to update the diplay unless anything changed
Encoder_Now = Count_Encoder
Display_0_at_startup:
Print At 0,0,Dec,Encoder_Now," " ' Display the result
EndIf
' Do other things as you require
Wend
' End of main program loop
End ' We should NEVER get here, but just in case
Include "font.inc" ' Font for our GLCD, make it an internal font
'* Bournes Optical Encoder with PICŪ 18F452 or possibly ANY 18F PICŪ. *
'* Uses Hardware Interrupt on PortB.1 with a Bournes 256 Cycle per rev encoder *
'* Joe Hanley (Captainslarty at yahoo.co.uk) *
'* Feb 2009 *
'* WE ARE USING PORTB.1 for the interupt, and PORTB.3 for the other signal *
'* THIS FREES UP THE OTHER INTERRUPT PINS FOR OTHER USES.. *
'************************************************* ************************************************
Device 18F452
XTAL 20
Include "proton18_g20joe.int" ' GLCD Routines only... add your own here.
' Here are our register symbols that include the Interrupts in use
' we are only using a limited few, but the COMPLETE register contents are listed for information
' If you are new to Interrupts, dont be put of by this huge list. !
' It could be written as just ....
'Symbol IPEN = RCON.7 ' Interrupt Priority Enable bit
'Symbol PEIE = INTCON.6 ' Peripheral Interrupt Enable
'Symbol GIE = INTCON.7 ' Global Interrupt Enable
'Symbol INT1IF = INTCON3.0 ' INT1 External Interrupt Flag 1 = The INT1 external interrupt occurred (must be cleared in software)
'Symbol INT1IE = INTCON3.3 ' INT1 External Interrupt Enable 1 = Enables the INT1 external interrupt PortB1
'Symbol INT1IP = INTCON3.6 ' INT1 External Interrupt Priority bit 1 = High priority PortB1
' and nothing more is needed - you could delete all the other symbol declares.
' But for a fuller explanations, here are the contents of all the register used, see the datasheet for your device.
Symbol NOT_BOR = RCON.0 ' Brown-out Reset Status bit
Symbol NOT_POR = RCON.1 ' Power-on Reset Status bit
Symbol NOT_PD = RCON.2 ' Power-down Detection Flag bit
Symbol NOT_TO = RCON.3 ' Watchdog Time-out Flag bit
Symbol RI = RCON.4 ' RESET Instruction Flag bit
Symbol IPEN = RCON.7 ' Interrupt Priority Enable bit
'1 = Enable priority levels on interrupts
Symbol RBIF = INTCON.0 ' RB Port Interrupt Flag
Symbol INT0IF = INTCON.1 ' INT0 External Interrupt Flag
'1 = The INT0 external interrupt occurred (must be cleared in software) PortB0 ALways HIGH
'0 = The INT0 external interrupt did not occur PortB0
Symbol TMR0IF = INTCON.2 ' TMR0 Overflow Interrupt Flag
Symbol RBIE = INTCON.3 ' RB Port Change Interrupt Enable
Symbol INT0IE = INTCON.4 ' INT0 External Interrupt Enable
'1 = Enables the INT0 external interrupt PortB0 Alwyas HIGH
'0 = Disables the INT0 external interrupt PortB0
Symbol T0IE = INTCON.5 ' TMR0 Overflow Interrupt Enable
Symbol PEIE = INTCON.6 ' Peripheral Interrupt Enable same as GIEL
Symbol GIEL = INTCON.6 ' Low priority peripheral interrupt
'When IPEN = 1:
'1 = Enables all low priority peripheral interrupts
'0 = Disables all low priority peripheral interrupts
Symbol GIEH = INTCON.7 ' Enable all high priority interrupts same as GIEH
Symbol GIE = INTCON.7 ' Global Interrupt Enable
'When IPEN = 1:
'1 = Enables all high priority interrupts
'0 = Disables all interrupts
Symbol RBIP = INTCON2.0 ' RB Port Change Interrupt Priority bit 1 high 0 low
Symbol INT3IP = INTCON2.1 ' unimplemented - read as 0
Symbol TMR0IP = INTCON2.2 ' TMR0 Overflow Interrupt Priority bit 1 High 0 Low
Symbol INTEDG3 = INTCON2.3 ' Unimplemented - read as 0
Symbol INTEDG2 = INTCON2.4 ' External Interrupt2 Edge Select bit PortB2 edge Rising If 1 falling if 0
Symbol INTEDG1 = INTCON2.5 ' External Interrupt1 Edge Select bit PortB1 edge Rising If 1 falling if 0
Symbol INTEDG0 = INTCON2.6 ' External Interrupt0 Edge Select bit PortB0 edge Rising if 1 falling if 0 Always High Priority
Symbol NOT_RBPU = INTCON2.7 ' PORTB Pull-up Enable bit - 1 disabled - 0 PORTB pull-ups are enabled by individual port latch values
Symbol INT1IF = INTCON3.0 ' INT1 External Interrupt Flag 1 = The INT1 external interrupt occurred (must be cleared in software)
Symbol INT2IF = INTCON3.1 ' INT2 External Interrupt Flag 1 = The INT2 external interrupt occurred (must be cleared in software)
Symbol INT3IF = INTCON3.2 ' unimplemented - Read As 0
Symbol INT1IE = INTCON3.3 ' INT1 External Interrupt Enable 1 = Enables the INT1 external interrupt PortB1
Symbol INT2IE = INTCON3.4 ' INT2 External Interrupt Enable 1 = Enables the INT2 external interrupt PortB2
Symbol INT3IE = INTCON3.5 ' unimplemented - Read As 0
Symbol INT1IP = INTCON3.6 ' INT1 External Interrupt Priority bit 1 = High priority PortB1
Symbol INT2IP = INTCON3.7 ' INT2 External Interrupt Priority bit 1 = High priority PortB2
' Interrupt on port b 0 to 2 - we will use portb.1
' use pin b1 (we can set priorities on B1 and B2 , portB0 is ALWAYS high priority.)
' to set up the interrupt... for PortB.1 high priority..
INT1IP = 1 ' High Priority interrupt on PortB.1
INTEDG1 = 1 ' rising edge on PortB.1
' enable the Interrupt 1
INT1IE = 1
' Flags for the interrupts are ...
INT1IF = 0 ' make sure its clear to start with.
'set up any variables
' If you use a Byte for the 2 items below, then the MAX count is 0 to 255, a word allows 0 to 65535
' with the conditioning in the Interrupt service routine you can limit the count to any vlaue within the range - see notes there.
Dim Count_Encoder As Word ' encoder counter - use byte word or anything to suit your purpose. !
Dim Encoder_Now As Word ' this is the interim storage for the value from the interrupt, must be same type as above
' We have hardware Interrupts
ON_INTERRUPT GoTo ISR ' hardware interrupt
' Jump over the Interrupt Service Routine
GoTo Over_Interrupt
'Interrupt Routine
ISR: ' Interrupt Service Routine
Context SAVE ' we are using an 18 series. SOMETIMES you do not HAVE
' to use this command with 18, check the datasheet
' Here yo DO, not using it will cause GLCD corruption at high rotation speed
' check PortB1 INT1IF, if = 1 then THIS was the interrupt
If INT1IF<>1 Then Get_Out ' it wasn't us ! so clear off
If PORTB.3=1 Then Count_Up ' if the other pin is 1 then up, otherwise down....
Dec Count_Encoder
' Here is some conditioning for the total value of the counter.
' lets say we want a maximum reading of 1024.... a Word is 0 to 65535
' so when re reach 65535 we have clocked down from 0, so reset to your desired max count
' this will act like a tuning knob, clocking over when the count is reached.
' in other circumstance you may want to use the encoder like a volume control
' where a stop is reached, so if its 0 and you continue turning anticlcokwise it stays at 0
' and when the count is reached, and you continue turning clockwise, it remains at that count.
' here it is set as an example for the volume control type with a range of 0 to 1024
' un rem the other lines to try the clockover type, and rem out the volume lines.
'If Count_Encoder = 65535 Then Count_Encoder= 1024 ' clock over counter type
If Count_Encoder <= 0 Then Count_Encoder= 0 ' volume control type of count
GoTo End_Interrupt
Count_Up:
Inc Count_Encoder
' the other part of the conditioning, we can use any value from 0 to 65535
' make sure that both parts of the conditioning relate to the same value !!!!
' If you wanted a maximum count of 10000 then change the 1024 value to 10000
'If Count_Encoder > 1024 Then Count_Encoder = 0 ' clock over counter type
If Count_Encoder >=1024 Then Count_Encoder = 1024 ' volume control type
End_Interrupt:
INT1IF = 0 ' clear flag
Get_Out:
Context Restore
Over_Interrupt: ' setup the ports, initialise variables and display, enable the interrupts.
' the encoder A - B output is A = PortB.1 , B = PortB.3
' we dont need a trisb becuase the startup is all input
' leaving the whole ports as inputs is safer so you dont accidently
' connect your encoder output to a PICŪ output !
Count_Encoder = 0 ' reset the variable to zero to start with
' you can preset this to a value to use if you like, to form a starting point
DelayMS 200 ' settle period
Cls ' Clear the lcd display
IPEN = 1 ' Enable Priorities for interrupts
PEIE = 1 ' Enable Peripheral interrupts
GIE = 1 ' Global Interrupt Enable
GoTo Display_0_at_startup ' make sure we have something on the display at startup
' Main Program Loop
While 1=1
If Encoder_Now <> Count_Encoder Then ' no need to update the diplay unless anything changed
Encoder_Now = Count_Encoder
Display_0_at_startup:
Print At 0,0,Dec,Encoder_Now," " ' Display the result
EndIf
' Do other things as you require
Wend
' End of main program loop
End ' We should NEVER get here, but just in case

Include "font.inc" ' Font for our GLCD, make it an internal font


Menu
Recent Articles


Using PDS with SPI GLCD based on ST7565R Controller
Graphic LCDs based on the ST7565 are cheaper then GLCDs with other controllers. SPI requires only four pins. If the circuit