EEPROM Gets Updated Mysteriously On First Run

by Alex Johnson 46 views

Ever wondered what goes on under the hood when your device boots up for the first time? It’s usually a clean slate, a fresh start where everything is initialized just right. But what happens when that fresh slate seems to get written on before you even get a chance to set things up? That's exactly the perplexing puzzle that user topquark22 encountered, and it’s a fascinating dive into the inner workings of device initialization, particularly concerning the EEPROM.

The Initial Enigma: A Fresh EEPROM No More

Imagine this: you've just flashed a new firmware, eager to see your device come to life. You start with a completely fresh EEPROM, meaning all its memory locations are filled with the default 0xFF value. This signifies that nothing has been configured yet, and the device should proceed with its standard setup routine. However, in this particular scenario, something peculiar happens during the very first run. Instead of remaining pristine, certain memory addresses within the EEPROM suddenly show values that weren't there before. Specifically, addresses 0xNf0 through 0xNf3 – where NN corresponds to the message bank ID (ranging from 0 to 3) – get populated with a default speed setting (0x64 0x00 0x00 0x00). This happens even though no explicit command was issued to write this data. It’s as if the EEPROM is updating itself, a truly mysterious occurrence that prevented the crucial prepareDevice() function from executing correctly.

The Setup: Reproducing the Mystery

To truly understand and tackle a bug, reproducibility is key. The user, topquark22, meticulously outlined the steps to recreate this baffling issue. It all starts with configuring specific jumpers on the device. By setting jumpers A4 and A5 to a particular message bank (in this case, message bank 0 was used for testing), the stage is set for the experiment. The first code uploaded is eeprom_reset.ino. This is intended to ensure a clean slate, and indeed, subsequent checks using eeprom_util.ino confirm that addresses 0x000, 0x0F0 through 0x0FB are indeed all 0xFF, verifying the EEPROM is as expected before the critical step. The next step is to upload morse.ino, which is the main program intended to run. After this, the eeprom_util.ino is uploaded again. This final check is where the anomaly becomes apparent. Upon uploading eeprom_util.ino the second time, the addresses 0x000 and 0x0F0 through 0x0FF are no longer all 0xFF. This confirms that morse.ino (or something it triggers) is indeed altering the EEPROM in an unexpected way, even though the intention was for a first-time initialization.

The Consequence: A Failed Initialization

The immediate impact of this premature EEPROM modification is a failure in the device's initialization process. The prepareDevice() function is supposed to perform essential setup tasks based on the initial state of the EEPROM. However, because addresses 0x0F0 through 0x0F3 are already populated with the default speed setting, prepareDevice() incorrectly interprets this as the device already being initialized. It sees these bytes and thinks, “Ah, the speed is set, so it must have been configured before.” Consequently, it skips the crucial initialization steps it’s supposed to perform on a truly fresh device. This leads to a cascade of problems, as the device doesn't get configured with the correct default settings for things like dot length and pause length, which are supposed to be written to addresses 0x0F4-0xF7 and 0xF8-0xFB respectively. The device is left in an uninitialized or improperly initialized state, failing to operate as intended right from the start.

The Expected Outcome: A Smooth Start

Ideally, after uploading the morse.ino program to a device with a fresh EEPROM, the initialization should proceed flawlessly. The prepareDevice() function should correctly identify the EEPROM as being uninitialized (i.e., address 0x000 should still be 0xFF before any configuration). Based on this, it should then proceed to write the default settings. This means that after the morse.ino program has run its initial setup, the EEPROM should reflect the following:

  • Address 0x000: This address should ideally be set to 0x00 after successful initialization, indicating that the setup process has been completed. Initially, before prepareDevice() runs, it should remain 0xFF.
  • Addresses 0x0F0 through 0x0F3: These bytes store the default speed. The expected value here is 0x64 0x00 0x00 0x00, representing the default speed setting.
  • Addresses 0x0F4 through 0x0F7: These are reserved for the default dot length, which should be initialized to its standard value.
  • Addresses 0xF8 through 0xFB: These bytes hold the default pause length, also to be set to its appropriate default value.

This sequence ensures that the device starts with a known, correct configuration, ready for operation without any hiccups.

The Fix: Shifting the Initialization Check

The core of the problem lies in how the prepareDevice() function determines if the device has already been initialized. Currently, it relies on checking the OFFSET_SPEED (which is at address 0xF0) to gauge the initialization status. However, as we've seen, this address can get mysteriously populated with default speed values before prepareDevice() has a chance to run its full initialization routine. This leads to the function incorrectly concluding that the device is already set up.

The proposed and implemented fix cleverly addresses this by changing the address used for the initialization status check. Instead of looking at OFFSET_SPEED (0xF0), the prepareDevice() function now checks OFFSET_MESSAGE (which is at address 0x00). This is a much more reliable indicator of a fresh start. If OFFSET_MESSAGE is 0xFF, it truly signifies an uninitialized EEPROM, allowing prepareDevice() to proceed with the full initialization sequence. If it’s anything else, it implies some prior configuration or a previous successful initialization attempt.

This change is reflected in commit [commit hash or link], which provides the specific code modification. By shifting the check to address 0x00, the prepareDevice() function can now reliably distinguish between a truly fresh EEPROM and one that has undergone some unexpected pre-configuration. This ensures that the device is properly initialized every time, starting from a clean slate as intended.

The Lingering Mystery: Where Does the Speed Come From?

While the fix effectively resolves the immediate issue of improper initialization, the root cause of the premature EEPROM update remains an open question. The mystery persists: how exactly is the speed data getting written to addresses 0x0F0 through 0x0F3 before the prepareDevice() function is even called to perform the initial setup? There’s no obvious call or function within the morse.ino sketch that directly writes to these specific EEPROM addresses during its initial loading or execution phase. This suggests a deeper, possibly subtle interaction happening at a lower level, perhaps during the firmware upload process itself, or an unintended side effect of how the microcontroller handles initial memory states. Investigating this further might involve delving into the bootloader, the specific memory management routines of the microcontroller, or even potential race conditions. For now, the workaround ensures correct device behavior, but the peculiar origin of that early EEPROM write remains a captivating puzzle for firmware enthusiasts and developers alike.

For more insights into embedded systems and microcontroller programming, you can explore resources like Embedded.com or Hackaday.