Computing stuff tied to the physical world

Permanent variables

Most microcontrollers come with some flash memory plus some RAM. Flash memory stores code and retains it contents even without power, while the RAM stores data while the code is running. RAM memory is volatile: power off means that all its contents are lost.

Not a problem, since all firmware in flash memory is set up to initialise RAM on power up.

But often you also want to store some configuration settings, permanently that is: things like the node ID and frequency band to use for RF communication, encryption keys, and all sorts of application-specific settings – calibration constants, user settings, baud rates, etc.

Some microcontrollers include EEPROM for this – which can be read and written under software control, and which retains its contents even when power is off. You can also use special-purpose EEPROM chips, connected via an I2C or SPI bus.

An EEPROM will typically retain its data for 10+ years and support over 100,000 writes.

The LPC8xx series µC’s does not include EEPROM, but it does allow altering its flash memory under program control – this is called “In-Application Programming” (IAP).

But flash memory is normally rated for “only” 10,000 re-writes and its contents can normally be changed only in pages of 64, 256, or 1024 bytes at a time. It would be quite wasteful of flash memory to allocate a full page for each permanent variable we have.

Fortunately, we don’t have to – due to some very specific properties of flash memory:

  • flash memory must be erased before use (in 64- or 1024-byte chunks on the LPC8xx)
  • once erased, we can store information in it, this changes some “1” bits to “0” bits
  • to subsequently change more “1” bits to “0” bits, we do not need to erase it again!
  • in other words: “erase” sets all bits to 1’s, and “program” sets some back to 0’s

Furthermore, it appears that writes to flash on the LPC8xx series happens in units of 32 bits. This means that we can erase, say, a 64-byte page, and then at different times store 16 different variables in that page. As long as each variable occupies a different position in flash memory, and as long as the write only changes 1’s to 0’s, all is well.

This provides sufficient capability to emulate permanent variables with these properties:

  • we need to reserve two 64-byte pages in upper flash memory, i.e. 128 bytes of flash
  • this lets us store up to 15 variables, numbered 1 to 15
  • alternately, we can reserve 2 KB of flash, and use it for 255 different variables
  • each variable is a 16-bit unsigned integer, i.e. values 0..65535

By playing some advanced tricks with the C++ “[]” operator, we can make this permanent memory look like a normal array, and use it with the following very natural syntax:

    rom[7] = 0x1234;
    if (rom[8] == 0x5678) ...
    rom[9] = rom[9] + 1;

The difference being, of course, that these values will persist across power-off cycling!

Here’s is a sample application using this EEPROM emulation code (also on GitHub):

RomVars<Flash64,0x0F80> rom;

int main () {
  // ...
  rom.init();

  unsigned i = 0;
  while (true) {
    int varNum = i++ % 5 + 1;

    if (varNum == 1) {
      uint16_t v10 = rom[10];
      printf("bump #10 to %u\n", ++v10);
      rom[10] = v10;
    }

    uint16_t oldVal = rom[varNum];
    uint16_t newVal = tick.millis;

    int start = tick.millis;
    rom[varNum] = newVal;
    int elapsed = tick.millis - start;

    printf("#%d: old %-6u new %-6u %d ms\n",
             varNum, oldVal, newVal, elapsed);

    tick.delay(500);
  }
}

Sample output, when run for the very first time:

[eeprom]
bump #10 to 0
#1: old 65535  new 3      1 ms
#2: old 65535  new 507    1 ms
#3: old 65535  new 1011   1 ms
#4: old 65535  new 1515   1 ms
#5: old 65535  new 2019   1 ms
bump #10 to 1
#1: old 3      new 2525   1 ms
#2: old 507    new 3029   1 ms
#3: old 1011   new 3533   1 ms
#4: old 1515   new 4037   1 ms
#5: old 2019   new 4541   1 ms
bump #10 to 2
#1: old 2525   new 5047   1 ms
#2: old 3029   new 5551   3 ms
#3: old 3533   new 6056   1 ms
#4: old 4037   new 6560   1 ms
#5: old 4541   new 7064   1 ms
bump #10 to 3
#1: old 5047   new 7570   1 ms
#2: old 5551   new 8074   1 ms
#3: old 6056   new 8578   1 ms
#4: old 6560   new 9082   1 ms
#5: old 7064   new 9586   3 ms
bump #10 to 4
#1: old 7570   new 10093  1 ms

As you can see, all variables started out as 65535, which is 0xFFFF in hex, the contents of “empty” flash memory. Now watch what we get when we run this code again:

[eeprom]
bump #10 to 30
#1: old 30262  new 2      1 ms
#2: old 30765  new 505    1 ms
#3: old 31269  new 1009   1 ms
#4: old 31773  new 1513   1 ms
#5: old 32277  new 2017   1 ms
bump #10 to 31
#1: old 2      new 2523   1 ms
#2: old 505    new 3026   3 ms
#3: old 1009   new 3531   1 ms
#4: old 1513   new 4035   1 ms
#5: old 2017   new 4539   1 ms
bump #10 to 32
#1: old 2523   new 5045   1 ms

Variable #10 keeps on incrementing, from what we had before – even though the µC was reloaded and may have been powered off in between. Note also that the initial values for variables #1..#5 are now whatever they were from the last run.

Writes add a little overhead, and this increases slightly when the boundary of a flash page is exceeded and requires some internal reshuffling. Flash writes briefly disable interrupts.

The 64-byte version (Flash64) is useful for the very memory-limited LPC810, whereas the 1024-byte version (Flash1k) offers more storage – and consumes 2 KB of flash memory.

The algorithm used here does not erase flash memory all the time – instead, it re-uses empty slots until there are none left, and then switches over to the other flash memory page. Meanwhile, the original page is erased, ready for reuse after more changes are stored. As a result, the flash memory will support far more than 10,000 variable changes (because it’s the erase cycles which wear out flash memory, not the number of changes to it).

Another important point to make, is that the logic is such that changes are “stable”: even if powered off during a page copy (which happens occasionally), the code will recover the last valid state on next startup. If you are still worried about data corruption, you can store a checksum (again, as a permanent variable). Either its old or its new value will be in flash.

It may seem that 15 variables is a bit limited, but there’s an easy way around that: we can allocate some more flash memory, use it for storing much more data ourselves (bypassing this emulation code), and then store the location of that chunk as permanent variable.

Next, let’s see how this can be implemented … the whole implementation takes 1 KB.

[Back to article index]