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.
Carefully read all of the instructions before beginning the lab.
Skim through the program provided below. This program counts up (in binary) using the first four pins in
# Janet Davis, 8 December 2013 # YOUR NAME HERE # 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.
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
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
# 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
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.
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.
This lab was developed by Janet Davis for CSC 211L in 2013. Parts of the lab were inspired by exercises written by Marge Coahran.
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.