2021-22 Sem 2 Project 6: PWM and servo motor control

Project duration: 2 weeks
Project type: individual (but feel free to help each other)

In this project, you will use pulse width modulation (PWM) to control the brightness of an LED and the angle of a servo motor. You will program the Arduino Nano to control the period and duty cycle (pulse width) of the PWM signals it produces. You will use the oscilloscope to verify that the PWM signals are being produced as expected.

The ATmega328P microcontroller (which the Arduino Nano is based on) provides a vast array of different functionality, including numerous options for producing PWM signals (and related signals). In this project, we program the ATmega328P at the register level to exploit a small part of this functionality. However, we’re really only scratching the surface, so you are strongly encouraged to use this opportunity to begin exploring the ATmega328P datasheet to learn about the many other available options. In particular, try to use the datasheet to make sense of lines of code you don’t understand in the example programs provided below.

To do:

  1. Download the ATmega328P datasheet PDF and explore the chapter entitled “16-bit Timer/Counter1 with PWM”.

What to submit for this project

There are four parts to this project. Each part specifies an evidence item you will need to submit.

  • The first three evidence items consist of photographs (details below). Please collect all of these in a single Word document. Include your name and student number at the beginning of the Word document.
  • The fourth evidence item is a video file, which can be uploaded to Brightspace separately.

Only two files should be submitted to Brightspace: the Word document and the video file.

Part 1: Circuit construction

Build the following circuit on your breadboard. Your lab supervisor will provide you with a servo motor. Please note that the servo motor must be returned at the end of the each lab session.

To do:

  1. Build the following circuit on your breadboard.
  2. Take a clear photograph of your breadboard circuit.

EVIDENCE ITEM 1: Add your breadboard photograph to your report document.

Servo motor with 3-pin connector for attaching to breadboard.

Part 2: Control LED brightness

The simplest way to produce PWM signals with the Arduino Nano is using the analogWrite() function from the Arduino library. As explained in the analogWrite documentation, this function can only be used with certain digital i/o pins. When pin D3 outputs PWM (as in the example below), the frequency of the PWM signal is fixed at 490 Hz.

The example below cycles the LED through four different levels of brightness by varying the duty cycle of the PWM signal (0%, 20%, 50% and 100%).

To do:

  1. Upload the following code to the Arduino Nano and verify that the LED brightness is varying as shown in the example video provided below.
  2. Use the oscilloscope to measure the frequency of the PWM waveform.
  3. Using the oscilloscope, take a clear photograph of the PWM waveform during each of the four phases of the sequence.

EVIDENCE ITEM 2: Add the 4 oscilloscope photos to your report document.

//
// Control LED brightness using the Arduino library function analogWrite()
// Written by Ted Burke 25-Apr-2022
//

void setup()
{
  pinMode(3, OUTPUT); // Set pin D3 as an output (same pin as OC2B)
}

void loop()
{
  // Cycle LED through various levels of brightness
  // When using analogWrite() function, pulse widths are from 0 to 255
  analogWrite(3, 0);   // LED not lit (0% PWM duty cycle)
  delay(2000);
  analogWrite(3, 51);  // LED dimly lit (20% PWM duty cycle)
  delay(2000);
  analogWrite(3, 127); // LED brighter (50% PWM duty cycle)
  delay(2000);
  analogWrite(3, 255); // LED at maximum brightness (100% PWM duty cycle)
  delay(2000);
}

Part 3: Servo motor control

The type of servo motor provided to you in this project allows the motor angle to be controlled using a PWM signal. The angle range is between 0 and 180 degrees. The servo has three wires:

  • BROWN WIRE: ground (0 V)
  • RED WIRE: positive supply voltage (5 V)
  • YELLOW WIRE: PWM control signal (switches between 0 V and 5 V)

The PWM control signal has the following properties:

  • The PWM period is fixed at 20 ms (equivalent to a PWM frequency of 50 Hz, or 50 pulses per second).
  • Angle is controlled by pulse width.
  • The pulse width varies between 0.5 ms and 2.5 ms.
  • 0.5 ms corresponds to a servo angle of 0 degrees.
  • 2.5 ms corresponds to a servo angle of 180 degrees.

