How to read rc servo pulses with an AVR Atmega32 and final board construction 04/13/2009Posted by aliasmrjones in The Build.
In the last post we took a run around the race course and found out we will need obstacle avoidance. I forgot one step – putting the final board together. In this installment I will show how I read rc server pulses from the receiver allowing me to use the rc transmitter as a failsafe kill switch and also show putting the final control board together.
First up, some pics of the final board construction.
I’m using a board from Sparkfun that includes powersupply, db9 serial port and rs232 conversion, in circuit programming port, 8 mhz crystal and plenty of room to solder our connections. I decided to connect everything with ribbon cables so I can re-use things on other projects if I need to. Since I originally thought I wouldn’t be using anything requiring analog to digital conversion, I needed an entire 8 pin port for the LCD and I needed pins from the other ports for things like i2c, serial and input pin interrup, I used port A, which is also the a2d pins. This turned out to be a headache later on.
The pic above shows the board after I soldered wires to connect the LCD. I tested this first because it can help with debugging other problems. Next I soldered connections for serial and power to go to the gps, i2c and the servos. I included one more 3 pin servo connection wired to one of the interrupt pins, which I’ll talk about later. I also used this connection to provide power for the servos, which comes from the rc battery, separate from the board power.
After soldering and testing all the connections I bought some thin, but stiff plywood and bolts/screws to use to attach everything to the car. I cut out a rectangle of plywood big enough to hold the boards and parts, but small enough to allow removing the rc car battery. There is a plastic cover on the steering servo in the front of the car and I decided to use this for mounting. I drilled holes through the plasic cover and then used small bolts to attach the plywood to the cover.
Then I used screws to attach the boards to the plywood. The compass was a little tricky since it didn’t have any mounting holes and it needs to be in a consistent orientation. I decided to superglue the compass to the plywood away from the main board and midway between the motor controller and the steering servo to try to minimize magnetic interference.
With the very real possibility of ending up in a pond, I decided I needed a way to kill the throttle if the car headed for water. I thought I could use the rc receiver to read the pulses from the transmitter and only run if the car detected pulses. I connected once channel of the rc receiver to one of the INT pins on the Atmega32. The receiver channels are wired just like servos and provide the exact same pulse timing that you would send to a servo. The 3 wires are ground, power and signal. I connected the signal wire to the INT1 pin on the Atmega32 and set it up to trigger an interrupt on any change (high to low or low to high) on the pin. Here is the code to do this:
//Set up external interrupt for reading receiver
MCUCR = 1<<ISC10;
GIMSK = 1<<INT1;
I previously defined EXTINTDIR as DDRD and EXINT as PD3. The first two lines set up the pin as input and enable the internal pull-up resistor. The next line sets INT1 as trigger interrupt on any change. There are a number of different conditions you can have trigger the interrupt including low to high, high to low, low and any change. See the Atmega32 datasheet for which bits to set in MCUCR for each type. The last line sets the bit in the global interrupt mask for INT1, which enables the interrupt pin.
The code to service the interrupt is very simple since all I need is a counter that increments once every time there is a change on the pin indicating a servo pulse on or off. I set up a global int variable called cntValue and start it at 0. I decided to set it back to 0 after 20,000 interrupts. We only care about change in the value, not the specific value itself so overflows don’t matter.
//External Interrupt Handler
ISR(INT1_vect) /* signal handler for external interrupt int0 */
//We just increment this variable whenever there is a state
//We store the previous value each cycle of our main navigation
//Then check current value against the previous value to see if
//We have rc transmitter signal. If not, safety kill.
if(cntValue > 20000)
cntValue = 0;
In the main navigation loop, we compare the current value to the value stored during the last time through the loop and if it is the same we kill the throttle. Basically, when the transmitter is on, no matter what state the controls are in, the car goes, when the transmitter is off or if it goes beyond signal range, the car stops.
OK, so now the main board is wired up and attached to the car and the kill switch will hopefully keep us out of the pond should things go really bad. Now, we need some way to avoid obstacles. The next post will be about using sonar to do that.