Controlling servos with the Atmel AVR Atmega32 03/13/2009Posted by aliasmrjones in The Build.
In the last post we built some functions to store and retrieve waypoints and settings to/from EEPROM. Now our waypoints and settings will survive power being turned off. The next and virtually last thing we need is to be able to control servos. Both steering and the throttle of our r/c car are controlled by standard servo pulses. Getting our Atmega32 to generate these pulses is the topic for this post.
Servos are controlled by pulses of electricity. The length of the pulse determines how much the servo turns. Generating pulses like this is called Pulse Width Modulation or PWM. For standard servos, the pulses should be between 1 and 2 milliseconds long. 1.5 ms is center, 1 ms is all the way counter-clockwise and 2 ms is all the way clockwise. (Some servos can turn more than this, but for safety these are good starting values. In the case of the steering servo in our car I checked and these are the correct values.) To keep a servo holding position, you have to send a pulse every 20 ms, or about 50 times per second.
To make this happen you simply have to set an output pin high, wait the appropriate amount of time and then set the pin low. After 20 ms, you have to do it again. The problem with this is that we have to keep actively servicing the servo pins. We’d prefer if our microcontroller were spending time reading gps data, calculating navigation, etc. and not servicing the servos. Luckily the Atmega32 has a timer with a built-in PWM generator. If we set it up correctly, it will happily generate all of our pulses with exactly the right timing in the background while the cpu spends cycles on other more important things.
The Atmega32 has the ability to output 2 different PWM cycles to 2 different pins. Our r/c car has 2 channels: one for steering and one for throttle, so the Atmega32 has enough to control everything. In order to keep things under control initially, we’re going to control the throttle with the radio control and just let deathpod3000 control the steering. That way, if it starts to steer in the wrong direction, we can stop the car and try to figure out what’s going on.
We’re going to use the “fast PWM” mode of the Atmega32’s Timer1. Timer1 is a 16 bit timer, which will provide the resolution we need. In “fast PWM” mode, the output pin starts each cycle high (1/True/+5v). It counts up to whatever value is in the Output Compare Register (OCR1A for our steering servo) and then it sets the pin low (0/False/0v). It continues counting up to the Overflow Value and then it sets the counter back to 0 and starts the cycle over again. We can change the width of the pulse by changing the value in OCR1A and the PWM generator will keep generating pulses of that width in the background until we change the value.
The first thing we have to do is figure out the length of a cycle. A servo likes to get pulses every 20 ms. The clock speed of our Atmega32 is 8 mhz. The internal clock of the chip “ticks” 8 million times per second. A millisecond is 1/1,000 of a second. That means the clock of our chip will “tick” 8,000 times per millisecond. We want a period of 20 ms, so that would be 8,000 * 20 or 160,000 “ticks” for each PWM cyle. Timer1, which drives the PWM generator is a 16 bit timer. 160,000 is too big to fit into 16 bits so we’re going to have to divide down the system clock into something longer.
Each timer in the Atmega32 has a prescaler. This prescaler can divide down the cock rate to allow us to produce longer cycles without overflowing the 16 bit counter. The prescaler can be set to 1, 8, 16, 32, 64. In our case, a value of 8 works perfectly. Setting the prescaler to 8 means that the timer counter will “tick” once for every 8 ticks of the system clock. The system clock is running at 8 mhz so our timer clock will run at 8 mhz / 8 = 1 mhz. Now our timer is ticking only 1,000 times per ms. To generate a 20 ms cycle we can set the overflow value for timer1 to 20,000, a value that will fit in 16 bits. The timer will “tick” at 1,000 times per ms until it counts up to 20,000 and then start over again at 0. So, we now have a way to generate the 20 ms cycles. On to the pulese.
With our prescaler set so the timer “ticks” 1,000 times per ms, it is easy to figure out what value to set OCR1A to in order to produce our pulses. We want to produce pulses from 1 ms to 2 ms. At 1,000 “ticks” per ms, setting OCR1A to 1,000 will produce a pulse of 1 ms, or full counter-clockwise. Setting it to 2,000 will produce 2 ms pulses, or full clockwise and setting it to 1,500 will produce pulses of 1.5 ms, or dead center. Values between these numbers will produce proportional rotation.
So, let’s say we set OCR1A to 1,500, or dead center. The timer will start at 0 and set the OCR1A pin high (+5v). The timer will count up to 1,500 (1.5 ms) and then set the pin low (0v), ending the pulse. The timer will continue to count up to the overflow value of 20,000 (20 ms). The counter will then be reset to 0, it will set the pin high and start counting up again. It will do all this in the background without us having to do anything more. By changing the value in OCR1A to a value between 1,000 and 2,000, the pulse width will change and the steering servo will turn and then hold at that position until we change the value in OCR1A again.
AVRLib includes code to set things for us. We just have to supply the correct values. Here is the code that does the magic:
//First initialize the timers
//Set OC1A/B pins to output
sbi(DDRD, PD4); //OC1B
sbi(DDRD, PD5); //OC1A
//Set top count value to 20000 = 20ms = 50 hz
//Set OCR1A to dead center
OCR1A = 1500;
//Turn on PWM
I created a simple function to take a relative bearing of +/- 180 degrees, scale it and center it on 1500 to convert the relative bearing to microseconds of pulse for the server. Now by calling the function and passing relative bearing to the next waypoint, the front wheels will turn toward the waypoint.
Connecting the servo is pretty easy. A standard servo has 3 pins: signal, +5v and ground . The wires are typically color coded with black being ground, +5v red and signal something else.
I unplugged the steering servo from the receiver on the car and connected power and gnd to appropriate pins on the stk500 board and signal to the OCR1A pin, which on an Atmega32 is PD5. Running a little test program that moved the servo back and forth showed it was working correctly. Running one little servo through the stk500 is OK. Running more than one or running a bigger servo would probably draw too much power.
OK, so now we need a way to make the whole thing come together. We have the LCD for display and just one button for input. Here is the simple UI I devised:
- At startup, read the secret EEPROM spot and let the user know if everything is A-OK.
- If there are waypoints stored in EEPROM, display them 1 at a time for 2 seconds each
- Display a message that says to press the button if you want to gogogo and count down 3, 2, 1
- If they press the button, start navigating
- If they don’t press the button, go into programming mode…continuously display lat/long
- When they press the button, store waypoint X and X=X+1
That’s pretty much it. I think we’re ready for her maiden voyage. Oh, yes, I almost forgot car pics…
The stk500 board actually fits ok on the car base and I can put an extra 7.2v r/c battery on top along with the handheld gps receiver temporarily to test everything. Next time you can watch video of the first drive. I’m sure it will be good for a laugh.