CS 153: Lab 6

Title image Fall 2018

Lab Exercise 6:

The purpose of this lab time is to continue to explore how to organize code in a way that is modular and that encapsulates implementation details so it is easier to build more complex code. The application for this project will be to create an electronic level and an electronic compass.


Tasks

Board

The lab and project will use a configuration of the board that uses an accelerometer and magnetic sensor and five LEDs. Pick out three green, two red LEDs, and five 560 Ohm resistors.

  1. Set up your board to drive five LEDs with 560 Ohm resistors with the three green LEDs in the middle and the red LEDs on the ends (picture below). Make sure the long pin is on the side connected to the board; the resistors should connect the shorter pin to ground. I'd suggest using pins 2, 3, 4, 5, and 6. When using serial port communication, pins 0 and 1 are used for that purpose.

  2. Add the LSM3830 accelerometer/magnetic sensor to your board. Connect 3.3V or 5V to the pin labeled VIN (pin 2), connect ground to the pin labeled GND (pin 3), connect pin A5 from the Metro board to the pin labeled SCL (pin 8), and connect pin A4 from the Metro board to the pin labeled SDA (pin 7).

  3. Code

  4. For the last project, you created a function setLEDs that made it much easier to control a bank of LEDs by enabling you to set all of their values with a single function call. This week we're going to take that a step further and create a library of functions to manage a bank of LEDs, making it easier to do many different things, as well as keep track of the current state of the LED bank.

    Make a new code file and save it as pattern. You can use the following template for it. Don't worry about filling in the details until you have finished steps 4 through 7.

    /*
      Your name, project, and a date go here
     */
    
    /* include files go here */
    
    int main() {
      /* function variable declarations here */
    
      // initialize the board before executing any Arduino code
      init();
      Serial.begin(9600);
    
      /* initialize the LED bank here */
    
      /* main code goes here */
    
      return(0);
    }	
  5. Create a second tab and save it as led_p6.h. Put the standard header at the top (name, project, date). Use the template below, which uses some pre-processor directives (#ifndef and #define) to ensure the contents of the include file are read only once by the compiler.

    /*
     * Your name/date/project go here
     * 
     * A library of functions for handling LEDs
     * Useful when you have a bank of LEDs on pins
     *
     */
    
    #ifndef LED_P6_H
    #define LED_P6_H
    
    #include <Arduino.h>
    
    /* constants defined here */
    
    /* typedefs go here */
    
    /* prototypes go here */
    
    #endif

    A .h file, or include file, is where we want to put things like constant and macro declarations, where we define new types or structures (structs), and where we put prototypes for library functions. Then a file that wants to use our library just needs to #include the .h file and it will have access to those constants, types, and functions. The remaining steps of the lab involve filling out the include file and writing the set of functions for managing the LEDs.

  6. Use #define to define a constant LED_MAX_PINS and set its value to 8.

    #define LED_MAX_PINS 8

  7. Create a new struct data type to hold information about the LED bank. Use a typedef statement and name the struct LEDBank. The struct should have three fields: an unsigned char array with LED_MAX_PINS elements to hold the pin numbers being used by the LED bank, an int to hold the number of active pins/LEDs, and an int to hold the current status of the LEDs. We will interpret the status field as we did last week with setLEDs, using each bit of the int to represent whether an LED is on (1) or off (0).
  8. Create a new tab and save it as led_p6.cpp. Put the standard header at the top of the file. Then #include the file led_p6.h (use quotes around the file name). Then write the following functions.

    1. int led_init( LEDBank *lb, int pins[], int nPins ) - the arguments to this function are the address of an uninitialized LEDBank struct, an array of pin numbers, and the number of values in the array. The function should loop over the number of pins and (1) assign to lb->pins[i] the ith value in pins, (2) use the pinMode function to set lb->pins[i] to be an OUTPUT, and (3) use digitalWrite to set the pin to LOW. After the loop, it should (1) assign to the appropriate field of the lb struct the value in nPins and (2) assign to the state field of the lb struct the value 0 (all LEDs are off). At the end of this function, the LEDBank struct should contain the pin numbers in an array, the number of active pins, and the current state of the LEDs. The function should return 0.

      Once you have written this function, put a prototype for it into the led_p6.h file. A prototype is the signature of the function: the return type, the name, and the arguments, followed by a semi-colon, as in the example below.

      int led_init( LEDBank *lb, int pins[], int nPins );
    2. int led_off( LEDBank *lb ) - this function should loop over all of the pins and use digitalWrite to set them all to LOW. It should then set the LEDBank status field to indicate all of the LEDs are off and return the current status.

      Once you have written this function, put a prototype for it into the led_p6.h file.

    3. int led_on( LEDBank *lb ) - this function should loop over all of the pins and use digitalWrite to set them all to HIGH. It should then set the LEDBank status field to indicate all of the LEDs are on and return the current status. You can use the value 0xFF to specify a value where the first 8 bits are ones.

      Once you have written this function, put a prototype for it into the led_p6.h file.

    4. int led_setAll( LEDBank *lb, int state ) - this function should loop over all of the pins and use digitalWrite to set their value to HIGH or LOW depending on the corresponding bit in the state parameter. It should then set the LEDBank status field to state and return the current status.

      Once you have written this function, put a prototype for it into the led_p6.h file.

    5. int led_status( LEDBank *lb ) - this function should return the current status value.

      Once you have written this function, put a prototype for it into the led_p6.h file.

    6. int led_set( LEDBank *lb, int which, int value ) - this function should set the LED specified by the which argument to the value specified by the value argument. It should then update the status field of the LEDBank struct and return the current status. You can do that with the following statement (see if you can work out what it is doing).
        lb->status = value ? lb->status | (1 << which) : lb->status & (~(1 << which));

      Once you have written this function, put a prototype for it into the led_p6.h file.

    This should complete your library. Try compiling it and fix any errors that occur.

  9. Returning to your pattern file, in the function variable declarations section, declare a const int NPins and set its value to 5. Then declare an int array of size NPins and initialize it to the pin numbers of the five pins you used for your LEDs. The declare a variable lb of type LEDBank.

    In the main code section, first initialize your LEDBank by passing the address of the LEDBank struct to led_init along with the led_pins array and the number of pins.

    Finally, use a sequence of calls to led_on, led_off, led_set, and led_setAll to manipulate the LEDs and test each function. Be sure to add a delay after each change of lights. After each call, print out the result of calling led_status( &lb ) to ensure that the LED status field is being properly updated.

    Take a short video of your LEDs working and submit it as part of the project report/handin.


When you are done with the lab exercises, you may start on the rest of the project.