This post is about using the ATmega328P microcontroller for Pulse Width Modulation. While the subject is covered pretty well in the device's data sheet, some clarification and simplification is in order. This is my intention here. Please have the datasheet available. It can be found here. Select the first pdf file.
What is Pulse Width Modulation
If you landed on this page you probably know, so this explanation will be really short. While you can control a DC motor by controlling the voltage it is connected to, it is usually more precise and actually easier to control the motor by repeatedly turning it off and on. The motor responds to the average time the voltage is applied. You apply the motor voltage as a string of pulses with a defined frequency and a defined pulse width. If the duration of the pulse is half of the duration between pulses, the PWM will be 50% and the motor should turn at 1/2 speed.
To Drive A DC Motor
My last blog entry used Timer/Counter 1 in Normal mode to produce a timed delay. The timed delay function only had to use the timer/counter to do one thing: count up to a value and issue an interrupt. Pulse width modulation has two requirements. It must establish a frequency of pulse repetition and control the width of the pulses. While interrupts can be generated, PWM typically controls the logic level of a pin, or pins, that connect to external devices. Let's start with the dc motor being that external device.
We have plenty of options to consider. The first is whether we can use an eight bit timer/counter or do we need a 16 bit timer/counter. The answer is we probably don't need all of that resolution to control a dc motor. We can use ATmega's timer/counter 0 or 2 - both 8 bit circuits. Let's use timer/counter 0.
Next we need to decide between two major PWM modes. One mode gives twice the maximum frequency of pulse repetition as the other. This "Fast PWM Mode" operates by having the counter count up, and when it reaches its upper value, the counter starts over at zero. If you were to graph count against time, you would see a sawtooth wave. The other mode, "Phase Correct PWM Mode" has the counter count up, and when it reaches its upper value, count down at the same rate. When the count reaches 0 it counts up again. Graphing this looks like a triangular wave. See my graphic below.
Why are there two modes? If you never change the motor speed, either mode is fine. If you do change the motor speed, the Phase Correct PWM mode always has the center of the pulse at exactly the same place in time (if you keep the pulse repetition frequency the same and only change the pulse width). When changing the speed in Fast PWM the center of the pulse will move. Phase correct PWM results in a smooth transition in speed, while Fast PWM is more abrupt. Motors like the former over the latter. My example will use the Phase Correct PWM
With that settled, we have to deal with the frequency of pulse repetition. I don't know enough about DC motors to say what is the best frequency of pulse generation. I guess if the frequency is too low, the motor could lose speed between pulses. I don't know if there is a down side of too high a frequency. The higher frequency could limit the resolution of the speed differences we can program. This will be clear soon. The frequency of pulse repetition rate is determined as follows:
Pulse repetition frequency = clock speed divided by the prescaler and divided again by the maximum count set into the timer counter and finally divided by 2
My clock speed is 12MHz and the possible prescaler values are 1, 8, 64, 256, 1024 (allowable values for timer/counter 0). The lower the prescaler value the higher the frequency. The lower the count also gives you higher frequency. A little drawing is in order here:
The slope of the triangular waveform is determined by the clock frequency and the prescaler value. The clock frequency is your system clock so is not a variable. The prescaler value is, however, programmable. The horizontal line at the top, "Frequency" is controllable by your program and represents the maximum count in the timer/counter. It should be clear that changing the maximum count changes the pulse repetition. If you lower the count, the peaks of the triangles move to the left decreasing the time between pulses. Generally, when you decide on a pulse repetition rate, you would keep that constant.
The lower horizontal line, "Pulse Width", represents the width of the pulses. Typically, this value changes during your application. For example, if your project is a car with dc motors controlling two wheels, you frequently alter the speed of the car. And, you could steer the car by differentially changing the speed of each motor. Therefore, the width of the pulses frequently change. The graphic above should make it clear how the width of the pulses are changed. Lower the horizontal line and the width of each pulse decreases (assuming the we are talking about the positive going rectangle).
Note that the count for the "Pulse Width" must be less than the "Frequency" count. The "Frequency" count then determines the resolution of the pulse width modulation. If the "Frequency" count is the maximum allowed, which is 255 for timer/counter 0, each count will correspond to a pulse modulation increment of about 0.4%. If you lowered that "Frequency" count to 10, you would only be able to program PWM in 10% increments.
There is another programmable selection. The "Resulting Pulses" in the graphic are the result of selecting: clear (go low) on a compare match when counting up, and set (go high) on a compare match when counting down. The compare match value is represented in the above graphic by the line "Pulse Width". You can also program the opposite action: set on a compare match when counting up, and clear on a compare match when counting down. If you program that option, the "Resulting Pulses" waveform would be turned upside down.
Putting It Together In a Script
I did a small sample project where I control a toy DC motor. Since my ATmega328P resides on my Gertboard, I connect the motor and a 6V battery (4 AA's) to the BD6222HFP H-Bridge motor controller. This isolates the motor and it's power source from the PI and the Gertboard. My project increases the motor speed from off to full on, decreases the speed to off, changes the direction of the motor, increases the speed to the max and reduces it to off again. This action is repeated continuously.
There are only two timer/counter 0 modes for Phase Correct PWM, mode 1 and mode 5. My project uses mode 5. Compare register OCR0A controls the "Frequency". If I use mode 1, the "Frequency" count would be fixed to 255. Using mode 5, I can select any value from 0 to 255. As it so happens, I chose 255 anyway. If I had two motors to control, I would choose mode 1. In that case, compare register OCR0A controls one motor and OCR0B controls the other. Pulse repetition frequency would be the same for both motors but each would have it's own PWM percentage. They could turn in the same or opposite direction from each other.
The pulse repetition frequency of this project is 12,000,000 / (2 * 64 * 255) = 368 repetitions per second.
The H-Bridge takes two inputs. One input gets the pulses from pin PD5 while the other input comes from pin PD7. PD7 will determine the direction of rotation just like reversing the leads to the motor. When PD7 is low, we will select clear on a compare match when counting up, and set on a compare match when counting down. When PD7 is high we select set on a compare match when counting up, and clear on a compare match when counting down. You can picture how this works by considering how you would stop the motor. The motor stops if both PD5 (PWM = 0%) and PD7 are the same polarity.
The script below has a lot of comments (more comments than code) so I think the details are well presented within the script.
No comments:
Post a Comment