All about AVR Atmega32 EEPROM 03/07/2009Posted by aliasmrjones in The Build.
The last post we figured out how to calculate distance and bearing to a waypoint, the fundamentals of our navigation routine. Now that we can determine where to go, we have to create a way to store and retreive the waypoints we want to follow. That is the topic for today.
Accessing the EEPROM in an AVR microcontroller using the gcc compiler is actually pretty easy. The two functions we will use are eeprom_write_block(const void* RAM, void* EEPROM, int length) and eeprom_read_block(void* RAM, const void* EEPROM, int length). Each takes 2 pointers, one to a spot in RAM and one to a spot in EEPROM and a length indicating how many bytes to move and it copies the data from one to the other.
What makes using these functions easier is the ability to define variables that exist in EEPROM. Using EEMEM in a variable declaration tells the compiler that the variable is in EEPROM instead of RAM. So, for example, you can define a single byte that represents how many waypoints we have like this:
char EEMEM eeWayPts;
You can then refer to this spot in EEPROM like referring to any other variable: &eeWayPts. As an example, here is a little piece of code that will declare the above in EEPROM, create a standard RAM variable, assign a variable, store the variable value in EEPROM and then read the value from EEPROM into another RAM variable.
char EEMEM eeWayPts;
x = 42;
eeprom_write_block((const void*)&x, (void*)&eeWayPts, 1);
eeprom_read_block((void*)&y, (const void*)&eeWayPts, 1);
That’s pretty much all there is to it. EEPROM will retain values even when power is turned off. We can store all the waypoints and other data in EEPROM, turn off the car and then a week later when we turn it on all the data will still be there.
So, first things first. When the AVR is programmed all memory, including EEPROM, is erased. This means right after programming, when we read the waypoint values into memory, it will be garbage values. That’s not such a good thing. If we forget to reprogram the waypoints, the car could go in any direction. I decided to put a specific value in the first few bytes of EEPROM and have the program check the location for a match. If a match is found, we know we’re good to go. If the values in that location don’t match, we know that the device has just been wiped and we need to initialize our EEPROM data.
In order to keep the main program clean and easier to read, I created simple functions to encapsulate all the EEPROM reading and writing . I also set up a 13 character string variable at the beginning of EEPROM. When we initialize the EEPROM, we’ll write “deathpod3000” into this area (12 characters + terminating 0). When the program starts, we’ll copy this EEPROM area to a RAM variable and then compare to the string “deathpod3000” using strcmp(). If they aren’t equal, we’ll initialize the EEPROM with some sane values, then continue with normal processing.
We are going to use an array to contain the list of waypoints. Each waypoint is 2 floats: lat and long. In order to make it easy to change the maximum number of waypoints, I declare a constant:
#define WAYPOINTS 20
We will use this constant throughout the program to declare the EEPROM array, the RAM array, in loops, etc. This way, if we need to change the maximum number of waypoints, we can change it in this one place and it will change all throughout the program.
Now, to read or write our array of waypoints from/to EEPROM, we need to know how many bytes to read or write. We need to know how many bytes in a float, multiply by 2 (lat/long), then multiply by…Hmmm, maybe there’s an easier and more consistent way to this. Enter sizeof().
As the name implies, sizeof() will tell us the size of any variable. We can use this in our functions for reading and writing to EEPROM in place of a literal number of bytes and then if we change the size of our waypoint array, we don’t have to worry about forgetting to change the values in the EEPROM read/write routines. Here are the little functions to read and write the waypoint array from/to EEPROM:
void ReadEEWaypointBlock(float *block)
eeprom_read_block((void*)block, (const void*)eeWaypoint, sizeof(eeWaypoint));
void WriteEEWaypointBlock(float *block)
eeprom_write_block((const void*)block, (void*)eeWaypoint, sizeof(eeWaypoint));
Now, it will always read and write the number of bytes in the EEPROM array no matter how we change the size of the array. Also, since we used the constant WAYPOINTS in both the declaration of the size of the EEPROM and RAM arrays, they will always stay in sync. So, changing the value of the WAYPOINTS define changes everything at once…the size of the arrays, the number of bytes copied, etc. No muss, no fuss.
So far, so good. We can store waypoints in EEPROM and when the program starts up, it reads the waypoints from EEPROM. We have almost all the pieces in place.
The only major thing missing now is controlling the servos. Both steering and throttle are controlled by standard servo pulses. The PWM generator on the Atmega32 will do most of the work for us. We also need to build a simple UI to allow choosing between “programming” mode, where we’ll set the waypoints, and “gogogo” mode where the car will follow the waypoints. We have the LCD for display and a single pushbutton for user input. That should be enough.
In our next installment, we’ll build some routines to control the steering servo and build the UI. At that point, we should be ready for her maiden voyage.