Microcontrollers don't do anyone very much good unless they are running some code. This page describes each section of the code, and has source available for download.
[ Navigator ][ Pilot ][ Servos ][ LCD Display ][ i2c EEPROM ][ GPS ][ Compass ][ Host PC ]


Navigator
   The "navigator" was responsible for keeping track of when and where the vehicle was going. To do this, it needed to be able to talk to the Host PC, external memory (EEPROM), and the navigator. Additional tasks included using the LCD display, gathering temperature readings as well as operating the boom equipment.
nav_main.asm The "final" source code for the navigator.
  When started, a brief initialization routine is executed. The DTR signal on the serial port is then checked to see whether or not the vehicle is connected to the PC. If it is connected then it enters "docked mode", which essentially is a RS-232 to i2c converter that allows the PC to read and write the EEPROM.
  If the vehicle is not "docked" then it is "mobile". The navigator enters a simple loop that fetches a record out of the EEPROM. If it is an unrecorded waypoint, then it sends the waypoint to the pilot and waits for a reply. When the pilot reaches the waypoint, it sends the current location back to the navigator. The navigator lowers the temperature probe to the desired depth, and simulates taking a reading. The location and reading are then stored over the waypoint in the EEPROM, and the next record is fetched. When no more locations are left, the navigator is done and enters a infinite loop.
    There are bugs in the code. (the text on the LCD is not always correct), and very minimal error checking (none on the 4-way handshake to the pilot). While some routines will work "out of the box", others will need some refinement.


prot_test.asm A simple test routine for the 4-way handshake to the pilot. There are likely bus contention issues if used un-altered.




Pilot
   The processor that handled the "pilot" functions was by far the most complicated code in the project. One major hurdle that had to be conquered was successfully linking assembly and C code. Many of the lower level functions, such as the LCD display and GPS interrupt routine were originally written in assembly, while the algoritms that determined distance and heading to the next waypoint were written in C. Getting the two languages to successfully run together and share variables with each other was "interesting".
