EEPROM: assignment vs write() vs put()

econjack

Well-known member
I'm using Teensy 4.1 on Win 11 with IDE 2.0.3. I have a structure that I want to store in EEPROM named EEPROMData. This may be a stupid question, but what does this statement do?

EEPROMData.currentWPM = currentWPM;

I want to store the current words/minute (Morse code) in EEPROM so it is persisted across power cycles. It appears that the assignment "works" while the app is running, but power cycle and the change is lost. If I use EEPROM.put(), the data is persisted across power cycles. Using EEPROM.write() also works, but with the disadvantage that I need to know the offset. Also, if I use write() instead of put(), does the write() count as a EEPROM life cycle for all of the emulated EEPROM, or just the count for the block dedicated to EEPROMData? I think I remember seeing that the wear leveling algorithm works on 4K blocks. Is that correct?

Thanks!
 
Wear leveling is handled 'transparently' for all methods of write or put.

Not sure what this statement might be doing: EEPROMData.currentWPM = currentWPM;

Using .put will put a complete structure of the size indicated.

Indeed, an OFFSET must be selected and used consistently. Any following writes must account for the length of that information before additional writing to account for the bytes used in with the next OFFSET.
 
EEPROM.put() still requires the address within the EEPROM memory. It only adds convenience for writing all the bytes of any variable or structure which the compiler knows the size.

Regarding the life expectancy of EEPROM, use of write() and put() is the same.

How the wear leveling works is complicated. It's also not explicitly specified. Future software versions could change the underlying details, though as a practical matter such a change would be extremely unlikely after 3 years of widespread usage without any known media management bugs (at least on Teensy 4.x... long ago we had such a bug on Teensy LC which was very ellusive and it took years to track it down). Of course you can discover those low-level details by reading and analyzing the core library source code. But officially, there is no specification about exactly how the wear leveling is accomplished.

But to specifically answer your question, yes, internally the EEPROM emulation code does indeed write to 4K sectors.
 
defragster, Paul:

That's kinda what I am seeing so this just confirms it. Clearly, put() is easier to use and has the same effect.

Thanks to you both.
 
defragster: The project I'm working on is Software Defined Transceiver for amateur radio.
Figure00-001Small.jpg
There are about 40 parameters that I track (calibration and IQ settings, power output, CW-SSB transmission, filter settings, etc.) that use the emulated EEPROM to preserve the operating environment between power cycles. Because some of these parameters may change while operating, I have "working variables" that copy the EEPROM member variables. While you may be comfortable sending Morse at 30 words per minute, the person your talking to can only do 15wpm. The user can slow down his speed to match the other person. If the user wants to persist the new value, you get the statement like I mentioned earlier. You can then use a put() to persist the new value. What really surprised me is that the radio does a lot of DSP, but the T4.1 is up to the task!
 
@econjack - that's very cool! Well done!
Perhaps you could add a USB keyboard and type Morse and have the Teensy decode and display :)
Project by Jack and Albert I see.

Hopefully the EEPROM management in sketch is working. Interface is just an 'array of bytes' to account for, so the OFFSET has to be accounted for proper results. The wear leveling under the covers by PJRC seems to make good use of the expected ~100K rewritable cycles, so controlled updates of any reasonable sort should be reliable.
 
@defragster: Yep, have things working correctly now. The keyboard code is already written, just waiting for some parts to come in. Great minds, you know! If you're interested, go to Amazon and search B09WYP1ST8, click on the Look Inside banner and go to the TOC. It will give you some idea of what the Teensy 4.1 is capable of.
 
@defragster: Yep, have things working correctly now. The keyboard code is already written, just waiting for some parts to come in. Great minds, you know! If you're interested, go to Amazon and search B09WYP1ST8, click on the Look Inside banner and go to the TOC. It will give you some idea of what the Teensy 4.1 is capable of.

TLDR - Ha - the 9 page TOC is quite extensive! Lots of greek TLA's to me, though newly interested. Didn't see the book before - though seems I hit the page for the T41-EP SDR once some time back.
Not looking like the kit is ready to go yet from the pages? 2/17/22 last update ... group.io seems active in recent days - though evolving with mysteries ... 9 blinks and 10+ second loss of function, getting good power.
 
I have two related questions about storing a data structure in emulated EEPROM. EEPROM.put() can store a complete array or structure, but for large array and structure sizes, the wear-leveling mechanism can take a significant amount of time. So, it would be nice to have an easy way to update EEPROM in smaller chunks. Let's say my application has an array and a struct defined as:
Code:
float savedMeasurement[16];

