boot flags in eeprom, chicken and egg situation

mrdave45

Member
Hi,

I have a calibration routine I want to run the first time the teensy 4.1 is turned on.

I figure i want to set a boot flag in eeprom, probably at address 0. Check whether this is set to (lets call it is_calibration_set for ease) is_calibration_set == 0, run calibration then set calibration flag to 1 so it no longer runs on subsequent bootups.

Problem I've got is, how do I ensure that the eeprom address is zero to start with. I dont know how to set to zero without it presumably always setting to zero on power up, thereby causing the calibration to always run. Can i be sure that an unwritten to EEPROM is actually blank and all zeros? Is there some kind of programming time directive that does something only at program time for eeprom?

I found some solutions which involve loading in an initialization sketch, but surely this is a common situation for many systems and there's a better solution. Or perhaps is there a way to automatically load an eeprom memory file when programming. Im using platformIO in vscode.

I found this snippet of code for an AVR which apparently solves the problem (I think its a totally different platform), but I don't see how this would get round that problem. Wouldnt this line:

uint16_t EEMEM SerialNumber = 0;

set the counter back to zero each time the device is turned on?

/*
* EEPROM_serialno.c
*
* Created: 12-Sep-16 14:55:51
* Author : David Prentice
*/

#include <avr/io.h>
#include <avr/eeprom.h>

uint16_t EEMEM SerialNumber = 0;

int main()
{
uint16_t wordRead = eeprom_read_word(&SerialNumber); // read the existing value

wordRead++; //increment it
eeprom_update_word(&SerialNumber, wordRead); //write to EEPROM

while (1) {
}
}

Thanks
 
What if you were to check for 'c' 'a' 'l' in the first three EEPROM locations. If you don't find it (which is almost certainly guaranteed on a brand new T4.1), then perform your calibration routine, and finally, write the 'c' 'a' 'l' flag to the EEPROM to keep the calibration routine from running the next time.

If you ever want to repeat the calibration, then just perform the 15-second recovery, which among other things, reinitializes EEPROM (erasing your 'c' 'a' 'l' flag).

Mark J Culross
KD5RXT
 
Traditionally blank eeprom is all ones ( 0xff ) not zero. I would think Paul would implement that behavior if the eeprom is emulated. You could write a program to read and display a few values to assure yourself that is the case.
 
What I have done in the past includes things like:
A hex robot, where I store 18 servo offsets, to be used and maybe some additional information.

I would maybe have eeprom address 0 hold, a data version number, some value set in my program, so, it I change the format of the data, the next time I run it won't match and everything is reset.
I then maybe store my offsets in 1 I store a simple checksum value, like simple one byte addition of all of the values stored.
2-19 (could be more than this like 2 bytes per...) store the servo offsets.

Not very eloquent, but got the job done.

With T4.1 and the like, you also have another option, of storing all of the data in a file.
using LittleFS (littlefs_program)
 
Thanks all,
Well, I did a little more digging. Turns out EEMEM for AVR does do what i would like but is part of avr/eeprom.h I cant seem to find an equivalent.
The blank eeprom does appear to be written 0xFF.
I've decided for now that if address 0 > 0 then i'll run calibration.

As im loading the eeprom value into memory for the conditional statements and leaving it there, its going to be very easy to change this value at any other point to run this particular calibration routine again. The solutions not quite what I hoped for but it has at least made it more robust than my original strategy.
Thanks
 
Doing a first time sketch to set EEPROM to a known value works, but is extra work.

One way to avoid it - assuming the odds of a unique value being stored - like the Teensy Serial number. Would be to read EEPROM and see if it matches, if not then set it and then do the writes of the needed values in other locations.

This file: {local install}\hardware\teensy\avr\cores\teensy4\usb_desc.c

