Using PROGMEM and EEMEM with AVRs

This post will give you a short example how to read and write EEPROM, and how to use flash memory as storage.

When you are jumping into programming microcontrollers many things are new and uncommon. At least to me as I used to develop in Java. This snippet may save you some time when you start playing with EEPROM and flash storage. Please note, that this is not a complete tutorial on this topic. You may find usefull links in the link section below.

Motivation

Why you want to use EEPROM storage should be clear. You will have permanent storage, even if the controller gets resetted. EEPROM can be read and written by your program at runtime or it can be written by programmer software, together with your program.

As you may know, or encounter in the future, RAM is an extremely tight resource. If you are dealing with strings, most of them are constant. They will be used as labels or messages with no need to change them. But the compiler will copy them from SRAM, thats where you program is stored, into RAM, thats where you variables go. The reason for that is that all library methods are dealing with strings, expect them to be pointers in RAM. But there is a way to overcome this waste of our exclusive RAM.

Example

Here is a short program for the ATmega8. It should be usable for a most of the AVR family. It stores values in EEPROM and messages in flash. On startup, it increments a counter to count the reboots. This counter is saved in EEPROM. Values and messages are printed out with the UART. Then the values are modified and printed out again.

/*
 * testing eeprom and flash storage
 */
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include "uart.h"

// eeprom storage
uint16_t reboot_counter_ee EEMEM = 0;
uint8_t config_ee[15] EEMEM = "testing ...";

// string in flash
const char firm_version_P[] PROGMEM = "test v0.01 2007/06/16rn";
const char reboots_fmt_P[] PROGMEM = "reboots: %drn";

void inc_reboot(void) {
  uint16_t reboots = eeprom_read_word(&reboot_counter_ee);
  reboots++;
  eeprom_write_word(&reboot_counter_ee, reboots);
}

void show(void) {
  char buffer[50];
  uint16_t reboots;

  uart_puts_P(firm_version_P);

  reboots = eeprom_read_word(&reboot_counter_ee);
  sprintf_P(buffer, reboots_fmt_P, reboots);
  uart_puts(buffer);

  eeprom_read_block(buffer, config_ee, 15);
  uart_puts(buffer);
  uart_puts("rn");
}

void modify(void) {
  char tmp[] = "123�";
  eeprom_write_block(tmp, config_ee, 4);
}

int main(void) {
  inc_reboot();
  init_uart();
  sei();

  show();
  modify();
  show();

  while (1) {}

  return 0;
}

Notes

  • If you want to write values to EEPROM with your programmer, be sure to generate an eeprom file. This can be achieved with:
      avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 -O ihex <yourfile>.exe <yourfile>.eep
  • If you generated your eep file, be sure to upload it. I did it with avrdude.
      c:winavrbinavrdude -v -F -p ATmega8 -c avr910 -P com4 -U eeprom:w:<yourfile>.eep:i
  • Be sure to write the eep after you have written your program, as avrdude automatically does an erase cylce which clears the eeprom as well. Otherwise you will see unprogrammed eeprom storage. You can try to suppress the erase with option -D but that does not work for me (errors in verification). If you want to preserve your eeprom while programming, you have to protect it by setting the “preserve EEPROM” fuse. That should be used if development and testing is over.

Links

  • A great tutorial on PROGMEM over at AVRFreaks. Be sure to read the complete thread as you might miss corrections otherwise.
  • Another great tutorial on EEPROM written also by Dean.
  • Also very helpful was this post on EEPROM at Scienceprog.

1 Comment

Comments are closed.