You are on page 1of 6

CoE 115 Lab3 (Keypad Interfacing and Interrupts)

Objectives
 To construct an keypad interface to the PIC microcontroller
 To program the PIC24F using a C source code
 To use an interrupt on change notification for input interfacing

Keypad Hardware and Pinout


The figure below shows the structure of the keypad with 7 pins for the 3 rows and 4 columns of the
switch matrix. When no button is pressed, all row and column lines are not connected to each other.
When a button is pressed, the corresponding row and column lines for the pressed button are
shorted. For example, pressing the button ‘6’ shorts ROW2 and COL3. The keypad mapping of the
pins and the row and column lines is summarized in the first table.

FIG. 1: Keypad Schematic

Microcontroller Port Mapping


For this exercise, we will use the following mapping of pins:

TABLE 2: Keypad Mapping to MCU Pins


KEYPAD PIC24F PORT DIRECTION
PORT
ROW 1 CN3 (also RA1) Input
ROW 2 CN30 (also Input
RA2)
ROW 3 CN29 (also Input
RA3)
ROW 4 CN0 (also RA4) Input
COL 1 RB7 Output
COL 2 RB8 Output
COL 3 RB9 Output
We will also use 4 LEDs for the output. The following pins are recommended:

TABLE 3: Output LED Mapping to MCU Pins


LED NO. PIC24F
PORT
LED_1 RB13
LED_2 RB14
LED_3 RB15
LED_4 RA0

The following pins are chosen so that the pins RB0-RB5 can still be used for the LCD.

“Hello World” in the PIC24F using C


Starting from this laboratory exercise, we will use C to program our PIC microcontrollers. As a first
step, we will make a simple C program and check if we can load it properly into the microcontroller.
Follow the code below and check if you can make your led blink.

#include "xc.h"

#define FCY 4000000 //needs to be defined first before libpic30.h


#include "libpic30.h"

_CONFIG1 (FWDTEN_OFF & JTAGEN_OFF)


_CONFIG2 (POSCMOD_NONE & OSCIOFNC_ON & FCKSM_CSDCMD & FNOSC_FRCPLL & PLL96MHZ_OFF &
PLLDIV_NODIV)
_CONFIG3 (SOSCSEL_IO)

#define LED_1 _LATB13

int main(void) {

//configure ports
AD1PCFG = 0xFFFF; //disable A/D's and configure shared ports as digital I/O
_TRISB13 = 0; //set RB13 as output

while(1)
{
_LATB13 = 0;
__delay_ms(250);
_LATB13 = 1;
__delay_ms(250);
}

return 0;
}
Press detection using “Change Notification” Interrupt (Single button)
Before we interface the whole keypad, we start with a single button and check if we can make use of
interrupts. For this part of the exercise, let’s try detecting the press on the button 8 (row3, col2).

Interrupts in the PIC microcontroller are controlled by a global interrupt enable bit. For this
particular exercise, we will be using the Input Change Notification interrupt. The global interrupt
enable bit for this interrupt can be found in the CNIE bit of control register IEC1. Input Change
Notification interrupts are enabled by setting this bit (high).

When using interrupts, the priority level must also be set. If the priority level is left at 0, then the
interrupt is effectively disabled. The interrupt priority levels are contained in the IPCx registers. The
priority level for change notification interrupts can be accessed through IPC4 or directly as CNIP[2:0].

The pins in which we want to monitor input change notification must be specified. This is done
through the CNENx register. To enable interrupt generation when a change in the value of the GPIO
pin occurs, the appropriate bit mask in CNENx should be set high. The CN interrupt enable bits within
CNENx can also be accessed individually as CNxIE. Since we are trying to detect presses from key 8
(row 3, col2) and row3 is mapped to CN29, then we need to enable the change notification interrupt
for CN29 (e.g. CN29IE = 1;).

Whenever an interrupt is generated, the corresponding interrupt flag is set and the execution goes
into the ISR if properly setup. The Input Change Notification interrupt flag is the CNIF bit within an
IFSx register. Until this flag is cleared, all supposed interrupts generated through the CNIF bit are
ignored.

In the provided sample program, only CN29IE is set since we are only using a key press in row3 to
generate an interrupt. If the key 8 is not pressed, then the row3 lines remain high because of the
internal pull-up that is used. When key 8 is pressed, the row 3 is pulled down by the col2 line and the
change from high to low of row3 generates an interrupt.

Follow and try the provided code below to handle interrupts using a single button:

#include "xc.h"

#define FCY 4000000 //needs to be defined first before libpic30.h


#include "libpic30.h"

_CONFIG1 (FWDTEN_OFF & JTAGEN_OFF)


_CONFIG2 (POSCMOD_NONE & OSCIOFNC_ON & FCKSM_CSDCMD & FNOSC_FRCPLL & PLL96MHZ_OFF &
PLLDIV_NODIV)
_CONFIG3 (SOSCSEL_IO)

#define LED_1 _LATB13

#define COL_2 _LATB8


