Lab: Blinking Lights

Assigned
Wednesday, Oct 10, 2018
Due
Tuesday, Oct 16, 2018 by 10:30pm
Submitting
Demonstrate each part of your lab before moving on to the next part. If you do not finish the lab during class time you can arrange a time to demonstrate the remaining parts during office hours. If you are unavailable for office hours you can email a video of your working implementation for the remaining parts of the lab.

Overview

In this lab, you will use the protoboard’s switches to blink LEDs in two or more different patterns. Along the way, you will write assembly code to call functions, read input, isolate bit fields, and perform conditional execution.

Note that you are expected to produce nicely-aligned, well-commented assembly code. Code that does not meet these standards will not be checked off.

Resources

Part A: Delaying With a Loop

Our first task is to write a program that will blink the LEDs slowly enough for us to see. We’ll control the LED on the microstick the same way we did in the previous lab, but we’ll add a pause by asking the PIC32 to loop for a while before changing the state of the LED. Specifically, we’re going to use a MIPS version of the following C procedure:

// Precondition: iterations > 0
void delayloop(int iterations) {
  do {
    iterations--;
  } while (iterations != 0);
  return;
}

Translate the above C procedure to MIPS assembly with pencil and paper. Make sure you follow all the MIPS calling conventions in your translated version. When you have completed your translation, have the instructor or a mentor sign off on this part of the lab.

Part B: Blinking Slowly

Now that we have our delayloop procedure we can use it to make the LED on the microstick blink slowly enough when running at full speed. You will need to calculate the number of iterations your delayloop procedure should run to pause for about half a second, then call this procedure with the appropriate value of iterations.

First, you’ll need to launch MPLAB.

mplab_ide &

Create a new project named blinky (if the software complains about an invalid folder, you will need to reboot your machine); pay close attention to whether you have a PIC32MX110F016B or the compatible PIC32MX250F128B and select the appropriate chip number when you create your project. In your new project, create a file named blinky.S (note capitalization) and copy the following code:

  # blinky.S
  # Written by Janet Davis, 13 October 2013
  # Edited by Charlie Curtsinger, 9 October 2018

.set noreorder  # Avoid reordering instructions
.text           # Start generating instructions
.globl main     # The label should be globally known
.ent main       # The label marks an entry point

# Preprocessor defines for output port control
#define ON  0x1
#define OFF 0x0

# The beginning of the delayloop procedure.
# Signature: void delayloop(int interations)
delayloop:
  ##########################################
  # Transfer your delayloop procedure here #
  ##########################################

# The main entry point for the program
main:    
  la   $s0, TRISA   # Load the address mapped to the TRISA control register
  li   $t0, 0    
  sw   $t0, 0($s0)  # Store the value 0 to all bits of the TRISA register,
                  # setting all bits of Port A as output
  la   $s0, LATA    # Load the address mapped to the LATA control register
loop:
  li   $t0, ON        
  sw   $t0, 0($s0)  # Write to LATA, turning the LED on
  
  #####################################
  # Call the delayloop procedure here #
  #####################################
  
  li   $t0, OFF    
  sw   $t0, 0($s0)  # Write to LATA, turning the LED off
  
  #####################################
  # Call the delayloop procedure here #
  #####################################
  
  j    loop          # Infinite loop
  nop             # Do nothing
.end main         # Marks the end of the program

This program is very similar to the one you used in the previous lab. Verify that stepping through the code with the debugger blinks the onboard LED on and off. Transcribe the body of the delayloop procedure you translated in part A, and call that procedure in both spaces indicated in the above assembly code. Call delayloop with a parameter of at least 100,000 to start; this will allow you to test the implementation before you derive an appropriate parameter to pause for half a second. Move on once you have your LED blinking slowly enough that you can see it blinking when the program is running at full speed (not in debug mode).

Setting Pause Time

I would like you to blink your LED so a full period takes one second; that means the LED will be on for half a second, then off for half a second. The first step in controlling pause time is to calculate how many instructions your delayloop procedure executes for a given value of iterations. Remember that the PIC32 will run one instruction immediately after all branch and jump instructions. Our PIC32 has a clock speed of 4MHz, and it executes one instruction on each clock cycle. That means one instruction takes .00000025s, or 250 nanoseconds. Using this information, find a reasonable value of iterations that will cause delayloop to run for about half a second; it is okay if you are off by a few cycles. Once you have an iteration count, update your code and demonstrate your program for the instructor or a mentor before moving on to the next part.