Has this function that gets the Teensy Serial Number:
Code:
void usb_init_serialnumber(void)
{
	char buf[11];
	uint32_t i, num;

	num = HW_OCOTP_MAC0 & 0xFFFFFF;
	// add extra zero to work around OS-X CDC-ACM driver bug
	if (num < 10000000) num = num * 10;
	ultoa(num, buf, 10);
	for (i=0; i<10; i++) {
		char c = buf[i];
		if (!c) break;
		usb_string_serial_number_default.wString[i] = c;
	}
	usb_string_serial_number_default.bLength = i * 2 + 2;
}

Setup could read that chip value and compare it to eeprom storage location(s) and finding it to match or not match could do the 'right thing'.
> proceed knowing it was done before
> write that serial number and the static settings as desired for next use, and proceed with that first time process done.

Putting a 'version #' in the code in another eeprom location would allow recognizing if it ever changes with updated code but still use the same scheme to do the 'right thing'.
 
Something like this:
Code:
// https://forum.pjrc.com/threads/71079-boot-flags-in-eeprom-chicken-and-egg-situation?p=312641&viewfull=1#post312641
#include "EEPROM.h"

// set EEPROM storage addresses and Code Version 
#define serNumAddr 100 // Base EEPROM offset address
#define codeVerAddr (serNumAddr + sizeof(uint32_t))
uint8_t codeVer = 1; 

void setup() {
  uint32_t serNum;
  uint32_t EEserNum;
  uint8_t EEcodeVer;
  Serial.begin(115200);
  while (!Serial && millis() < 4000 );
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  Serial.print( "Serial Number:" );
  Serial.println( serNum = get_serialnumber() );
  Serial.print( "Code Ver:" );
  Serial.println( codeVer );
  EEPROM.get( serNumAddr, EEserNum );
  if ( serNum == EEserNum  ) {
    EEPROM.get( codeVerAddr, EEcodeVer );
    if ( codeVer == EEcodeVer  ) {
      Serial.println( "All Good!" );
    }
    else { // update storage for new code version
      Serial.println( "New Code Version" );
      EEPROM.put( codeVerAddr, codeVer );
    }
  }
  else { // update storage for new TEENSY and code version
    Serial.println( "New TEENSY and Code Version" );
    EEPROM.put( serNumAddr, serNum );
    EEPROM.put( codeVerAddr, codeVer );
  }
}

void loop() {
}

uint32_t get_serialnumber(void)
{
  uint32_t num;
  num = HW_OCOTP_MAC0 & 0xFFFFFF;
  // add extra zero to work around OS-X CDC-ACM driver bug
  if (num < 10000000) num = num * 10;
  return num;
}

Code:
C:\T_Drive\tCode\EEPROM\BootSig\BootSig.ino Sep 12 2022 22:38:25
Serial Number:8983390
Code Ver:1
New Code Version
 
Looks like everything is solved here, but a couple quick answers to follow up, and for anyone who later finds this by search.

EEPROM emulation on Teensy 4.0, 4.1, MicroMod does indeed have EEPROM memory initialized to 0xFF.

AVR's EEMEM feature to initialize the memory during upload is not supported. Uploading new code leaves EEPROM memory intact. Teensy 2.0 and Teensy++ 2.0 (both discontinued) were the only models to ever support the EEMEM feature.

The restore process (holding the pushbutton for 15 seconds) wipes all erasable non-volatile memory. All EEPROM memory is erased back to 0xFF.

On the storage of calibration constants, you might consider writing 1 or more bytes with the version number of your software. If you later make changes to your calibration process, this sort of info written together with the raw data can make future firmware updates easier.
 
Looks like everything is solved here, but a couple quick answers to follow up, and for anyone who later finds this by search.

...
On the storage of calibration constants, you might consider writing 1 or more bytes with the version number of your software. If you later make changes to your calibration process, this sort of info written together with the raw data can make future firmware updates easier.

OP seemed satisfied, had fun writing the Teensy Ser# verify/store sketch in p#7 and in that one byte 'version' was included. Though it could have been packed in the upper byte of the Serial # since it does: num = HW_OCOTP_MAC0 & 0x00FFFFFF;
 
Doing a first time sketch to set EEPROM to a known value works, but is extra work.

