EE109 – Spring 2017 Introduction to Embedded Systems

EE109 – Spring 2017: Introduction to Embedded Systems

Lab 7

Digital Stopwatch


In this lab exercise you will use your Arduino board and the LCD shield to implement
the functionality of a digital stopwatch. This lab will incorporate embedded coding concepts from multiple I/O modules including timers, ADC, and digital I/O in a single project. You may utilize code from other labs or in class demos as modules/functions for this project.

Stopwatch Application

The Arduino and LCD shield will be used to implement a stopwatch application that counts upwards in increments of tenths of seconds from 00.0 to 59.9 seconds. It will provide the ability to start, stop, and reset the stopwatch back to 00.0. It should also implement a "lap" feature which freezes the displayed time at the instant the "lap" button is pressed while still keeping the internal watch time incrementing. When "lap" is pressed again (or "start" is pressed again) the internal watch time (which has been running) should be re-displayed and then continue as normal.

Button Inputs

As input to your stopwatch you will use two of the push-buttons (Up and Down) on your LCD shield.

The code from your LCD lab can be reused here to perform the ADC conversion on the button input value and determine if a button was pressed.

State Machine Approach

It will likely be beneficial to keep a "state" or mode variable tracking what mode your stopwatch is in. Presses of the button will cause updates/transitions between modes or states as shown below and may cause actions to be taken depending on the transition that occurred. What needs be done in each state (whether the time is incremented and what is displayed) should be fairly intuitive. The program will also need some sort of internal representation of the time (more on that below) and logic to decide when to update the display.

In terms of overall structure, there are many ways to organize your program. One suggested approach is to have a loop in the main() routine that polls the ADC for button presses and updates the state of the stopwatch as needed. While that loop is running, your timer is also running generating interrupts every 0.1 second. You can put your display and time update logic in the timer ISR. That code can examine the state variable to determine if an update of the internal and/or displayed time is necessary. The main loop and the ISR can be thought of as two separate programs, both running at the same time, that communicate about what needs to happen through the state variables.

Note 1: Since the display can really update only every 0.1 second it would make sense to put all the display logic (except for maybe the reset case) into the code that gets executed every 0.1 second, which is the ISR for the TIMER rather than putting the display logic into the main() loop.

Task 1: Using Multiple Source Code Files

This lab will use the LCD functions that you developed in Lab 6. However since these routines are tested and working, we want to separate them from the rest of the Lab 7 code that is being developed. Most software projects larger than a few hundred lines of code are split in to multiple source code files, and we want to learn how to properly do that in this lab.

The reason the prototype for writenibble is not included in the lcd.h file is that that function is only used by the other functions in the lcd.c file, so there is no reason for it to be known by the rest of the program. Think of it as a private function that can only be used by the other functions in lcd.c.

The #include line for the lcd.h must use double quotes instead of the '<' and '>' characters. The angle brackets tell the compiler to look for the header file in the development software directories and are always used for including header files that are part of the development software. The double quotes tell it to look for it in the local directory are should be used with header files you create.

Task 2: Timer Module

The counting action of the stopwatch is based on using the 16-bit TIMER1 module to generate an interrupt every 0.1 seconds. Refer to the slides shown in class for information on the various register bits settings appropriate for this application.

Values for the timer prescaler and the modulus must be selected that yield a 0.1 second timer interrupt interval. The modulus value is stored in the OCR1A register can be any 16-bit value from 1 to 65535. The prescalar is controlled by the three bits, CS12, CS11 and CS10 bits, in the TCCR1B register. It can be set to divide the 16MHz clock by 1, 8, 64, 256 or 1024 depending on the state of these bits. Once these bits are set to a value that selects one of these divisors, the timer start counting. Conversely, if you want to stop the timer at any point, setting the three prescalar selection bits to 000 turns the prescaler off and this stops the counting of the timer. The timer is effectively turned on and off by changing the prescaler settings in register TCCR1B.

Find a combination of prescalar setting and counter modulus for TIMER1 that will cause it to generate interrupts every 0.1 seconds. From this determine which values will be used for the CS12, CS11 and CS10 bits in register TCCR1B. Fill in your choice on the Lab 7 grading rubric.

Tracking the Time

When the stopwatch is running, the TIMER1 module's ISR routine will get called every 0.1 second and the program must increment the stopwatch's time value whenever the ISR is called. There are numerous ways the program can store the time value but some ways are better than others. It is recommended to not store the time value as a single number, such as the number of seconds or tenths of seconds that has passed since the timing started. While this makes incrementing the time value very easy, it requires doing a lot of divisions or calls to snprintf to format the displayed number properly. It's more efficient to store the time as three separate fixed-point variables, one for each of the three digits to be displayed, and have your ISR change these numbers as needed to increment the time.

The time values can be stored in a couple of ways. If you store them as plain numbers (1, 2, 3, etc.) then you will need to convert these to the ASCII representations of the numbers before sending them to the LCD using the writedata() function. The LCD only displays ASCII character codes.

Alternatively, you can store the time values as their ASCII number codes (0x31 for 1, 0x32 for 2, etc.). This makes incrementing and comparing them a bit more difficult but no conversion is necessary when sending the number to the LCD with your writedata() function.

Cool C Programming Trick: The integer values 0 through 9 are sequential, and so are their ASCII codes starting at the code value 0x30. '0' = 0x30, '1' = 0x31, ... , '9' = 0x39. So to convert an integer value of 0 through 9 to its ASCII code, just add 0x30.

writedata(x + 0x30);

Or even better, since '0' = 0x30, just add the character '0' to get the same result.

writedata(x + '0');

Task 3: A Running Timer

Before writing the full stopwatch lab with buttons to control the stopwatch timer, first just implement a running timer. When the program starts, the time shown on the LCD starts at 00.0 and then increments every 0.1 seconds. When it reaches 59.9 it rolls back to 00.0.

Use/alter code from your previous labs/exercises as a basis for completing this task. You will need to integrate code from the timer examples to make a timer that generates an interrupt every 0.1s, code to output to the LCD, and code for the main routine. ADC code from the LCD lab to poll the button inputs is not used in this task but will be used in the next.

Below are some guidelines for getting started.

Once you have the running timer working check that it operating correctly. The time display should advance at the correct rate, the numbers should change properly and the time should roll over to 00.0 after reaching 59.9. Don't trying adding the stopwatch buttons to the program as described below until you have this task completed.

Task 4: The Stopwatch Program

Your running timer program should now be expanded to add the buttons for implementing the stopwatch functions.

Your program should meet the following requirements.

  1. On startup, show a splash screen for a couple of seconds as in Lab 6.
  2. Initialize the count to 00.0 whenever the program is started.
  3. Correctly display all times on the LCD.
  4. When the timer is stopped, start counting in tenths of seconds when "Start_Stop" is pressed. Note: The time value should increment to 59.9 seconds and then on the next increment go back to 00.0 and continue incrementing from there.
  5. When the timer is running, stop counting in tenths of seconds when "Start_Stop" is pressed.
  6. When the timer is running, and "Lap_Reset" is pressed, hold the displayed time while continuing internal time updates.
  7. Update the display with the current internal time and continue counting when "Start_Stop" or "Lap_Reset" is pressed and the display time is being held (i.e. LAP state).
  8. Reset the time to 00.0 when "Lap_Reset" is pressed and the timer is stopped.

Make sure to comment your code with enough information to convey your approach and intentions. Try to organize your code in a coherent fashion. Once you have the assignment working demonstrate it to one of the instructors and get their initials on your rubric. Turn in a copy of your source code through the link on the web site.