EE109 – Spring 2018: Introduction to Embedded Systems
Arduino Input and Output
In this lab exercise you will build a signaling device that makes an LED flash the Morse code signal for a letter of the alphabet. Rather than have 26 input buttons it will only have three, each assigned to one of three letters of the alphabet. When one of the buttons is pressed, an LED will flash the Morse code signal for the corresponding letter.
Morse code is a method of signaling letters and numbers by sending on and off patterns of light or audible tones. It was first developed in the 1830's and for many years was the standard way of communicating via telegraph and radio. It is still used today by amateur radio operators, the military, and sometimes for emergency communication.
Characters in Morse code consist of a unique set of short or long signals, known as "dots" and "dashes". The codes for letters A through Z and 0 through 9 are shown below. The dots and dashes of a character are separated by a short gap, and a longer gap separates characters. The most widely known Morse code sequence is the distress call "SOS" of three dots, three dashes, three dots.
The length in time of a dot or dash in Morse code is not specified directly, only the relationship between the dot and dash length:
- Dashes are three times longer than a dot.
- Gaps between the dots and dashes of a single character are the same length as the dot.
- The gap between characters is the length of the dash.
The speed of the transmission of the characters can cover a wide range, but the above ratios should be adhered to in order to make the transmission understandable by the receiving party.
The buttons you will be using are the same ones you used in Labs 1 and 2. These are classified as momentary action, single pole, normally open buttons. These terms define how the button works.
- Momentary action
- The button is in the ON state only when it is held down. Once you release pressure on the button, it returns to the OFF state on its own.
- Single pole
- The button has one on-off circuit inside it. Buttons can be found with 2, 3, or more circuits that all turn on and off together when the button is operated.
- Normally open
- When the button is NOT being pressed the switch contacts are OPEN so the two terminals of the switch are not connected. Pressing the switch closes the contacts. Switches are also available as "normally closed" models meaning that the contacts are connected (closed) until the switch is pressed.
The buttons for this lab have four pins on the bottom that are spaced so they will fit in the holes in the breadboard. One side of the button has a flat part that is used to determine how the four pins should be connected. On each side of the flat spot, the two pins are connected together. When the switch operates, it opens and closes the connection between the two pairs of pins as shown below.
When installing the button on the breadboard, it should be oriented so switch contacts #1 are in one of the 5-hole rows on the breadboard, and switch contacts #2 are in another 5-hole row. The two pairs of contacts must be in different rows of five hole breadboard holes. When done that way, the switch opens and closes the connection between the two rows of the breadboard holes.
We used LEDs in Labs 1 and 2 connecting them to electronic circuits. In this lab you will be connecting one to a port bit of the Arduino configured to be an output and your program will determine whether it is on or off. As before, the LED will need a resistor in series with it to limit the amount of current flowing through the LED. Recall that in Lab 1, the current through the LED and resistor was given by
I = (Vs - VLED)/R
where Vs is the source voltage, VLED is the voltage drop across the LED and R is the resistance. When an output bit is in the high state the voltage on the pin is close to 5V so we can assume Vs = 5V, and VLED is usually close to 2V. We can make the LED glow a bit brighter than in Lab 2 by using a smaller 220Ω resistor. This will result in a current through the LED of
I = (Vs - VLED)/R = (5 - 2)/220 = 13.6mA
The LED must be oriented so the anode (positive end) is towards the Arduino and the cathode (negative end) is towards ground. The resistor can be in series with the LED either between the Arduino and the LED, or between the LED and ground.
Use your Arduino Uno and breadboard to build the circuit shown below.
- The three switch inputs should be wired to the Arduino inputs D2, D3 and D4, which are connected to Port D, bits 2, 3 and 4 of the microcontroller.
- The LED and current limiting resistor should be connected to pin D8 of the Arduino, which is Port B, bit 0 of the microcontroller.
- The ground wire from the Arduino to the breadboard should go to one of the columns of holes that forms the bus that runs the length of the breadboard. Use one of the buses by the blue line for ground. The ground connections to the switch and the resistor should all be made to that bus column.
Do the following steps to get started.
- In your ee109 folder create a lab3 subfolder.
- Download the file "template3.c" from the class web site. Put it in the lab3 folder and rename it "lab3.c". Remember the command mv (Macs) or rename (Windows) can be used to rename a file (e.g. mv template3.c lab3.c
- Copy the generic Makefile from your ee109 folder into the lab3 folder. The lab3 folder should now contain two files: Makefile and lab3.c.
- Edit the Makefile to change the name of the project object file on the OBJECTS line to be "lab3.o". Make sure you are editing the one that is the lab3 folder. Also edit the PROGRAMMER line as done before to be compatible with your computer.
You're now ready to starting adding code to lab3.c. Your program should do the following operations.
- At the top of main(), configure the appropriate Port B bit as an output (for the LED).
- Next, configure the appropriate Port D bits which are inputs to have their pull-up resistors enabled.
- Start an infinite loop where each iteration does the following:
- Examine the state of button 1 to see if it has been pressed (using the
checkInput()function). If so send the pattern of dots and dashes for the character 'U' by making Port B, bit 0 go high and low at the correct times. Use calls to the
dash()functions ) which you will implement and which are described below) so that this section of code is fairly "clean" and understandable.
- Do the same thing for button 2 to send an 'S'.
- Do the same thing for button 3 to send a 'C'.
- Ensure the appropriate delay after the character is finished and before the next begins.
- Examine the state of button 1 to see if it has been pressed (using the
The lab3.c template file already has code provided for checking a bit on Port D to see if it is a zero or one.
- This routine checks the state of an input bit in I/O port D (which is where the three buttons are connected). The argument to this function is a number from 0 to 7 that tells which bit to check. If the port bit is a zero, the function returns 0, otherwise it returns non-zero.
The file also includes empty skeleton functions for a few other routines that you will have to finish writing.
- This routines sets or clears the output bit the LED should be connected to (Port B, bit 0). If the argument is zero, the PB0 is cleared to 0, otherwise it is set to 1.
- This routine control the LED to generate a dot. It should turn the LED on, delay the correct amount of time and then turn the LED off and delay.
- Similar to the dot() routine but generates the longer dash.
To create the dots and dashes of the correct lengths, the program will have to execute delays during execution. To implement delays in the program use the _delay_ms function. For example the following line would cause a 300msec delay.
The argument to the _delay_ms function must be an integer value. In addition, it should be a constant value, not a variable. The avr-gcc compiler allows variables on some types of systems but on others they can not be used.
When generating the patterns of dots and dashes make sure to use the proper lengths as defined above. In your program you should choose what delay amount to use for the length of the Morse code dot. All the other timing amount (dashes, gaps between dots and/or dashes, gaps between characters, etc.) are based on the length of the dot. Select a dot length that makes the output code readable, not too fast and not too slow. Your program must include the delay after a character is generated. The program should not begin sending the next character until after the proper delay from the end of the previous character.
To help with this, we have defined the fundamental time unit as DOT_LENGTH at the top of your program. Please put all delay lengths in terms of this constant so that it can easily be changed later.
#define DOT_LENGTH 250 /* Define the length of a "dot" time in msec */
If you feel 250msec is too fast or slow, experiment a bit and come up with one you prefer.
You can add whatever other functions you find useful to the program. Try to make the code easy to understand and modify. Make sure to add comments to the code so someone looking at it can easily see what you are doing.
In the process of getting your program working it's recommended that you first confirm that you can simply control the LED from the switches. Before adding the code to generate the Morse code dots and dashes, first just have the program turn the LED on if button 1 is pressed and turn it off if the button 1 is not pressed. If this works, change the code to have it work with button 2, and then try it with button 3.
Your goal is to eliminate possible sources of problems by testing parts of the whole thing before they all have to work together. If you just build the final circuit and try it with the full program, what do you do if nothing works? Is it a problem with the buttons, with the LEDs, with the program, or what?
Once you have the program working and the LEDs are blinking the Morse code for 'U', 'S' and 'C', use the oscilloscope to examine the output signal and confirm that the timing relationships between the dots, dashes, and the spaces between them is correct.
- Turn on the scope and connect the two probes to channels 1 and 2.
- Turn on both channels by pressing the '1' and '2' button until they are lit.
- Set the vertical control for both channels for 2 volts/division.
- Use the vertical position controls to put the yellow channel 1 line in the upper part of the display just above the center of the screen, and the green channel 2 line in the bottom part.
- Set the horizontal control to 500ms per division. This can be changed later to get a good view of signals.
- Channel 1 will be used to view the button press signal. Connect probe 1 to the 'C' button output where the wire is going to the PD4 input bit.
- Channel 2 will show the output signal going to the LED. Connect probe 2 to the anode of the LED where the wire from the PB0 output bit is connected to the LED.
- You need the scope to trigger when the button is pressed so adjust the trigger settings so the scope triggers on a falling edge on channel 1:
- Set "Trigger Type" to "Edge".
- Set "Source" to "1".
- Set "Slope" to a falling edge.
- Adjust the trigger level to about 2 volts.
- The scope should now be ready to trigger when the 'C' button is pressed. Press the "Single" button in the upper right to make it wait for the next trigger signal, and then press the 'C' button on your breadboard.
If everything is working, you should get a display like that below. Depending on your choice of the dot delay time the display may be compressed or stretched out so use the horizontal controls to adjust the scope timing and position to get a good view of the signal. Check that the timing is correct for the dots, dashes and the gaps between them.
We want to observe and measure the propagation delay from when a button is pressed to when the output starts changing as the dot and dashes are sent. If you followed the instructions above to view the 'C' character then the scope is already mostly configured to make this measurement.
- Press the horizontal knob to bring the trigger point back to the center of the screen. We want to look at the signals right after the triggering point.
- Adjust the horizontal timing control to 5μs per division
- Press the "Single" button in the upper right to make it wait for the next trigger signal, and then press the 'C' button on your breadboard.
From the scope display, how long did it take from the time the button input went to zero before the LED output went high? Repeat the test about 8-10 times and make a note of the times. For each test you will have to wait for the Arduino to finish outputting the 'C' character before you can start a new one.
Last week we built a combinational circuit with two button inputs and an LED output. If we had measured the propagation delay through that circuit from button to LED it would have been around 20ns, and and since the gates never change, the delay would have been fairly consistant (i.e. always around 20ns). However, with the Arduino you should be seeing different delays every time the button is pressed. Can you explain why the propagation delays vary from one test to another?
Also, notice the delay difference from last week when we implemented a similar circuit with hardware and the delay was on the order of 20ns. Using software it takes on the order of 1-10 microseconds. That is about two orders of magnitude difference. Software takes much longer because our loop is composed of many software instructions that the processor must fetch from memory, decode and then execute. You can see that if computation needs to be done quickly, hardware has advantages!
Once you have the assignment working demonstrate it to one of the instructors including showing the scope display of the timing of the "C" character and your measurements of the propagation delay. Have them fill out a grading rubric and turn in a copy of your source code (see website for details.)