Introduction
My last two posts concerned, what I call, Register Programming of the ATmega328P microcontroller of the Arduino Uno. My last post discussed the expanded capability of interrupts, over and above the functions in the Arduino IDE's reference language. This post concerns the use of the ATmeta328P's counter/timers to manage pulse width modulation (PWM). Pulse width modulation is used to control such things as the light intensity of LEDs and the speed of small DC motors when the intensity or speed is to be changed during operation. If the light intensity or the motor speed is to be constant, supplying the device with a constant DC voltage is sufficient. Some devices such as servos, require PWM just to operate. The repetition rate of the pulses, or PWM frequency is normally kept constant. The variation in light intensity or speed is achieved by varying the width of the pulses (changing the duty cycle). PWM controls these devices by rapidly turning then on and off.
While it is certainly possible to use the Arduino IDE's analogWrite() function, to control LED brightness and motor speed, forget about controlling a servo by this method. The servo requires pulses of a specific frequency. The function analogWrite() produces a fixed PWM frequency of 490Hz or 980Hz depending upon which arduino pin used. Servos I am familiar with requires 50Hz - a big, big difference.
Knowing how to use the ATmega's counter/timers, and how to program the various, associated, registers will give you much better control in any project.
How I Am Going To Cover The ATmega328P's Timer/Counters
Atmel equipped the ATmega328P (and the other microcontrollers in the family) with a very robust set of capabilities for it's timer/counters. There are three independent, different, timer/counters. Each timer/counter has many modes, and many options. Rather than trying to cope with all of these variants, now, I am going to present how PWM is created in very general terms. Then, I will include enough of the details to demonstrate controlling a servo. Finally, I will discuss all of those modes and options.
You should certainly download the datasheet for the device. There are four chapter associated with timer/counters. Here is a link to the datasheet. I'm going to use the language of the datasheet as much as possible.
Determining the Repetition Rate of the Pulses (PWD Frequency)
We know we are going to create a string of pulses of a particular frequency. What in the Arduino already has a repetitive characteristic? How about the 16MHz clock that drives the ATmega328P? That clock will work but may be a little fast and is invariant. The timer/counters use that clock, but we usually feed that clock into divider circuits to produce lower clocks frequencies. There are five or six outputs from these dividers, called prescallers, in the Atmega documentation. Each precaller divides the 16MHz clock by a different power of 2. The prescallers may slow the clock down sufficiently to be useful, but they only give a choice of five or six different PWM frequencies. What if we want something different? Something like 50Hz is impossible from any prescaller, alone. That is where the timer/counters come in to play.
The timer/counter takes the output of the chosen prescaller and further divides the frequency. This divider is not limited to a power of two. It can divide by any whole number from 1 to a maximum number which is determined by the number of bits in the physical counter within the ATmega328P, or by a programmed value.
The timer/counter simply counts the clock pulses it receives from the prescaller. With each clock pulse, the counter increments by one. When it reaches it maximum value, or the number programmed into an associated register, it will count down with the next clock, decrementing by one until it reaches 0. The next clock pulse starts the counter incrementing again.
If we plot the count against time, we get what is commonly called a triangle waveform. The significance of this waveform is that its frequency will correspond to the pulse repetition frequency. It is obvious that this frequency depends upon three factors: the Arduino's 16MHz clock frequency, the prescaller selected, and the count we choose for our timer/counter. Later, we will put all of that together and generate a formula.
Determining the Pulse Width (Duty Cycle) and Generating the Pulses
Now that we know how to determine the pulse frequency, let's move on and determine the pulse width and generate the pulses themselves. This is really one operation. We introduce two more elements: a comparator circuit, and the comparator register. For our present discussion, we are going to say that the comparator is part of the circuit that drives one of the Arduino's I/O pins.
The comparator compares two values: the value programmed into the comparator register, and the current value in the timer/counter. Here is were we make our pulses: When the comparator detects that the two values are equal, it changes the logic level of the I/O pin.
Let's assume that the current logic level of the I/O pin is high, and that the timer/counter is at 0. The timer/counter counts up and when it reaches the value programmed into the comparator register the comparator will flip the I/O pin low. The counter/timer continues to count up until it reaches the maximum count we have selected. The counter now counts down and when it, again, reaches the value programmed into the comparator register, the comparator flips the I/O pin High. We now continue to count down to zero, which is where we started. This cycle continues for as long as we let it. To produce pulses, the value of the comparator register must be lower than the value programmed into the timer/counter's register. If the comparator register has a value equal or higher than the timer/counter register, or has a value of 0, the I/O pin's logic level will not change. Here is a diagram of the operation:
It should be obvious that if we lower the line labeled "Pulse Width" (corresponds to the value programmed into the comparator register), the positive portion of the pulses get narrower, while the negative portion gets wider, thus lowering the duty cycle. The frequency will not change. Note that the center of the positive pulse is centered on the peak of the triangle wave. The center of the low pulse is centered upon the count of zero. This has some significance as we will see later when we discuss other modes of operation.
What's a Servo
A servo is a device used by radio control models (planes, cars, boats), in robotics, and in other applications where something has to be moved. Something, like the wheels that steer a car, the ailerons of a plane, or the fingers of a robotic hand.
There are industrial servos, usually run by AC. I'm not talking about those (I don't know anything about them). I'm only considering the dc types like in the picture. The photo shows a "standard" size servo. There are smaller ones available. Check out Adafruit and Sparkfun. Also, there are two types: continuous and non-continuous. The following discussion, and my code example, is for the non-continuous servo. I'll discuss the continuous servo in another post.
The non-continuous servo rotates through a 180° arc. It consists of a motor connected to a potentiometer (a variable resistor) via a series of gears. The potentiometer shaft also serves as the output shaft that drives your device. When the motor changes the potentiometer setting, it changes a voltage that is fed into a feedback circuit. There is a second voltage feeding the feedback circuit, a voltage derived from pulses from an external device like the PWM output of your Arduino Uno.
The motor rotates until the voltage out of the potentiometer equals the second voltage. When that that happens, the motor stops. The width of the input pulses determine the position of the output shaft. Saying it another way, the motor turns only when the pulse width changes, making the two voltages unequal. When the voltages become equal, again, due to the turning of the potentiometer, the motor stops. The gears greatly reduce the rotational speed of the motor. The torque at the output shaft is increased in the same proportion as the speed is decreased. The beauty of this device, is the motor can turn, and hold fast, against a good deal of applied torque.
Controlling a Servo
I have a non-continuous servo I experiment with. I previously discussed this in my post: Gertboard - Pulsewidth Modulation - Servo Control. The Gertboard is an attachment for the Raspberry Pi.
The servo has three connections: ground, power, and control. Ground and power connect to an external voltage source. I used 6V worth of AA batteries. Ground and control connect to the Arduino Uno. I used Uno pin 10 for the control signal.
The control signal is a string of pulses of 50Hz - a pulse every 20ms. The servo will be at -90° if the pulse width is 0.5ms, at +90° if the pulse width is 2.5ms. and at zero, if the pulse width is 1.5ms. My understanding is there is some variability in the specifications for different servos. The pulse repetition frequency of 50Hz. seems to be standard, as is the pulse width of the zero position (1.5ms.). It's the maximum and minimum pulse width, at the extreme clockwise and counterclockwise positions, that may vary with the device. Some trial and error may be required. I would start at 1.0ms. and 2.0ms. and see how far your device turns.
Timer/counter, and the Output and Input Registers
Now we get play with the ATmega328P's timer/counters within the ATmega328P, There are three of these: Timer/counter0, Timer/counter1, and Timer/counter2. I use Timer/counter1. The registers in Timer/counter1, and in the timer/counter itself, are 16 bits long. We need the 16 bit resolution - Timer/counter0 and Timer/counter2 use 8 bit timer/counters and registers.
Hopefully, you can match the following detailed discussion with the non-specific discussion at the beginning of this post. As I said before, there are a lot of options available. To wit, Timer/counter1 has 15 modes of operation, 12 of which are for pulse width modulation. Table 16-4 of the datasheet lists these modes. We will be using mode 9, "PWM, Phase and Frequency Correct". I'll explain what that means later, when discussing the other modes. Remember, from before, we need to program a register to contain the value of the highest timer/counter count to establish the pulse repetition rate. In the parlance of the datasheet, this value is called "TOP". There is a formula below that comes from the datasheet. That formula uses "max value" to describe the same thing. We also will need a register to determine the width of the pulses.
Now would be an excellent time to look at the block diagram in Fig. 16-1 of the datasheet. You can replace all of the small "n"s, like in "TCNTn" with a "1", because this is Timer/counter1. "TCNTn" becomes "TCNT1", ect.
On the left of the diagram, you see the timer/counter itself: "TCNT1". Below "TCNT1" are two output control registers, "OCR1A" and "OCR1B", and one input control register, "ICR1". The output control registers connect, to "other circuitry" discussed in the next paragraph. I'll discuss "ICR1" pin functionality later.
Let's talk about that "other circuitry". The mode we select controls the interconnections between the various blocks in the block diagram. The output control registers each connect to comparators, depicted as rectangles with equal signs inside. These two comparators also connect to the timer/counter. When the value of the timer/counter equals the value stored in an output control register, a signal is sent to perform some action. In my project, when the comparator between the timer/counter and output control register "OCR1A" reach the same value, the "equal" signal will migrate to the "Control Logic" block (connection set by the mode selection). The result will be that the timer/counter will count down at the next clock from the prescaler. "OCR1A" is programmed with the highest value we want the timer/counter to reach "(TOP", or max value), and, obviously, is used to control the pulse repetition frequency. When the timer/counter reaches zero, called "BOTTOM" in the datasheet, it will count up at the next clock.
The output of the comparator between the timer/counter and output control register "OCR1B" connects to the block called "Waveform Generation". The "Waveform Generation" block connects to the ATmega328P pin "OC1B" (via a programming selection). When the output of this comparator signals equality, "Waveform Generation" circuitry will change the logic state of the pin. Whether it changes from "Low" to "High" or "High" to "Low" depends on whether the timer/counter was counting up or down at the time. It also depends on how we program one of the control registers not shown on the diagram.
Programming the Registers
Now is the time to discuss how we determine the value of max count to program into "OCR1A". There is a formula in the datasheet:
pulse repetition freq = clock speed / (2 * prescaler * max count)
Substituting the known values:
50 = 16,000,000 / (2 * prescaler * max count)
Rearranging and simplifying:
max count = 160,000 / prescaler
We have a choice of fixed values for prescaler: 1, 8, 64, 256, and 1024. We want to choose the lowest value of the prescaler to give us the highest value of max count. The larger max count, the greater resolution we will have when programming the pulse width. We can't use the prescaler of 1 because that would give us a max count of 160,000. A 16 bit counter can only count up to 216 -1, or 65536. Choosing the next prescaler value, 8, gives us a max count of 20,000. This will do nicely. There is also the happy coincidence that 20,000 is exactly the time between pulses in microseconds. Therefore, when we go to programming the pulse width, of say, 1.5ms., it will be attained by programming a value of 1500.
Generating The Code
I have a sample sketch below. All it does is slew the servo in 10° steps from +90° to -90° and back again. It runs continuously with 500ms. between steps. Registers "OCR1A" and "OCR1B" can be written to, directly, as can be seen in the sketch. "OCR1A", once set to 20,000 will not change - because the pulse repetition frequency will not change. "OCR1B" changes every time we wish to change the pulse width.
We have four more programming steps to attend to: Selecting the timer/counter mode 9, setting the prescaler to 8, selecting how we want the output pin to change logic state, and making the output pin, "OCP1B", an output. The first three of these will be done by programming two ATmega328P control registers, "TCCR1A" and "TCCR1B"
When I wrote the code below I included lots of comments that describe the programming of the control registers. No sense of being redundant so have a look at the code. Please look at tables 16-3. 16-4, and 16-5 of the datasheet for further details of the programming of these two control registers.
There is a lot more to discuss, including:
- The other PWM modes
- Non-PWM modes
- Input control register
- Waveform generator modes
- Timer/counter0 and Timer.counter2
- The continuous servo
- Generating and using timer/counter interrupts
- Some other uses for the timer/counters
I'm going to deal with these issues in my next post or posts.
No comments:
Post a Comment