Now that we have a program that blinks an LED at a steady rate, you will need to make it respond to switch input. Our goal is for the LED to blink at 1Hz when the switch is off and 2Hz when the switch is on. Working with input pins isn’t too difficult, but you have to get a few details right so please read the instructions carefully.

Setting up Port B for input

So far we have used the pin RA0 as an output to turn a single LED on and off. This pin is part of a collection of pins referred to Port A. The control register TRISA sets each pin up to work as either an input or output; for example, storing a 0 into bit 3 of TRISA will make RA3 an output, and storing a 1 into bit 2 of TRISA will make RA2 an input.

There is another collection of pins on the PIC32—with corresponding pins on the microstick—called Port B, which is controlled by the memory-mapped register TRISB. We are going to use the pin RB5, so you’ll need to store a 1 in bit 5 of TRISB; remember that we count bits from the right starting at zero. Update your MIPS program to do this now.

Reading From Input Ports

Now that we have pin RB5 set up as an input we need to read from it. So far we have only dealt with outputs, which we control by storing values to the LATA memory-mapped register. Storing values to LATA will control the state of any output pins in port A. There is an equivalent LATB memory-mapped register for port B, but these LAT registers are only suitable for use with outputs. To read from inputs we instead load from the locations PORTA and PORTB. If the input pin RB5 is high, the value loaded from PORTB will have a 1 in bit 5 of the result. Update your code to load from PORTB at the start of each iteration of the main loop.

Hardware Setup

Now that we have code that can read from input pins we need to connect some sort of input. While we were able to use logic switches with the protoboard in previous labs, this would be a bad idea with the Microstick. Remember that voltages are not absolute values, but rather the potential energy difference between two levels. There is no guarantee that ground for the protoboard will be the same as the ground for the microstick, so we have to use the onboard voltage source and ground rather than the protoboard. To do this, we will use the single-pole, double-throw (SPDT) switches on the protoboard.

An SPDT switch connects a common output wire to one of two inputs. When the switch is up, it connects the common wire in the middle to the top input. When the switch is down, it connects the common wire to the bottom input. A logic switch uses the common wire for output with the top input connected to positive voltage and the bottom input connected to ground.

Look at the PIC32MX1XX/2XX Family Data Sheet to find the RB5 pin, VDD (a positive voltage source), and VSS (ground). Connect these pins to an SPDT switch on the protoboard so RB5 connects to VDD when the switch is up and VSS when the switch is down.

Now that you have your switch wired up to the microstick, use the debugger to look at the value loaded from PORTB when the switch is up or down. Verify that bit 5 (counting from zero) is set when the switch is up and cleared when the switch is down. You will likely notice that unconnected pins have an unpredictable effect on the bits of PORTB; this is expected.

Now that you have all the new pieces in place, you’ll need to use the switch to control the blink rate. Your program should already configure port B as an input and read from PORTB at the top of the program’s main loop. Now add code to isolate bit 5 of the value loaded from PORTB and change the parameter passed to delayloop based on the switch setting. Remember to follow MIPS calling conventions with your register use; that means you may not assume that any $t_ register will be preserved across a call to delayloop. Once your program is working, demonstrate it to the instructor or a mentor before moving on.

Part D: A Second Switch

In the final part of this lab you will use a second switch to add additional control to the blinking LED. You will need to wire a second SPDT switch to RB7, connected to either VDD or VSS in the same way as the first switch. Once you have the second switch connected, implement one of the following features:

  1. Use the two switches in combination to select four different blinking speeds.
  2. Use the first switch to control the time the LED is on and the second to control the time the LED is off.
  3. Wire up four LEDs to RA0, RA1, RA2, and RA3. Changing the switches should control which combinations of LEDs turn on and off together; there are many possibilities, so you are free to choose whatever scheme you like.

Have the instructor or a mentor sign off on your implementation once you have finished one of the above features.