EE109 – Spring 2023 Introduction to Embedded Systems

EE109 – Spring 2023: Introduction to Embedded Systems

Lab 8

Pulse Width Modulation

Honor Statement

This is an individual assignment. The code you write, use for the demo and submit should be wholly your own and not produced by working in teams or using, in part or in whole, code written by someone else (found online, from a fellow student, acquaintance, etc.). You will be asked to verify this when you submit your code on Vocareum by signing your name in the honor.txt file on Vocareum. Penalties for violation include a 0 on the assignment and a potential further deduction or referral to Student Judicial Affairs.

Introduction

In this lab exercise you will use a rotary encoder to control the generation of a pulse width modulation (PWM) signal from the Arduino Uno. In the first part of this lab we will use an 8-bit Timer/Counter to generate a PWM signal to control the brightness of an LED. The second part of the lab will involve using the higher resolution 16-bit Timer/Counter to generate a more precise PWM signal to control a servo motor. By adjusting the encoder position you'll be able to control the position of the servo.

For a copy of the Lab 8 grading sheet, click here.

Using Timers to Produce Pulse Width Modulation Signals

In previous labs, the Timer/Counter modules were used to implements time delays so that certain tasks would happen at a known rate, such as the 0.1 second counting rate of a stopwatch. When using the Timer/Counter modules in this way, they only interact with your program, usually by interrupts. The Timer/Counters can also be configured to produce signals that appear on one or more of the I/O pins and can be used to control external devices.

Each of the three Timer/Counter modules on the Arduino can produce output signals on two separate signals lines. Connections can be made to these signals just like with any other output from the microcontroller. The diagram below shows which I/O pins are used by the Timer/Counter modules when they have been configured to produce an output signal.

When working with the Timer/Counter modules the output signals are always referred to by the names shown above inside the boxes such as "OC0A" or "OC2B". It's important to be aware that that these signals share the connections to the microcontroller with the digital I/O lines we have been working with as shown above. For example OC2A is the same connection on the Arduino as the Port B, bit 3. At any one time, only one module, either the Timer/Counter or the digital I/O can be using the line. For example, if the Timer 1 is outputting a signal on the OC1A pin, then Port B, Bit 1 can not be used as a digital I/O by putting bits in the PORTB register.

One common use of the Timer/Counter modules in the Atmel microcontroller used on the Arduino Uno is to generate Pulse Width Modulation (PWM) signals. Please watch the video and review the slides that are linked on the Assignments page to learn more about pulse width modulation.

To generate PWM signals the timer has to perform two tasks. First it has to generate pulses with a constant period. The period of the pulses is the time from the 0→1 transition of one pulse until the next 0→1 transition. To generate pulses at a fixed period the timer has to count the clock signal until the pulse period has been reached and then reset back to zero and start over.

The second task for the timer is to control the width of the pulse which is the amount of time during the pulse period that the output is in the high or logic 1 state. The pulse output goes to the one state at the beginning of the pulse period, then at some point during the pulse period the timer must terminate the pulse by setting the pulse output to zero for the rest of the pulse period.

When working with PWM signals, your program configures the timer to generate the signals with the desired pulse period at the start of the program and never changes this setting since the pulse period is fixed value. As the program operates, it modifies or "modulates" the pulse width as needed to generate the proper signal.

PWM Control of an LED

Early in the semester you experimented with changing the brightness of an LED by altering the value of the current limiting resister so as to change the amount of current flowing through it. In that experiment the LED was glowing at all times but at different levels of brightness. In this task you will use a PWM signal to control the brightness of an LED by rapidly turning it on and off. When the LED is on, it is on at full brightness, but by varying the PWM pulse width we can controls how long it is on, and this changes the brightness of the LED as perceived by the human eye.

For this task we will be using Timer/Counter2, one of the 8-bit Timer/Counters. In the PWM mode we will be using, each period of the pulse is limited to the length of time it takes the timer to count from 0 to its maximum value of 255. The timer simply counts up to the maximum count value and then starts over. The relationship between count value, prescalar and time is given by

We will use the prescaler to divide the 16Mhz clock by 1024, so the period of our pulses will be

or