struct settings_t {
[INDENT]char[10] softwareVersion;
long     lastFrequency;
float    powerOutLevel;
int      currentWPM;
// ... many more settings[/INDENT]
} EEPROMData;

To write just one array location to EEPROM, I have seen examples using indexes and pointers. However, can the pointer arithmetic be eliminated? Would something similar to this statement work?
Code:
EEPROM.put(&savedMeasurement[i],(SAVED_MEAS_BASE + i * sizeof(float));

Extending this idea, is there any straightforward way to write one member variable of a struct, say, EEPROMData.currentWPM, to EEPROM without manually constructing a table of offsets and data sizes of structure members?
 
Yes and no.

Yes, of course incremental writing is possible.

And no, a highly convenient method that requires no effort to manage the details doesn't exist.
 
@nwplouff: I worry about incremental writes for fear of messing the offsets up. Given the wear level being done, I'm going to stick with the put() method. If the EEPROM ever gets flaky, it's years down the road and they will just have to buy a new T4.1.
 
Indeed, indexing through a structure can work as long as structure padding and offsets and indexing are kept in line - any errors will bring data loss into play.

Also allowing the low level wear leveling logic to run its course may mean one longer write - but will minimize Flash rewriting and extend media life.

Also possible that some number of smaller writes (when all is written) could result in taking longer with repeat interrupt disable and Flash writes.

Saw notes on Arduino that perhaps the writes to unchanged data won't waste Flash updates? Not looked at PJRC low level code enough to confirm it does such byte compares in process and only writes fresh when there are changes involved.
 
Saw notes on Arduino that perhaps the writes to unchanged data won't waste Flash updates? Not looked at PJRC low level code enough to confirm it does such byte compares in process and only writes fresh when there are changes involved.

@defragster, the low-level byte write routine (in eeprom.c in the Teensy 4.1 board package) does indeed only write to EEPROM when the data is changed. Here's the key piece of code from eeprom_write_byte():

Code:
	if (data == olddata) return;
	if (sector_index[sector] < 2048) {
		//printf("ee_wr, writing\n");
		uint16_t newdata = offset | (data << 8);
		eepromemu_flash_write(end, &newdata, 2);
		sector_index[sector] = sector_index[sector] + 1;
	}

( If sector_index[sector] is 2048, there are no free "slots" left in that flash sector, so the function gathers the valid data in the sector, erases the sector, and writes the valid EEPROM data back to flash.)

So, from the first line in the code snippet, if the value found in EEPROM matches the new value, no write occurs.
 
@defragster, the low-level byte write routine (in eeprom.c in the Teensy 4.1 board package) does indeed only write to EEPROM when the data is changed. Here's the key piece of code from eeprom_write_byte():
...

Thanks, was being lazy. - looked a bit with provided link and if reading was correct - may have learned something?:
Interesting that is a BYTE write - all 2,4,more Byte writes call that one byte at a time to find write and compare the Current and New data before writing - but all writes are committed one byte at a time.

This won't be lossy and until the sector is filled with updated offset&data values it won't cause a move to new sector and erase operation.

Also shows that half the data in the storage sector is overhead bytes to facilitate the wear level process. So other thread expected lifetime calculation would be off by factor of 2?

Also points out that multiple small writes wouldn't incur any extra wear given the net effect would be only writing changed values, and only erasing a sector when it can't hold the next edit set of offset&data pairs with formatted space in the sector.
 
@defragster, the numbers in the other recent EEPROM thread are based on using 4 Kbyte flash sectors as 2K 16-bit words. Also, just to be clear, when all 2K "slots" in a flash sector are full, the low-level write routine copies the valid data in the sector to a temporary RAM buffer, erases the sector, and rewrites all the valid data into the first several slots of the same flash sector. That is, each EE address always maps to the same flash sector, but not to any specific address within the 2 Kword sector.

So far I haven't found any clear writeups of the details of the address-spreading and wear-leveling mechanisms after looking through both PJRC and Arduino documentation, so I had to read the code and do a lot of doodling and diagramming on paper before I really understood it.
 
Do you mean this link instead?
Teensy 4.1 EEPROM Endurance

Yes (re p#14 & #15), post #7 that thread. And the 'factor of 2' was an erroneous question p#14 - it was noted 4K sectors have 2K words so the /2 already included.

Found code 6/6/2019 that externally tests EEPROM storage mechanism for T_4.0 smaller 1080 bytes - not sure what thread it came from and seems there was a more extensive test version.
 
Back
Top