plt_main.c The high level functions for the pilot.
    Upon start up and initialization, the pilot starts reading GPS messages into a buffer and waits for a waypoint from the navigator. When it receives the waypoint, it starts the motors and heads on its way. A control loop gathers the current location and bearing from the GPS and compass, and then decides how far away it is from the next waypoint. If it is farther than a certain threshold (30') it roughly steers towards the waypoint and starts the loop again by getting a new location from the GPS buffer.
    Because floating point math and trigonometry is so expensive on a small 8-bit processor, the algorithms are only give rough results, and rely on feedback to average the results. Examples of this are computing the distance to the waypoint as the distance of the longest side (latitude or longitude), without considering diagonals. Headings are rotated into the 0-90 degree quadrant, and then rounded to one of three bearings within this quadrant before being rotated back into their original quadrant.
    In the final hours of desperation, the GPS interrupt routine / parser was also moved into the C file to make debugging (supposedly) easier. Support for the compass was removed, so the vehicle needs to be moving before it can determine its heading.

plt_asm.a51 These are the low level routines. This is where a message is pushed to the LCD screen, waypoints are sent and received, and commands are sent to the servos. All the calls are from C to assembly or from assembly to assembly. Because the C compiler seemed to waste a lot of space with pointers, shared values were implemented as global variables.


plt_init_asm.a51 The initial assembly code for the pilot. Includes a GPS ISR and parser, and also decides whether the GPS or compass would give a more accurate reading.




Servo Controller
   Because the pilot was going to be doing quite a few tasks, it was decided that instead of adding the burden of time critical servo control, this responsibility would be off-loaded onto a smaller, less capable microcontroller. In retrospect, the 89C55 probably did have the resources to keep up with the servos, and could have saved us many headaches debugging the communications between the pilot and servo controller.
servo_desc.doc The specification for the Servo controller.
servo.asm The Motorola source code. After waiting for the pilot to start, this controller receives commands for one of eight servo channels. The timing pulses (1-2ms every 30ms) are then continually generated and sent to the appropriate servo.

servo_test.a51 A program for the 89C55 host that simply tells the servo controller to cycle 4 servos between full clockwise and full counterclockwise.

servo_bug.html Insight on how one of the simplest routines became one of the most complicated.





LCD Displays
   The LCD Display driver was written in order to simplify the interface for the vehicles systems, and also to simplify development of the rest of the component parts.
plt_lcd.a51 A test program for the pilot (89C55) and an LCD character display.
lcd_test.asm A simple test routine for the Navigator's LCD Display. Only supports one line, and may have delay issues.




I2C EEPROM
   With only 256 bytes of internal EEPROM on the AT90S2333 AVR microcontroller, it was obvious that we were going to need to incorporate an external EEPROM to store our waypoints and data. With not much knowledge of EEPROMs, I headed to the local electronic components outlet (Active Electronics, Bellevue WA). There I found a 64K EEPROM by Microchip.

   After a bit of hunting on Microchip's website, I managed to find the datasheet for my particular EEPROM, a 24C65. Unfortunately for me, it's an older chip and the datasheet disappeared from Microchip's website within days of my finding it. Fortunately for you, I saved a copy, and have it available on the resources page.

   Without a built in I2C interface on the AVR chip, it is necessary to laboriously bit bang the protocol using general I/O pins.

    If we needed to support the full I2C protocol, it would be necessary for us to share the bus with other devices, including other processors. In this application however, there were only two devices: the microcontroller and the EEPROM. The fact that the processor would always be the "master" and the EEPROM would always be the "slave" made things much easier.

   Because we are the master, and dictate how the transfer is actually executed, we don't need to tie up any timers or interrupts. Instead, we can simply use to pins as clock and data lines and use some simple routines to write and read the EEPROM.

   Someone has obviously struggled with this before, as there was already a solution posted on Atmel's website. There were a couple differences between the EEPROM for which the code was initially written and our EEPROM, but the amount of editing required was minimal.

A few minutes of coding, and a few more minutes of wiring led to what I thought would be a simple solution. Hmmm... There's got to be some natural law about that scenario. It took me quite a while to figure out why I couldn't see any signals when I probed the circuit with an oscilloscope. After disconnecting the EEPROM, I was able to see little flutters where distinct signals should have been. Then the previous day's lecture on I2C came back to me: pull-up resistors! Devices on the I2C bus prevent level conflicts on the bus by not pulling it high, but only pulling it low. To get the bus to a logic high, it is necessary to use pull up resistors. Once these were in place, the circuit worked great.



Global Positioning System
   The purpose of this interface program is to read the NMEA data sentences that are sent from the handheld GPS unit. Several different sentences are sent, however only the GPRMC sentence is used for our navigational purposes.
   The particular GPS receiver used was a Garmin GPS-12XL. It incorporates a standard NMEA 0183 V2.0 serial interface at 4800 baud, 8-N-1. All data sent are printable ASCII characters.
   Approximately once each second, the GPS unit sends several NMEA sentences, each one identified by a starting character (a dollar sign: "$") and a 5 character sentence identifier (GPRMC for example).

garmin_investigate.txt A summary of the NMEA sentences sent by the Garmin GPS 12XL.
gps.a51 A test routine that accepts and parses the interesting parts of the $GPRMC string into small ASCII strings and displays them on the LCD display.



Compass
   The digital compass is a simple device that has an output pin for each cardinal direction (N,W,S,E). It asserts a high on one or two pins depending on it's orientation. For intermediate directions (NW, SE, etc), the pins for both cardinal directions are asserted.
compass.a51 This simple program reads the compass and places the measured bearing into a memory location. May not work independently of the pilot code.





Host PC
   All of the work to put this together would have been useless if there was no way to tell it where to go or to get results back. The code below allows a user to store a list of waypoints into the EEPROM, and then read back measurements and waypoints that were not reached.

complex.zip This program allows a user to enter and read a list of way points. Poor interface, no help, and very few trapped errors.

simple.zip A very simple interface to the EEPROM. Reads and writes ASCII strings.


December 2000 - Matt Cosand, Kevin Nichols