For this task we will be using Timer2 in what is called "Fast PWM" mode and this is illustrated below. At the start of the pulse period (count = 0) the OC2A output signal, which appears on Arduino port D11 (Port B, pin 3), will go to the high state to start the pulse. Register "OCR2A" is used to store a value that determines the pulse width by telling the timer when to terminate the pulse. During each pulse period, the timer counts up incrementing the count value, and when it reaches the value in OCR2A the output signal goes back to zero while the timer continues to count up to 255. The counter then resets back to zero and the process is repeated for the next pulse.

Your program will be have the job of changing the value in OCR2A in order to change the width of the pulse.

Task 1: Circuit for controlling an LED

The circuit for this lab is shown below. The rotary encoder is connected to the PC4 and PC5 inputs. Note that this is different from the inputs used in Lab 7 so make sure to rewire it correctly. The LED and current limiting resistor is connected to output PB3.

Task 2: Generate the PWM signal

For this lab you should be able to start with a copy of the lab 7 program that uses interrupts to handle the inputs from the rotary encoder. Do the following tasks.

For the checkpoint part of this lab we will be using the 'A' signal on input PC4 from from the rotary encoder as a switch to determine which of two PWM width signals to generate.

The main loop for the program should consist of the following.

Once the program is downloaded, rotating the encoder slowly should make the LED change brightness between two levels as the 'A' input changes and this causes the PWM width to change.

If the LED brightness is changing, turn on an oscilloscope and hook the probe to the LED output and confirm that the PWM signal is changing between 20% and 80% as the encoder is rotated. Show this to a CP to record that you have the checkpoint task completed for this lab.

Checkpoint: Show that rotating the encoder causes the brightness of the LED to change, and show the PWM signal on a scope to a CP for the checkpoint credit.

Task 3: Using the rotary encoder to adjust the PWM signal

In this task we will use the rotary encoder to change the width of the PWM pulse width smoothly between 0% and 100% of the period rather than just having it change between 20% and 80%.

Much of the code for this task can be reused from Lab 7 that was using the rotary encoder with interrupts. For the PWM output some changes will have to be made to the code.

PWM Control of a Servo Motor

The next thing we want to do is use the PWM signal to control a servo motor. Servo are designed to operate using a PWM signal with 20ms period, and the pulse width should only vary between 0.75ms to 2.25ms. In the task above where we controlled the LED, we used a pulse period of 16.4ms since that was the longest we could get from the 8-bit Timer/Counter2. In order to get the recommended 20ms pulse period, for this part of the lab we will use the 16-bit Timer/Counter1 to control the servo motor.

Timer/Counter1 has two internal 16-bit registers that are used to generate the PWM pulses. In the timer mode we will be using, register "OCR1A" is used to determine the pulse period by storing the value the timer should count to before resetting and repeating. Register "OCR1B" is used to store the value that determines the pulse width by telling the timer when to terminate the pulse. The pulse is produced on the OC1B output, which is Arduino port D10 (Port B, pin 2). The OC1B output will go high at the start of the pulse period (counter = 0) and when the timer reaches the value in OCR1B it will set the pulse output back to zero. The timer then continues to count until it reaches the value in OCR1A. At that point it resets to zero and starts the process over. This is illustrated the figure below. Needless to say, the value in OCR1B should be less than the value in OCR1A.

Before starting to write the code for this task, first figure out what values will be placed in the OCR1A and OCR1B registers to generate the PWM signal as specified above. Using the same process you used in the stopwatch lab, find a combination of a timer prescaler of either 1, 8, 64, 256 or 1024, and an OCR1A value that fits in 16-bits (<65,536), that gives a pulse period of 20msec. The count value to go in OCR1A can be calculated from

For example, with time=0.02 (20msec), find the count for a prescaler of 1. Then repeat the calculation for a prescaler 8, then for 64, etc., and determine which prescalers yield count values that are less than 65,536. There may be more than one combination of count and prescaler that work.

With the prescaler and OCR1A count selected you know the rate that the timer is counting at. Now use that same prescalar value in the above equation again to find the count values that will go in OCR1B to give delays of 0.75msec and for 2.25msec. These are the counts for the minimum and maximum pulse widths you will be using, and the encoder will be used to change the OCR1B value between these limits. Turning the encoder one way will cause the program to increase the value in OCR1B making the pulse wider, turning it the other will decrease the value to make the pulse narrower.

