External Interrupts: Interfacing a Scroll-Wheel Switch


I recently picked-up a couple of these little “scroll-up-down, push-to-select” type switches.  Exploring how to interface them to a PIC led to me using the External Interrupt Inputs for the first time.  This article describes what I did and what I learned to get to the point shown in the video.




The Investigation

ContactsThe scroll-wheel on the switch can be continuously rolled up or down and can also be clicked like a push-button.  There are 5 contacts on the back and a quick investigation with a continuity tester found that the contacts labelled in red in the image are for the push button part of the switch.

The contacts labelled in blue are for the scroll-wheel part of the switch.  The continuity tester showed that as the wheel was scrolled up and down there was momentary contact between pin 1 and pins 2 & 3.  Clearly, there are two switches inside which make and break contact in some sort of pattern.  To get a better idea of what was going on, I connected the switch to the PICkit 2 Logic Analyser using this circuit.






On the right are the traces I got when the wheel was scrolled up and down.  As you can see, when scrolling up, Switch1 and Switch2 go High at the same time.  When scrolling down, Switch1 is High while Switch2 remains Low for a moment. Getting the direction of the scroll-wheel is therefore simply a case of waiting for Switch1 to go High and reading the state of Switch2 at that point.


The Interface

The following flowchart outlines a program to read the scroll-wheel by periodically checking its state.  This periodic polling method works to a point and it’s what I used for my initial testing.  However, this approach has a number of problems.


DownTraceLabelledConsider this image of the trace when the wheel is scrolled down.  As long as the states of the switches are read sometime between points A and B then the correct result will be obtained.  But if the PIC doesn’t get around to reading the switches until after point B then it will look like the wheel is scrolling up rather than down because both Switch1 and Switch2 will be High at the same time.  Therefore, in order to make a polling approach work, the PIC has to check the switches frequently enough that it won’t miss the beginning of any movement of the wheel.  This severely limits the other actions the PIC can carry out between checking the switches.  Furthermore, if the PIC is polling the switches extremely quickly then it may read the switches once between points A and B and then again between points B and C – it will think the wheel has gone down and then up.

A better approach would be if the program could ignore the scroll-wheel entirely until Switch1 goes High and then - at that point - read the state of Switch2.  This is where the External Interrupt Inputs come in.


External Interrupt Inputs

ExternalInterruptPinsAs the name suggests, these are inputs which can be used to trigger an interrupt, causing the PIC to immediately execute the code in the Interrupt routine.  The PIC 18F1220 has 3 of these inputs: INT0, INT1 and INT2.  The inputs can be configured to trigger the interrupt on either the rising edge or the falling edge of the signal on the pin.

For this application, I wanted the interrupt to occur on the rising edge of Switch1.  The interrupt will then read the state of Switch2 to determine the direction of the movement.  The schematic shows how I connected the PIC.  For details of the LCD connections, see the Swordfish Help file.


On the PIC18F1220, the INT0 input is controlled by the following registers:

PIC18F1220 - INT0 Registers
INTCON.4	INT0IE		INT0 External Interrupt Enable bit
				1 = Enables the INT0 external interrupt
				0 = Disables the INT0 external interrupt
INTCON.1	INT0IF		INT0 External Interrupt Flag bit
				1 = The INT0 interrupt occurred
				0 = The INT0 interrupt did not occur
INTCON2.6	INTEDG0		External Interrupt 0 Edge Select bit
				1 = Interrupt on rising edge
				0 = Interrupt on falling edge
Note: Unlike INT1 and INT2, INT0 is always a high priority interrupt and
      therefore it does not have an interrupt priority bit.

In Swordfish, INT0 can be set-up like this:

INTCON2.Bits(6) = 1               'Set INT0 to interrupt on rising edge
INTCON.Bits(1) = 0                'Clear INT0 interrupt flag
INTCON.Bits(4) = 1                'Enable INT0 interrupt

Even though the INT0 interrupt has been enabled, it still won't trigger an interrupt.  This is because the Global Interrupt Enable bit (GIE) has not been set (INTCON.7=1).  In Swordfish the Enable() command both lets the compiler know where the interrupt routine is and sets the Global Interrupt Enable bit:

