mbed Starter Kit Experiment Guide

Pages
Contributors: ShawnHymel
Favorited Favorite 4

Experiment 2: Buttons and PWM

Now that you have had a chance to set up your mbed account and blink an LED, we will move on to simple human interaction: pushbuttons! In this tutorial, we add 3 pushbuttons to the LPC1768 and use them to control the colors in an RGB LED.

mbed pwm rgb circuit

The Circuit

This circuit can be made with parts in the SparkFun mbed Starter Kit. Also, keep in mind that the LPC1768 box contains a USB mini-B cable for programming and power.

Parts List

Schematic

mbed Buttons and RGB LED schematic

Click on schematic to view larger image.

Connections

Connect the LPC1768 to the buttons and LED in the following fashion.

Pay special attention to the component’s markings indicating how to place it on the breadboard. Polarized components can only be connected to a circuit in one direction. Polarized components are highlighted with a yellow warning triangle in the table below.

Fritzing Diagram

mbed buttons rgb led fritzing

Be careful with the direction of the LED (polarity matters!). In the diagram, the flat edge (see the Tips section below) is facing to the left.

Note that the colors of the wires do not matter. Feel free to use any color you like! Do not worry if your kit does not have 5 black wires.

Hookup Table

Place the LPC1768 in the first breadboard with pin VOUT in position i1 and pin 20 in position b20.

Connect the rest of the components as follows:

Component Breadboard 1 Breadboard 2
b25 (RED) b26 (GND) b27 (GREEN) b28 (BLUE)
330 Resistor e25 g25
330 Resistor e27 g27
330 Resistor e28 g28
Pushbutton d6 d8 g6 g8
Pushbutton d14 d16 g14 g16
Pushbutton d22 d24 g22 g24
10K Resistor i6 ( + )
10K Resistor i14 ( + )
10K Resistor i22 ( + )
Jumper Wire j1 ( + )
Jumper Wire a1 ( - )
Jumper Wire a5 h6
Jumper Wire a6 h14
Jumper Wire a7 h22
Jumper Wire j20 j25
Jumper Wire j19 j27
Jumper Wire j18 j28
Jumper Wire a26 ( - )
Jumper Wire a8 ( - )
Jumper Wire a16 ( - )
Jumper Wire a24 ( - )

Tips

Pushbuttons

The leads across each other on the pushbuttons are always connected to each other. The leads on the same side are only connected when button is pressed.

push button diagram

LED

You can use a set of needle nose pliers to bend the LED’s leads and a pair of cutters to fit the LED in the breadboard. Note that there is a flat side on the plastic ring around the bottom of the LED. This denotes the side with the pin that controls the red color. See this tutorial to learn more about polarity.

RGB LED common cathode annotated

Resistors

330 ohm resistors are given by the color code “orange orange brown.”

330 ohm resistors

330 ohm resistors

10K ohm resistors are given by the color code “brown black orange.”

10K ohm resistors

10K ohm resistors

The Code

If you have not done so already, sign into your mbed.org account. Go to the Compiler and create a new program. Leave the template as “Blinky LED Hello World” and give it a name such as “rgb_buttons.” Click OK and wait for your new program to be created.

Once that is done, click “main.cpp” under your project folder (e.g. “rgb_buttons”) in the left “Program Workspace” pane. Delete all of the code in main.cpp so you are left with a blank project (we still want to use the “Blinky LED Hello World” template, as it automatically links “mbed.h” for us).

blank mbed project

Program

Copy the following code into main.cpp of your rgb_buttons project.

language:c
#include "mbed.h"

// Define buttons
InterruptIn button_red(p5);
InterruptIn button_green(p6);
InterruptIn button_blue(p7);

// Define LED colors
PwmOut led_red(p21);
PwmOut led_green(p22);
PwmOut led_blue(p23);

// Interrupt Service Routine to increment the red color
void inc_red() {

    float pwm;

    // Read in current PWM value and increment it
    pwm = led_red.read();
    pwm += 0.1f;
    if (pwm > 1.0f) {
        pwm = 0.0f;
    }
    led_red.write(pwm);
}

// Interrupt Service Routine to increment the green color
void inc_green() {

    float pwm;

    // Read in current PWM value and increment it
    pwm = led_green.read();
    pwm += 0.1f;
    if (pwm > 1.0f) {
        pwm = 0.0f;
    }
    led_green.write(pwm);
}

// Interrupt Service Routine to increment the blue color
void inc_blue() {

    float pwm;

    // Read in current PWM value and increment it
    pwm = led_blue.read();
    pwm += 0.1f;
    if (pwm > 1.0f) {
        pwm = 0.0f;
    }
    led_blue.write(pwm);
}

// Main loop
int main() {

    // Initialize all LED colors as off
    led_red.write(0.0f);
    led_green.write(0.0f);
    led_blue.write(0.0f);

    // Define three interrupts - one for each color
    button_red.fall(&inc_red);
    button_green.fall(&inc_green);
    button_blue.fall(&inc_blue);

    // Do nothing! We wait for an interrupt to happen
    while(1) {
    }
}

Run

Click the “Compile” button to download a binary file with your compiled program. Copy that file to the LPC1768. You can choose to delete the previous mbed_blinky_LPC1768.bin or just leave it there. If you have more than one .bin file on the mbed when you press the restart button, the mbed will choose the newest file to load and run.