Task 4: Controlling a servo motor

In this task you will add code to your program that controls the LED so the encoder is controlling both the LED and the servo motor at the same time. As the encoder is rotated, the LED gets brighter or dimmer, and at the same time the servo motor rotates in one direction or the other.

Timer/Counter1 uses the OC1B output signal which is also used by the LCD. For the remainder of this lab remove the LCD from the Arduino. With the LCD removed you will have to reconnect the wires to the encoder and LED (PC4, PC5, and PB3) and ground to go directly to the Arduino. Once you have done that, confirm that the brightness of the LED can still be controlled by the encoder the same as it was when the LCD was attached.

Before modifying your program to control the servo motor, make a copy of your lab8.c just in case you have to go back to it for some reason. For the second part of the lab, the following tasks will need to be done.

When the program is running, rotating the encoder will change the PWM signal from Timer2 that control the LED, and also change the PWM signal from Timer1 that rotate the servo motor. Add code in the ISR or the main program to accomplish this.

To confirm that the pulses are being created as specified, use an oscilloscope to look at the pulses on the OC1B output (Port B, bit 2 or Arduino port D10). You should be able to see the pulse width change as you rotate the encoder. Check to make sure that

You must demonstrate your working PWM signals by showing them on an oscilloscope to an instructor. Do not connect the PWM signal to the servo motor until an instructor has confirmed that the signal is correct.

Connecting the Servo Motor

To complete this lab, get a servo motor from the instructors if you don't already have one in your lab kit. Each servo comes with a set of black plastic arms that can be placed on the servo motor's shaft. Pick one of them and press it down onto the motor's shaft. This will make it much easier to see the rotation of the motor.

When connecting the motor to your Arduino, first disconnect the power to your Arduino before connecting the motor. The cable to the motor has a three conductor connector with three wires going into it, red for power, black for ground and yellow for the PWM signal. Using a piece of breadboard hookup wire, make a connection from the +5V on the Arduino to the red wire by inserting the end of the wire into the connector socket for the red wire as shown in below. Do the same to connect the Arduino ground to the black wire, and from the Port B, bit 2 (D10) output pin to the yellow wire.

Once you reconnect the power to your Arduino the motor should move to the initial position as set by your PWM signal.

Try rotating the encoder knob and see if the motor responds correctly. Check to see that you can move the motor through an arc of about 150 degrees. The LED brightness should also change as the position of the motor changes.

Results

When your program is running you should be able to confirm the following

Once you have the assignment working demonstrate it to one of the instructors. The answers to the review questions below should be edited into the Lab8_Answers.txt file. The Lab8_Answers.txt file and all source code (lab8.c, lcd.c, lcd.h, and Makefile) must be uploaded to the Vocareum web site by the due date. See the Assignments page of the class web site for a link for uploading.

Please make sure to save all the parts used in this lab, including the servo motor, in your project box. These will be needed for labs throughout the rest of the semester. We suggest also saving all the pieces of wire you cut and stripped since those will be useful in later labs.

Review Questions

Answer the following questions and submit them in a separate text file ("Lab8_Answers.txt") along with your source code.

  1. Billy Bruin wants to control a servo motor like the one used in Lab 9 but only has the 8-bit Timer/Counter0 available to use to generate the PWM signal. He says this won't work since the maximum pulse period you can get from the 8-bit timer is 16.4ms and servo motors need PWM signals with a 20ms period. Tammy Trojan decides to try using the 8-bit timer anyway just to see if it will work. Assuming she sets it up for the maximum period of 16.4ms, what values will she have to put in the OCR0A register to generate the minimum pulse width of 0.75ms, and the maximum pulse width of 2.25ms?
  2. When Timer/Counter1 is counting, the count value is kept in the TCNT1 register and is constantly being compared for equality (and only equality) to the values in the OCR1B and OCR1A registers to determine when to terminate the PWM pulse or to end one pulse period and start the next. Suppose at some point your program adjusts the PWM width by changing the OCR1B register, and the new OCR1B value is lower than the value that is currently in the TCNT1 register. What will happen to the output signal during this pulse period?