The following example program outputs PWM from the OC1A pin of the ATmega328P microcontroller (Arduino pin D9). The PWM period is fixed at 20 ms. The pulse width switches between 1.0 ms and 2.0 ms depending on the voltage detected on pin D2, which is controlled using the external switch S1 (or a piece of wire, as shown in the video below).

To do:

  1. Upload the code to the Arduino Nano and verify that the servo angle is changing as expected when the voltage changes on pin D2. The video below illustrates what you should see.
  2. Use the oscilloscope to measure the frequency of the PWM waveform on pin D9.
  3. Use the oscilloscope to capture a clear photograph of the PWM waveform for each of the two servo positions.

EVIDENCE ITEM 3: Add the 2 oscilloscope photos to your report document.

//
// Control of PWM frequency and duty cycle using Arduino Nano / ATmega328P
// Written by Ted Burke 25-Apr-2022
//
// This example uses Timer 1, which is a 16-bit timer
// System clock frequency F_clk = 16 MHz
// Timer 1 prescaler is set to 1/8
// 16-bit register ICR1 controls PWM period (T_pwm = 20 ms)
// 16-bit register OCR1A controls PWM pulse width (i.e. duty cycle)
// PWM output is on pin OC1A (same physical pin as D9)
// Pin OC1B (same pin as D10) is not used in this example
//

void setup()
{
  // Configure Timer 1 for Fast PWM with T_pwm = 20 ms
  // COM2A1:0 = 10  -> Clear OC1A on compare match (set output to low level)
  // COM2B1:0 = 00  -> OC1B disconnected, normal port operation
  // WGM13:0 = 1110 -> Fast PWM wave generation mode with ICR1 controlling period and OCR1A controlling duty cycle
  // CS12:0 = 010   -> Prescaler = 1/8, i.e. set Timer 1 clock to F_clk/8
  TCCR1A = 1<<COM1A1 | 0<<COM1A0 | 0<<COM1B1 | 0<<COM1B0 | 1<<WGM11 | 0<<WGM10;
  TCCR1B = 1<<WGM13 | 1<<WGM12 | 0<<CS12 | 1<<CS11 | 0<<CS10;
  TCNT1 = 0;          // reset Timer 2 counter
  ICR1 = 39999;       // Set PWM period Tpwm = 20 ms, OCR1A = (Tpwm * F_clk * prescaler) - 1
  OCR1A = 3000;       // Set initial PWM pulse width to 1.5 ms, Note: 1000 = 0.5 ms, 5000 = 2.5 ms
  pinMode(9, OUTPUT); // Enable OC1A (same pin as D9) as an output
}

void loop()
{
  if (digitalRead(2) == 1) // switch is closed
  {
    OCR1A = 2000; // pulse width = 1.0 ms, roughly 45 degree angle
  }
  else
  {
    OCR1A = 4000; // pulse width = 2.0 ms, roughly 135 degree angle
  }
}

Part 4: Modify the program to produce a pre-programmed sequence of movements

The video below shows the circuit performing a repeating pre-programmed sequence.

  • The servo angle increases by 10 degrees every second.
  • When the servo angle increase, the LED brightness also increases.
  • When the servo angle reaches 180 degrees, it resets to 0 degrees. The LED brightness also resets to 0.

To do:

  1. Write an Arduino program that reproduces the same pre-programmed sequence.
  2. Record the video of your circuit performing the sequence.

EVIDENCE ITEM 4: Submit the video of your circuit reproducing the specified sequence of movements to Brightspace.

You may find the following helper function useful. It allows the servo to be moved to a specified angle (in degrees) with a single function call.

void set_angle(float angle)
{
  // Pulse width variables
  float pw;              // pulse width
  float pw_min = 0.5e-3; // minimum pulse width is 0.5 ms, which sets angle to 0 degrees
  float pw_max = 2.5e-3; // maximum pulse width is 2.5 ms, which sets angle to 180 degrees

  // Calculate pulse width and then use it to update OCR1A
  pw = pw_min + (angle/180.0) * (pw_max-pw_min);
  OCR1A = pw * 16e6 / 8;
}