Press the LPC1768’s restart button. You should be able to press any of the 3 buttons on your breadboard to increment the red, green, and blue intensity of the LED. Note that when you reach the maximum brightness, the intensity resets to 0.

mbed running rgb led pwm

Concepts

What is going on in our program? We should understand a few concepts about our seemingly simple RGB LED and button program that are crucial in embedded systems.

Pull-up Resistors

We use 10kΩ pull-up resistors to prevent shorting 3.3V to ground whenever we push one of the buttons. Additionally, the pull-up resistors on the microcontroller (LPC1768) pin holds the pin at 3.3V by default until the button is pushed. Once pushed, the line is pulled down to ground, so the pin goes from 3.3V to ground. We can use that falling edge (3.3V to 0V) to trigger an interrupt within the microcontroller. To learn more about pull-up resistors, see our tutorial here.

Functions

If you are not familiar with C syntax, you might want to brush up on some of the basics.

In the above code, we created 3 functions: inc_red(), inc_green(), inc_blue(). Functions generally perform some action when called, and replace the much-maligned GOTO statement. They also allow us to re-use code without having to copy-and-paste sections of code over and over again.

Each time we call one of the functions (e.g. inc_green()), the lines of code within that function are called, and then the function exits, returning program execution to just after the function call.

Notice that we placed the three functions above main(). When the compiler looks at the code, it needs to have functions declared before they are used in the code (within main() in this case).

If you put main() before the functions, you would get a compiler error. That’s because the compiler does not know what int_red(), etc. are when it sees them in main(). Try it!

Objects

We are using C++ to write all of our programs. If you have never used C++ before, the syntax might appear a bit foreign. We recommend reading about some basic C++ syntax first.

In the program, we create objects for each of our buttons and LEDs. For example:

language:c
InterruptIn button_red(p5);

and

language:c
PwmOut led_red(p21);

button_red is an instance of class InterruptIn. Think of a class as a “blueprint” that lets us create any number of instances (i.e. objects). Each instance is tied to a variable (e.g. button_red). In this case, button_red is an InterruptIn object.

Objects have special properties that let us call functions within that object (called “member functions”) and manipulate data stored in the object (data in objects is stored as “data members”). For example, we create a PwmOut object named led_red. Later in the code, we call the member function led_red.write(0.0f), which tells the led_red object to perform some action (change the PWM of the associated pin in this case - see PWM below!).

We pass in the parameter p5 (a special variable known to the mbed compiler - it points to the location of pin 5 in software) when we create an InterruptIn object. This tells the mbed which pin to bind the interrupt to.

Now that we have created an InterruptIn object, we can call member fucntions within that object. The InterruptIn class defines a fall() member function, which allows us to set a function to be called whenever a HIGH-to-LOW transition occurs on the pin (pin 5 in this case).

language:c
button_red.fall(&inc_red);

Note that the ‘&’ symbol is used to indicate the address of the inc_red function.

Interrupts

Interrupts are an integral part of embedded systems and microcontrollers. Most programs that you might be used to run sequentially, meaning that the program executes each line in order (and jumps or loops where necessary). In contrast, many microcontrollers rely on subroutines known as “Interrupt Service Routines” to handle external and internal events.

In our mbed program, we “attach” an interrupt to our button pins and tie this interrupt to a function call (e.g. inc_red()). The line

button_red.fall(&inc_red);

says that whenever we see a falling digital signal (i.e. from 3.3V to 0V), we must stop whatever we were doing and execute the function inc_red(). This is similar to callbacks often found in programming but can be tied to external (i.e. outside the microcontroller) events.

YouTube user Patrick Hood-Daniel offers a great explanation of interrupts and some example code in a different microcontroller (Atmel AVR ATmega32):

PWM

To control the brightness of the LEDs, we do not vary the current going into the LED. Rather, we rapidly turn the LED off and on to give the appearance of a dimmer LED. This is known as “Pulse-Width Modulation” (PWM). Note that we are flipping the LED off and on so fast that generally our eyes cannot see the flickering.

We adjust the “duty cycle” of this flipping process in order to control the brightness. Duty cycle just refers to how long our LED stays on in relation to how long it stays off.

duty cycle example

The higher the duty cycle (e.g. 75% in the image above), the brighter the LED will appear to be. At lower duty cycles (e.g. 25%) the LED will hardly appear to be on. For a full tutorial on PWM, see Pulse-width Modulation.

In our rgb_buttons program, we use mbed’s built-in PwmOut object to control PWM on several of the LPC1768 pins. We can read the PWM value with the .read() method and set a new PWM value with .write(). We use a floating point number to specify the duty cycle. This can be between 0.0 (0% duty cycle) to 1.0 (100% duty cycle).

Going Further

We’ve covered three important concepts in this tutorial: user input with buttons (using pull-up resistors), interrupts, and PWM. If these topics provide some confusion, feel free to read through the “Digging Deeper” section to learn more about these topics in greater detail.

Beyond the Tutorial

  • See how the LED changes when you press a button down? Can you make it so that the LED changes when you release a button?
  • Since we are using interrupts, our while(1){} loop is empty. Make something happen while waiting for a button push! For example, flash another LED in that while loop.
  • Can you re-create the functionality of the program without using any interrupts?
  • You might notice that the LED changes accidentally sometimes when you release a button. This is caused by a phenomenon known as contact bounce. Can you create a way to remove contact bounce (known as “debouncing”)? This can be done in hardware or software.

Digging Deeper