#define ROW_3 _RA3

int ROW_3_pressed;

int main(void) {

//configure ports
AD1PCFG = 0xFFFF; //disable A/D's and configure shared ports as digital I/O
//LEDs
_TRISB13 = 0; //set RB13 as output
_TRISB14 = 0; //set RB14 as output

//COL2
_TRISB8 = 0; //set column port as output
_LATB8 = 0; //signal that will pull down the row line when pressed

//ROW3
_TRISA3 = 1; //set row port as input
_CN29PUE = 1; //enable pull up for CNx

//Enable CN Interrupts
_CNIF = 0; //clear CN interrupt flag
_CNIP = 1; //set CN interrupt priority
_CNIE = 1; //enable CN interrupts in general
_CN29IE = 1; //enable specific CNx interrupt

//start of keypad interfacing


ROW_3_pressed = 0;
COL_2 = 0; //signal that will pull down the row line when pressed
//same as _LATB8 = 0 above
while (1)
{
if(ROW_3_pressed)
{
__delay_ms(250);
LED_1 = ~LED_1;
}
else
{
LED_1 = 0;
}
}

return 0;
}

void _ISR _CNInterrupt(void)


{
__delay_us(1000); //wait for switch bounce finish
if (ROW_3 == 0)
ROW_3_pressed = 1;
else
ROW_3_pressed = 0;
_CNIF = 0;
}

[CHECK: 20%] Vary the provided code to detect key presses from any of the following buttons: 1, 3,
4, 6, *, and #. The LED 1 should blink the same if any one of the key 8 and the selected key is pressed.
Example, LED 1 should be if 8 or 4 is pressed.

[CHECK: 20%] Modify the ISR such that when a key press is made in COLX, LED_X is toggled. Thus,
pressing ’1’ toggles LED_1, ’2’ toggles LED_2, etc. up to LED_4. Take note that you may not modify the
hardware connections specified i.e. row1 must remain an input pin, while col1, col2, and col3 remain
as output pins.
FULL KEYPAD (60%)

Connect all rows and columns to the microcontroller. Modify the program such that when a key press
is made, the state of all LEDs are switched (and held until the next button press) to the binary
equivalent of the number pressed. For example, pressing button ’1’ sets (LED4,LED3,LED2,LED1) to
(0,0,0,1), pressing button ’5’ sets it to (0,1,0,1), etc. For ’*’, set the output to (1,0,1,0), and for ’#’, set
the output to (1,0,1,1).

__________________________________

Important Notes:

1. Determine which pin caused the interrupt - Multiple pins (identified by the CNENx bit masks)
generate only one interrupt (CNIF bit of IFS1). Thus, it is up to the programmer to determine the
source of the interrupt by individually checking port pins.
2. Perform a software debounce.
3. Set the key press flag - ISR’s often have limited space in program memory. Thus, it is common
practice for ISR’s to simply modify a flag and let the main part of the program decode this flag.
4. Clear the interrupt flag - This allows future interrupt requests generated after executing the ISR to
be recognized again.

Programming Guide:

xc.h
-can be found on "...\Microchip\xc16\vX.XX\support\generic\h"
-this header file selects the appropriate deivce header for the project (e.g. p24FJ64GB002.h)

p24FJ64GB002.h
-can be found on "...\Microchip\xc16\vX.XX\support\PIC24F\h"
-contains variable names for SFRs and their bits or components
-included by the xc16 compiler with the help of "xc.h"

p24FJ64GB002.gld
-can be found on "...\Microchip\xc16\vX.XX\support\PIC24F\h"
-can be used as reference for proper interrupt vectors (ISR_names)
-contains values for replacing SFR names into addresses (e.g. LATA = 0x2C4)

p24FJ64GB002.inc
-can be found on "...\Microchip\xc16\vX.XX\support\PIC24F\inc"
-contains configuration options with description

libpic30.h
-can be found on "...\Microchip\xc16\vX.XX\support\generic\h"
-contains the function definitions for __delay32(), __delay_ms(), and __delay_us()
__delay32() delays for the # of instruction cycles inputted
__delay_ms() and __delay_us() delays for the # of ms/us inputted
however to use them, there is a need to define an FCY variable
e.g. for a 4MHz instruction cycle: #define FCY 4000000UL
C Program Template:

#include "xc.h"

_CONFIG1 (FWDTEN_OFF & JTAGEN_OFF)


_CONFIG2 (POSCMOD_NONE & OSCIOFNC_ON & FCKSM_CSDCMD & FNOSC_FRCPLL &
PLL96MHZ_OFF & PLLDIV_NODIV)
_CONFIG3 (SOSCSEL_IO)

#define led LATBbits.LATB5 /*sample only*/

void sample_func(void);

int main(void) {

/* configure ports (TRIS registers and other settings) */


/* e.g. shared analog inputs, internal pullup settings, etc. */

/* do stuff here */

return 0;
}

void sample_func(void) {
/*do stuff*/
return;
}

You might also like