Overview
In this lab, you will be designing a “Morse code decoder”. This is a device that allows you to translate a Morse code signal into its alphanumeric representation. Morse code consists of dashes and dots separated by spaces; different sequences of these signals each correspond to an alphanumeric letter. Check out the Wikipedia article on Morse code to learn more.
You will build this decoder by first detecting an incoming signal using an input switch (first with polling, then with interrupts). Then, you will display the translated message; you will light LEDs based on what signals you receive and write out the message in its ASCII form to a serial port for you to read.
The lab consists of answering a series of questions and submitting a code repository. All the required questions are highlighted in blue. Make a copy of the submission document and record your answers there. Since this lab requires a few video demonstrations, make sure that your links work .
Learning Objectives
After this lab, you should understand the following:
• Build a circuit on a breadboard involving switches and diodes
• Work with the digital I/O ports available on a microcontroller
• Program a microcontroller in C at the register-level
• Implement event-based polling
• Utilize hardware interrupts and timers for event handling
• Write ASCII data to a serial port
Required Equipment
• Hardware
• Elegoo Kit
• Arduino
• LEDs
• Pushbutton Switch
• Wires
• Resistors
• Software
• AVR Toolchain
• It is highly recommended to install Atmel Studio. Refer to the Resources page to set up your programming environment if you haven’t already done so. There are guides to set up your environment to program your microcontroller on different platforms.
• Github Repository
• Click on this link to create your repository on Github Classroom. To facilitate easier grading, your repository name should include your pennkey. Please rename your repo to be labx-pennkey.
Part A: Warming Up with LEDs
We begin with a prelude to Lab 2. Even though this is a bit of a lengthy warm-up, it will hopefully ease you into working with push-buttons and LEDs, in addition to writing embedded C code for a microcontroller (and making sure that your environment is properly set up).
1. Lighting Things Up (safely)
First, we will set up the LEDs so that we can drive them with our microcontroller.
Connect four LEDs to Pins 9, 10, 11 and 12 on the Arduino Uno along with a current limiting resistor each.
Figure 1 – LED Resistor Circuit
To get a better understanding of the Arduino pins, check out the pinout diagram on our Resources page.
It may help to sketch out a circuit showing the connections before you wire them up on the board (since you’ll have to submit your schematic for this part, anyhow). Also, try using the red/blue/green/yellow LEDs in your kit to easily tell them apart!
Make PB1, PB2, PB3, and PB4 output pins and pull them all high. All of your LEDS should turn on. Refer to the GPIO lecture for guidance.
Recall that manipulating a register is analogous to overwriting bit(s). For example, if I wanted to set PB1 and PB3 to high on PORTB, then I could write PORTB = 0x0A (since 0x0A = 0b00001010), but note that this clears the other bits. If I wanted to keep the values of the rest of the pins intact, then I could write PORTB |= 0x0A instead.
Note that there is another way to set a register. If I want to set only PB1 high, for example, then I can just do PORTB |= (1 << PORTB1), which will use a macro to set the pin. That way, you don’t have to manually figure out the hex values to set the port to!
• Hooray! You just wrote your first AVR C code! Take a picture of those fantastic LEDs with their lights on and attach it to your submission document.
• Take a screenshot of your code. It should only be a few lines. No need to include the “include
Play around with turning different combinations of LEDs on and off so you get a bit more comfortable manipulating bits.
2. Pushing Buttons (politely)
Now, set up a switch to control the LEDs. Using one of the switches/buttons in your kit, use a 10kΩ resistor to make the input connection to PD7 similar to the circuit below. The button can be wired on the high side or low side. Make sure your logic is consistent with the behaviour that you are expecting – e.g. button press = HIGH, button released = LOW.
Figure 2 – Push Button Circuit
Write a C program that toggles an LED when the button is pressed by reading the value of PD7.
Remember to set PD7 as an input pin. You can choose whichever LED port to turn on to detect the button press.
• Take a short video demonstrating your code.
• Does the LED turn on and off as expected when you press the button? If not, explain the behaviour observed.
• Take a screenshot of your code. It should only be a few lines. No need to include the “include
With the ability to read from a pin and write to a pin, you can now make your LEDs turn on and off based on how many times you’ve pressed the button!
Write code so that each time you press the button, the next LED in sequence turns on and all others are off.
That is,
• Initially, only the LED on pin 9 (PB1) should be lit.
• If you press the switch once, then only the LED on pin 10 (PB2) should be lit.
• If you press the switch again, then only the LED on pin 11 (PB3) should be lit.
• If you press the switch once more, then only the LED on pin 12 (PB4) should be lit.
This behavior should repeat once you press the button a fourth time, and so on. You may have to press the button and then hold it for a second before releasing it. (We haven’t gotten to debouncing yet, but if you are familiar with the concept, feel free to implement it. You will not be penalized if there is no debouncing in your code.)
• Take a short video demonstrating your code.
• Does the LED turn on and off as expected when you press the button? If not, explain the behaviour observed.
• Take a screenshot of your code. It should only be a few lines. No need to include the “include
Tip: You can add a delay using _delay_ms() (which takes in a parameter of milliseconds), so that you can see each LED light up between each press. Otherwise, the program may cycle through the different states too quickly for you to see.
• Use CircuitLab to draw your schematic. Attach an image to your answer to this question.
Part B: Polling for a Press
Now, onto the core of the lab!
We will start out by implementing a relatively straightforward circuit in which we will detect whether a button has been pressed, with our microcontroller waiting for an event (i.e. polling).
Begin by hooking up a low-asserting input switch to input port PB0 (i.e. Pin 8).
You might find it useful to leave your hardware from Part A and do some rewiring as needed. Make sure that the port is pulled high when the switch is not pressed.
We will output through PB5, which connects to the magical onboard LED on the Arduino board, to confirm that your polling setup works.
To test the hardware configuration, write a simple input polling program to light the LED whenever the button is pressed. Save this file as “partb.c”.
• Describe your approach to implementing polling and how this differs from the approach of simply reading the pin in the previous part.
Part C: Now, Using Inte
rrupts
Instead of using polling, which can be inefficient since it locks the CPU until an event has occurred, we will now utilize hardware interrupts via interrupt service routines.
Write an interrupt-based program that will respond by lighting the on-board LED appropriately to input capture events (i.e. it should provide the same functionality as before, except it will use interrupts rather than polling).
Note that when port PB0 is used for input capture in this fashion, it is referred to as ICP1 in the literature. Refer to the input capture lecture for guidance.
The microcontroller will need to respond to both the high-to-low (button depressed) and low-to-high (button released) transitions in order to turn on and off the LED at appropriate times. To do this, set the appropriate edge control bits in the TCCR1B register and enable the interrupt in the TIMSK1 register and call sei(). Also, make sure to enable the clock using the TCCR1B register.
The interrupt service routine will respond by clearing the input capture flag in TIFR1 by storing a 1 to the ICF1 bit. Reading PORTB, declared as PINB, will determine whether the button was depressed or released, and the routine should set the LED on PORTB accordingly. Note that to write the interrupt routine, you should declare a function with the signature ISR(TIMER1_CAPT_vect).
Write code to accomplish the task described above. Save this file as “partc.c”.
• Give an advantage and a disadvantage to using interrupts over polling for this task.
Part D: Dash or Dot
Now that we are able to determine whether the button was pressed using interrupts, we will figure out whether we have received a dash, a dot, or a space based on the length between presses.
For this lab:
• A dot is defined as the button pressed for approximately 30ms to 200ms
• A dash is defined as the button pressed for approximately 200ms to 400ms
• A space is defined as the button not pressed for longer than 400ms
• For a 16MHz clock, how many “ticks” are in 30ms, 200ms, and 400ms?
In order to determine the time, it is necessary to examine the TCNT1 register. However, with the clock running at 16 MHz, and the TCNT1 register being a 16-bit register, the TCNT1 register will overflow approximately every 4ms. Thus, in order to properly time events longer than 4ms, a timer overflow interrupt (enabled in the TIMSK1 register) or use of the prescaler in TCCR1B is necessary to keep track of the more significant bits of time.
Since serial communication will not be covered until later lectures, a short print_to_serial library will be used. Download the files here and look at the example in main to see how to print to the serial monitor.
The microcontroller should determine whether there is a dot, dash, or space and write the resulting characters to the terminal. Save this file as “partd.c”.
• Take a short video demonstrating your code.
You can debug the code by having the microcontroller print out the timer variables as well as the transition times on each press and release of the button. Notice that sometimes there are several events of very short duration – this is due to mechanical bouncing of the contacts on the switch, which you may have already noticed in Part A. By ignoring these short transition times, the program should be able to handle debouncing robustly.
• Describe how a prescaler allows us to work with a wider range of frequencies on our microcontroller.
Part E: Putting It Together – LEDs and ASCII
We are now able to detect whether we have received a dash or a dot. With this information, it is now time to show some output!
We will use LEDs to show whether we received a dash or a dot and then print each character received to the serial port.
Connect two different colored LEDs to the Arduino. Light one up if a dash is received and light the other one if a dot is received. The LEDs should only be lit for a brief period of time after the button press (about 50ms or so).
Now, program the microcontroller to decode the dots and dashes into actual alphanumeric characters. Have the program write out each character’s ASCII value to the serial port so that you can transmit a message successfully.
A listing for Morse code translations is provided below. Look for a single space (i.e. no button press for >400ms) in a dot/dash sequence to separate each character from one another. Save this file as “parte.c”.
• Take a short video demonstrating your code.