Enable(ExternalInt0)              'Enable interrupt handler and turn on interrupts

The interrupt routine is kept as simple as possible.  A flag Scrolled is set to indicate that the wheel has moved.  A bit variable ScrollDirection is set to the value of Switch2 and will therefore = 1 when the wheel is scrolling up and = 0 when it is scrolling down.  Finally, the INT0 Interrupt Flag is cleared, ready for the next interrupt to occur.

Interrupt ExternalInt0()
    Scrolled = True               'Set Scrolled flag
    ScrollDirection = Switch2     'ScrollDirection depends on the state of Switch2 at the time
				  'Switch1 went High: 1 = Scroll UP, 0 = Scroll DOWN.
    INTCON.Bits(1) = 0            'Clear INT0 interrupt flag  
End Interrupt


Example Code

Here is the listing for the program used to produce the demo shown in the video.  The scroll-wheel is used to change the value of a variable Count which is then output onto the LCD.  I have a Swordfish module "BigDigits.bas" which prints the large characters onto the LCD.  More on that another day...

Scroll-Wheel Demo Program
*  Name    : Scroll-Wheel Switch                                               *
*  Author  : AndyO                                                             *
*  Notice  : Copyright (c) 2010 AndyO                                          *
*          : All Rights Reserved                                               *
*  Date    :                                                                   *
*  Version : 1.0                                                               *
*  Notes   : Reads Up/Down Scroll Switch connected to INT0 (PortB.0) and       *
*          : PortA.3                                                           *
*          :                                                                   *
'Device, Clock and Config directives
Device = 18F1220
Clock = 8
Config MCLRE = OFF                      'Disable MCLR on Pin 4 (RA5)
'Module Options & Includes
Include "InternalOscillator.bas"        'Configures internal oscillator
Include "Utils.bas"
Include "LCD.bas"
Include "BigDigit.bas"                  'Prints big numbers on LCD
Include "Convert.bas"
'Variable and Const Declarations
Dim Switch2 As PORTA.3
Dim Scrolled As Boolean                 '=True when switch has moved
Dim ScrollDirection As Bit              '1 = Switch moved Up, 0 = Down.
Dim Count As Byte                       
'Name:      ExternalInt0         
'Purpose:   Triggered by rising edge on External Interrupt 0 (PortB.0).
'           Sets "Scrolled" flag to indicate the switch has moved and sets
'           "ScrollDirection" to indicate which direction switch has moved in.
Interrupt ExternalInt0()
    Scrolled = True                     'Set Scrolled flag
    ScrollDirection = Switch2           'ScrollDirection depends on the state of
                                        'Switch2 at the time Switch1 went High -
                                        '1 = Scroll UP, 0 = Scroll DOWN.
    INTCON.Bits(1) = 0                  'Clear INT0 interrupt flag  
End Interrupt
'Main Program
'---Set-up I/O
SetAllDigital                           'Turn off analogue functions
'---Initalise variables
Count = 0
Scrolled = False
'---Set-Up INT0 External Interrupt
INTCON2.Bits(6) = 1                     'Set INT0 to interrupt on rising edge
INTCON.Bits(1) = 0                      'Clear INT0 interrupt flag
INTCON.Bits(4) = 1                      'Enable INT0 interrupt
Enable(ExternalInt0)                    'Enable interrupt handler
'---Program Start---------------------------------------------------------------
BigDigit.PrintNumber(Count, True)
While True
    If Scrolled = True Then             'If Switch has moved:
        Scrolled = False                    'Clear Scrolled flag
        If ScrollDirection = 1 Then         '}
            Inc(Count)                      '}Increase or decrease Count
                                            '}depending on which way the switch
        Else                                '}was moved
            Dec(Count)                      '}
        EndIf                               '}
        BigDigit.PrintNumber(Count, True)   'Output Count  

Swordfish Compiler

Swordfish is a highly structured, modular compiler for the PIC18 family of PIC® microcontrollers. Swordfish is a true compiler that generates optimised, stand alone code which can be programmed directly into your microcontroller.

Get it today!