One way to avoid it - assuming the odds of a unique value being stored - like the Teensy Serial number. Would be to read EEPROM and see if it matches, if not then set it and then do the writes of the needed values in other locations.

This file: {local install}\hardware\teensy\avr\cores\teensy4\usb_desc.c

Has this function that gets the Teensy Serial Number:
Code:
void usb_init_serialnumber(void)
{
	char buf[11];
	uint32_t i, num;

	num = HW_OCOTP_MAC0 & 0xFFFFFF;
	// add extra zero to work around OS-X CDC-ACM driver bug
	if (num < 10000000) num = num * 10;
	ultoa(num, buf, 10);
	for (i=0; i<10; i++) {
		char c = buf[i];
		if (!c) break;
		usb_string_serial_number_default.wString[i] = c;
	}
	usb_string_serial_number_default.bLength = i * 2 + 2;
}

Setup could read that chip value and compare it to eeprom storage location(s) and finding it to match or not match could do the 'right thing'.
> proceed knowing it was done before
> write that serial number and the static settings as desired for next use, and proceed with that first time process done.

Putting a 'version #' in the code in another eeprom location would allow recognizing if it ever changes with updated code but still use the same scheme to do the 'right thing'.

Thats inspired!
I like that a lot. Thanks.
 
Looks like everything is solved here, but a couple quick answers to follow up, and for anyone who later finds this by search.

EEPROM emulation on Teensy 4.0, 4.1, MicroMod does indeed have EEPROM memory initialized to 0xFF.

AVR's EEMEM feature to initialize the memory during upload is not supported. Uploading new code leaves EEPROM memory intact. Teensy 2.0 and Teensy++ 2.0 (both discontinued) were the only models to ever support the EEMEM feature.

The restore process (holding the pushbutton for 15 seconds) wipes all erasable non-volatile memory. All EEPROM memory is erased back to 0xFF.

On the storage of calibration constants, you might consider writing 1 or more bytes with the version number of your software. If you later make changes to your calibration process, this sort of info written together with the raw data can make future firmware updates easier.

Thats answered a lot of questions about the device for me and. i can largely stop digging now.

I take it that your last comment about version numbers is so that you can reuse the calibration values or whatever else is stored more easily as my firmware update could then rewrite the data to different addresses if it needs to as software version x puts certain data in a now known place?

I hadnt really considered that. I would either have used the same formatting (a no doubt fatal assumption) or just forced a recalibrate on first boot up.
 
...
I take it that your last comment about version numbers is so that you can reuse the calibration values or whatever else is stored more easily as my firmware update could then rewrite the data to different addresses if it needs to as software version x puts certain data in a now known place?

I hadnt really considered that. I would either have used the same formatting (a no doubt fatal assumption) or just forced a recalibrate on first boot up.

@KurtE referred to that in p#4: " a data version number, some value set in my program, so, it I change the format of the data, the next time I run it won't match and everything is reset."

Code in p#7 allocates and sets a one byte: uint8_t codeVer = 1;

So, if later the saved parameters are altered or extended the same Serial# test will pass, but if built with updated value: uint8_t codeVer = 2;

A recalibration and update could be performed in the p#7 code here where the stored 'codeVer' is updated:
Code:
    else { // update storage for new code version
      Serial.println( "New Code Version" );
      EEPROM.put( codeVerAddr, codeVer );
    }
 
@KurtE referred to that in p#4: " a data version number, some value set in my program, so, it I change the format of the data, the next time I run it won't match and everything is reset."

Code in p#7 allocates and sets a one byte: uint8_t codeVer = 1;

So, if later the saved parameters are altered or extended the same Serial# test will pass, but if built with updated value: uint8_t codeVer = 2;

A recalibration and update could be performed in the p#7 code here where the stored 'codeVer' is updated:
Code:
    else { // update storage for new code version
      Serial.println( "New Code Version" );
      EEPROM.put( codeVerAddr, codeVer );
    }

Ok, I did see that (p#4), but the pennys only just dropped. I think i see what you mean now. ok. Ill look into that.
Thanks
 
Back
Top