# Lab 14: Handling Interrupts on the PIC32

In this lab, you will add an interrupt handler routine to an existing PIC32 program. Interrupts make it possible to comput one thing in the “foreground” while an I/O operation happens in the “background.” When that operation completes—or in this case, when the user provides an input—the foreground operation is interrupted. After handling this event, your program can pick up where it left off.

Assigned: Tuesday, December 6

Due: This lab does not need to be submitted.

Collaboration: You may choose your partner(s) for this lab.

## Preparation

Carefully read all of the instructions before beginning the lab.

## Part A: Setting up

Skim through the program provided below. This program counts up (in binary) using the first four pins in PORTA.

# Janet Davis, 8 December 2013
# Count in binary on four LEDs.
# When a switch is flipped, briefly twinkle the LEDs, then resume
# counting.
# Illustrates the use of interrupt-driven port I/O.

.set noreorder          # Avoid reordering instructions
.set noat
.global main            # main marks the program entry point

.text                   # Start generating instructions

# setPortABits(int bits)
# This function sets bits as specified in I/O port A.
#
# parameter:        Bit mask in $a0 # produces: nothing # pre-condition:$ra contains return address
# post-condition:   PORTD contains the bits specified in $a0 .ent setPortABits setPortABits: # function body la$t0, PORTA      # Load address of Port A
sw        $a0, 0($t0)     # Set specified bits in Port A
jr        $ra # return to caller nop .end setPortABits # delay(iterations) # This function runs the specified number of iterations of a delay loop. # # parameter: 32-bit value in$a0
# produces:         nothing
# pre-condition:    $ra contains return address # post-condition: Time has elapsed proportionate to the number of # iterations. .ent delay delay: delayloop: addi$a0, $a0, -1 bne$a0, $zero, delayloop nop # return to caller jr$ra
nop
.end delay

# main loop
.ent main
main:
# Set PORT A bits 0-3 to output
la      $t9, TRISA li$t0, 0x0
sw      $t0, 0($t9)

# Set PORT B bit 5 to input, remainder to output
la      $t9, TRISB ori$t0, $zero, (1 << 5) sw$t0, 0($t9) # Count in binary on four LEDs li$s0, 0          # Initalize counter
li      $s1, 16 # Counter reset value outerloop: add$a0, $s0,$zero
jal     setPortABits    # Display counter value on Port A LEDs
nop
li      $a0, 0x200000 # Delay jal delay nop addi$s0, $s0, 1 # Add one to counter bne$s0, $s1, outerloop nop li$s0, 0          # If counter was 16, reset to 0.
j outerloop             # Loop forever
nop

.end main


Create a new MPLAB project and add the code above as a new source file. To run this program, first connect the pins RA0, RA1, RA2, and RA3 to logic indicators on your protoboard. You should also connect pin 8 to ground on your protoboard. Turn the protoboard on, build your MPLAB project, and program the device. Verify that the four LEDs count up to 15 in binary. Make sure your logic indicators are set to TTL levels. You may want to reorder your connections to the logic indicators to display the bits in order.

## Part B: Installing an interrupt handler

We are going to use a switch connected to PORTB to make the lights blink briefly before returning to the counting procedure. Unlike our previous lab with switches and the PIC32, this time we will use interrupts to respond to switch changes instead of polling the switch state in a loop.

The first step in handling interrupts is to write the interrupt handler routine. The procedure below is an interrupt handler, with some missing pieces:

# handleCNInterrupt
# Handles a Change Notice interrupt
.ent handleCNInterrupt
handleCNInterrupt:
# Interrupt prologue
rdpgpr  $sp,$sp        # Restore stack pointer from shadow register set

# TODO: Save registers here

# Read PORTB to clear change condition
la      $t9, PORTB lw$t0, 0($t9) # Clear CNBIF - Change Notice B Interrupt Flag - # to signal this interrupt has been handled. la$t9, IFS1CLR
li      $t0, (1 << 14) sw$t0, 0($t9) # Save the current state of the Port A LEDs la$t9, LATA
lw      $s0, 0($t9)

# TODO: Blink ten times very fast

# Restore the previous state of the LEDs
la      $t9, LATA sw$s0, 0($t9) # TODO: Restore registers here eret # Return from exception nop .end handleCNInterrupt  Notice the three TODO comments in the interrupt handler; you will need to fill in the code to blink all four LEDs ten times, as well as code to save and restore all registers used by the interrupt handler but don’t fill this in yet. Unlike a procedure call, interrupt handlers do not follow calling conventions; an interrupt handler can execute at any point so every register must be restored to their original state, even temporary registers like $t0.

Before filling in the missing code, you should add the following directive before the .text section in your program:

# Install our interrupt handler at position 0 in the exception handler
# vector.
.section .vector_0, code
j handleCNInterrupt
nop


You will also need to enable interrupts. Add this code before the outerloop label in main.

# Disable global interrupts
di

# Set "ON" bit in the Change Notice configuration register for Port B
la      $t9, CNCONBSET ori$t0, $zero, (1 << 15) sw$t0, 0($t9) # Set CNBIE - Change Notice B Interrupt Enable la$t9, IEC1SET
li      $t0, (1 << 14) sw$t0, 0($t9) # Enable interrupts for pin RB5 la$t9, CNENB
li	    $t0, (1 << 5) sw$t0, 0($t9) # Set interrupt priority to 7 (default is 0 => no interrupt!) # Bits 18, 19, 20 of IPC8 # Cannot load immediate to high order bits, so load and then shift la$t9, IPC8SET
addi    $t0,$zero, 0x7
sll     $t0,$t0, 18
sw      $t0, 0($t9)

# Enable global interrupts
ei


## Part C: Filling in the interrupt handler

What happens when you run this program without adding the blinking code? You may observe some strange behavior. Why is this happening?

Fill in the missing code to blink all four lights, then modify the interrupt handler routine to save and restore all registers you use in the handler.

## Part D: A second counter (if you have time)

Modify the provided program to use the logic indicators for two independent 2-bit counters. The first 2-bit counter should automatically count up (or wrap around to zero) after each delay, much like the 4-bit counter provided above. The second counter should use the interrupt handler to count up each time the switch is flipped.

### Acknowledgements

This lab was developed by Janet Davis for CSC 211L in 2013. Parts of the lab were inspired by exercises written by Marge Coahran.