PIC24 and EEPROM
by kacang bawang
If you’re reading this, chances are you are in the same boat I was in before writing this text – I wanted to save some data to EEPROM
on a PIC24F
and … it doesn’t have one. (The manual claims that PIC24F
has EEPROM
, but that is not fully so – only select models, namely PIC24F___K_, do). What to do? Well, there is this nice lib (AN1095) from Microchip that implements software EEPROM
. The lib is well written, lots of docs, but alas – it does not run out of the box. This post is about the additional steps that it took to get it running.
Before going further, let me state the equipment that I am using.
Chip: PIC24FJ256GB206
IDE: MPLabX 2.30
Debugger: PickIt3
The problem one is faced with after first trying to use the software EEPROM
code is this – the written data does not survive a cold reset (but works as expected until then).
The cause of the problem is the programmer/debugger. It erases our EEPROM
while reprogramming the chip (which happens every time we start a new debug session). Let’s see why.
Here is the line of code that declares the array that holds the EEPROM
data (line 117 from eeprom_emu.c):
1 2 3 |
unsigned char emulationPages[DATA_EE_BANKS * NUM_DATA_EE_PAGES][NUMBER_OF_INSTRUCTIONS_IN_PAGE * 2] __attribute__ ((space(psv), aligned(NUMBEROF_INSTRUCTIONS_IN_PAGE *2), noload)); |
The compiler creates the following sections in the program memory (numbers given are specific to my project)
1 2 3 4 5 6 7 8 9 10 11 |
Program Memory [Origin = 0x200, Length = 0x2a9f8] section address length (PC units) length (bytes) (dec) ------- ------- ----------------- -------------------- .text 0x200 0x287a 0x3cb7 (15543) .text 0x2a7a 0xaa 0xff (255) .init.delay32 0x2b24 0x1c 0x2a (42) _0x40cc16d454f597a2 0x2c00 0x800 0xc00 (3072) # -- this is the eeprom array .const 0x3400 0x8bc 0xd1a (3354) .text 0x3cbc 0x38be 0x551d (21789) .dinit 0x757a 0x256 0x381 (897) |
This results in a randomly named section being created an inserted somewhere in the middle of the program data (compiler selects placement). Well, you will want to debug your program sometime, right? Just know that PickIt3
erases all pages that house the code, so if something is placed in their midst (such as, oh I don’t know, an EEPROM
section), it also gets erased. Ok, no problem, we’re smart – we know that under Project Properties -> PickIt3
we can set the range of flash addresses to be programmed. We select some settings that look like this (using some example numbers):
Program: 0x0 – 0x7fff
<—– the higher this value, the longer it takes to program
Protect: 0x2c00 – 0x33ff
Unfortunately that won't work. The protected section does not get protected if it is placed in the middle of program range. Further experiments showed that the protected range has to be at the end, like this:
Program: 0x0 – 0x7fff
Protect: 0x2c00 – 0x7fff
This means that:
1. We have to somehow place our EEPROM
array at the end of program memory.
2. We have to specify (and keep updating as the program grows/shrinks) the erasure area.
Above creates upkeep headaches, but, having EEPROM
is more important to us, so let's keep going. First we need to tell the compiler to reserve our array memory a little differently:
1 2 3 |
unsigned char emulationPages[DATA_EE_BANKS * NUM_DATA_EE_PAGES][NUMBER_OF_INSTRUCTIONS_IN_PAGE * 2] __attribute__ ((space(psv), section(".eeprom"), address(0x8000), aligned(NUMBEROF_INSTRUCTIONS_IN_PAGE *2), noload)); |
The additional options give our data section (it ends up in its own section) a readable name, and we manually specify the program memory offset (0x8000
in our case) where the allocation will begin. I suppose the aligned
attribute is not necessary anymore, but I left it just in case I make a typo in the address.
The new section table looks like this:
1 2 3 4 5 6 7 8 9 10 11 |
Program Memory [Origin = 0x200, Length = 0x2a9f8] section address length (PC units) length (bytes) (dec) ------- ------- ----------------- -------------------- .text 0x200 0x287a 0x3cb7 (15543) .const 0x2a7a 0x8bc 0xd1a (3354) .text 0x3336 0x38be 0x551d (21789) .dinit 0x6bf4 0x256 0x381 (897) .text 0x6e4a 0xaa 0xff (255) .init.delay32 0x6ef4 0x1c 0x2a (42) .eeprom 0x8000 0x800 0xc00 (3072) # -- check out the cool section name ;) |
With this setup your data should survive between debug sessions at the expense of having to micromanage its location.
Thanks for all the info!! I figured out after trying all this that it’s actually easier to just set the flag that you want to use aux flash:
#define __AUXFLASH 1
And then change the address where you want to save the “EEPROM” to:
#define DEE_PAGE_TBL(bank, page) ((0x040000 + (DEE_BANK_SIZE * (bank)) + (DEE_PAGE_SIZE * (page))) >> 16)
#define DEE_PAGE_OFFSET(bank, page) ((0x040000 + (DEE_BANK_SIZE * (bank)) + (DEE_PAGE_SIZE * (page))) & 0xFFFF)
Or you could make a new #define and create another #elif _ASSIGN_DEEE_LOCATION and set the location as above.
Great post! I struggled with this issue myself when first attempting to use DE Emulation. I came to the same solution that you did, protecting the memory region with MPLAB settings and then telling the linker to position the emulationpage at a specific address within that range.
I’ve now come up against an even more difficult issue. I included the EZBL bootloader into my project and this isn’t playing well with the DE emulation library while running in dual partition mode. I can still read/write to EEPROM correctly while partition 1 (starts at 000000h) is active. But if I switch to partition 2 (starts at 400000h) the write/read to EEPROM fail.
I don’t suppose you’ve come up against this challenge as well?
Wah, it’s been a little bit since I worked on this. Not sure I can be of any help right now… sorry.