ATmega328P EEPROM Corruption Despite BOD Enabled

Hi everyone,

I'm working on a custom board based on the ATmega328P-AU, driving two stepper motors via TMC2660 drivers, and I'm running into a frustrating EEPROM corruption issue.

:light_bulb: The Setup:

  • MCU: ATmega328P-AU, running at 5V
  • Motor Drivers: 2x TMC2660 (SPI + STEP/DIR)
  • Power Supply: 5V rail shared between MCU and TMC drivers
  • EEPROM: Used to store a single integer (saved_insrt_len) only once

Summary: Flow + Risk Points
[Initial Config] → write once to EEPROM :check_mark:

[Boot] → read EEPROM in setup() → saved_insrt_len ← OK :check_mark:

[Normal Run] → use value from RAM → EEPROM untouched :check_mark:

[Later: reset or motor activity]

EEPROM becomes corrupted :cross_mark:
(saved_insrt_len invalid or magic byte erased)

Welcome to the forum

How exactly are you storing the integer to EEPROM and reading it back and what data type is the variable ?

Your written logic makes no sense to me, but posting your code might help us.
(in CODE tags please)

This is eeprom write and read operation
and
shared setup function where the eeprom gets read the stored eeprom value

int saved_insrt_len,

int write_read_IntIntoEEPROM(int address, String cmd)
{
int data = cmd.substring(8).toInt();

int current_value = readIntFromEEPROM(address);
int verify_value;

if(current_value != data )
{
// Create a data structure with the integer number.
// Store the data structure into the EEPROM at the specified address.
EEPROM.put(address, data);

EEPROM.get(address, verify_value);

if (verify_value != data) 
{
   return EEPROM_CONFIG_ERROR;
}

}
EEPROM.write(EEPROM_MAGIC_ADDR, EEPROM_MAGIC_VAL);
// Return the integer value stored in the data structure.
return data;
}

int readIntFromEEPROM(int address)
{

// Create a data structure to store the retrieved data.
int data;

// Read the data structure from the EEPROM at the specified address.
EEPROM.get(address, data);

// Optional debug output
// Serial.print("EEPROM valid: ");
// Serial.println(is_eeprom_valid() ? "Yes" : "No");
// Serial.print("saved_insrt_len: ");
// Serial.println(saved_insrt_len);

// Return the integer value stored in the data structure.
return data;
}

void setup()
{
//delay for tmc26x setup/powerup
delay(100);

Serial.begin(SERIAL_BAUD_RATE);

/initiliaze the GPIO pins/
GPIO_init();

/initiliaze the tmc26x driver ic/
tmc26x_init();

delay(2000);

/Run homing sequence according the configured homing sequence/
homing();

motor_pos = 0;

delay(2000);

saved_insrt_len = readIntFromEEPROM(MEM_ADDR_INSRT_LEN);

// Serial.print("saved_insrt_len : ");
// Serial.println(saved_insrt_len);

}

int saved_insrt_len,

int write_read_IntIntoEEPROM(int address, String cmd)
{
int data = cmd.substring(8).toInt();

int current_value = readIntFromEEPROM(address);
int verify_value;

if(current_value != data )
{
// Create a data structure with the integer number.
// Store the data structure into the EEPROM at the specified address.
EEPROM.put(address, data);

EEPROM.get(address, verify_value);

if (verify_value != data) 
{
   return EEPROM_CONFIG_ERROR;
}

}
EEPROM.write(EEPROM_MAGIC_ADDR, EEPROM_MAGIC_VAL);
// Return the integer value stored in the data structure.
return data;
}

int readIntFromEEPROM(int address)
{

// Create a data structure to store the retrieved data.
int data;

// Read the data structure from the EEPROM at the specified address.
EEPROM.get(address, data);

// Optional debug output
// Serial.print("EEPROM valid: ");
// Serial.println(is_eeprom_valid() ? "Yes" : "No");
// Serial.print("saved_insrt_len: ");
// Serial.println(saved_insrt_len);

// Return the integer value stored in the data structure.
return data;
}

void setup()
{
//delay for tmc26x setup/powerup
delay(100);

Serial.begin(SERIAL_BAUD_RATE);

/initiliaze the GPIO pins/
GPIO_init();

/initiliaze the tmc26x driver ic/
tmc26x_init();

delay(2000);

/Run homing sequence according the configured homing sequence/
homing();

motor_pos = 0;

delay(2000);

saved_insrt_len = readIntFromEEPROM(MEM_ADDR_INSRT_LEN);

// Serial.print("saved_insrt_len : ");
// Serial.println(saved_insrt_len);

}

Did you miss the request to use code tags ?

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the < CODE/ > icon above the compose window) to make it easier to read and copy for examination

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

Please post your full sketch, using code tags when you do

Posting your code using code tags prevents parts of it being interpreted as HTML coding and makes it easier to copy for examination

In my experience the easiest way to tidy up the code and add the code tags is as follows

Start by tidying up your code by using Tools/Auto Format in the IDE to make it easier to read. Then use Edit/Copy for Forum and paste what was copied in a new reply. Code tags will have been added to the code to make it easy to read in the forum thus making it easier to provide help.

It is also helpful to post error messages in code tags as it makes it easier to scroll through them and copy them for examination

type or paste code here
```int saved_insrt_len,

int write_read_IntIntoEEPROM(int address, String cmd)
{
int data = cmd.substring(8).toInt();

int current_value = readIntFromEEPROM(address);
int verify_value;

if(current_value != data )
{
// Create a data structure with the integer number.
// Store the data structure into the EEPROM at the specified address.
EEPROM.put(address, data);

EEPROM.get(address, verify_value);

if (verify_value != data) 
{
   return EEPROM_CONFIG_ERROR;
}
}
EEPROM.write(EEPROM_MAGIC_ADDR, EEPROM_MAGIC_VAL);
// Return the integer value stored in the data structure.
return data;
}

int readIntFromEEPROM(int address)
{

// Create a data structure to store the retrieved data.
int data;

// Read the data structure from the EEPROM at the specified address.
EEPROM.get(address, data);

// Optional debug output
// Serial.print("EEPROM valid: ");
// Serial.println(is_eeprom_valid() ? "Yes" : "No");
// Serial.print("saved_insrt_len: ");
// Serial.println(saved_insrt_len);

// Return the integer value stored in the data structure.
return data;
}

void setup()
{
//delay for tmc26x setup/powerup
delay(100);

Serial.begin(SERIAL_BAUD_RATE);

/initiliaze the GPIO pins/
GPIO_init();

/initiliaze the tmc26x driver ic/
tmc26x_init();

delay(2000);

/Run homing sequence according the configured homing sequence/
homing();

motor_pos = 0;

delay(2000);

saved_insrt_len = readIntFromEEPROM(MEM_ADDR_INSRT_LEN);

// Serial.print("saved_insrt_len : ");
// Serial.println(saved_insrt_len);

}

Better, but please post a sketch that compiles as there is much missing from the current one

/**********************************************************************************

   This was developed for the TMC26xx RLA board that is placed in the Quaco robot.
   The only pupose of this code is to initializes the tmc26x driver with required
   configuration values. Step and direction pulses will be sent from external
   microcontroller.

   IMPORTANT: This code should not be uploaded to microcontrollers with bootloader.
   microcontrollers with optiboot bootloader are slow to startup.
   hex file can be found at C:\Users\Sastra\AppData\Local\Temp\arduino_build_xxxxxx

   download TMC26X_config.exe tool for easily finding configuration settings

 **********************************************************************************/

#include <SPI.h>
#include <EEPROM.h>
#include "main.h"
#include "cmd_actions.h"
#include "tmc26x.h"

String command;


void setup()
{
  //delay for tmc26x setup/powerup
  delay(100);

  Serial.begin(SERIAL_BAUD_RATE);

  /*initiliaze the GPIO pins*/
  GPIO_init();

  /*initiliaze the tmc26x driver ic*/
  tmc26x_init();

  delay(2000);

  /*Run homing sequence according the configured homing sequence*/
  homing();

  motor_pos = 0;
  
  delay(2000);

  saved_insrt_len = readIntFromEEPROM(MEM_ADDR_INSRT_LEN);
  
  // Serial.print("saved_insrt_len : ");
  // Serial.println(saved_insrt_len);

}


void loop() {
   
  if (Serial.available()) {
    command = Serial.readStringUntil('\n');
          /*get the respective command value*/
      uint8_t return_value = parseCommand(command);
      
      if (return_value)
      {
        //   /*executes the command*/
        return_value = cmd_action(return_value);
        /*send the command feedback*/
        Serial.println(return_value);
      }
    }

}



/**
  @brief Initializes the GPIO pins for motor control and limit switch input.

  This function sets up the GPIO pins used for controlling motors and reading
  the limit switch state. Pins are configured as either inputs or outputs
  depending on their intended use.
*/
void GPIO_init()
{
  // Configure the limit switch pin for the right motor as an input.
  pinMode(LIMIT_SW_PIN_RMOT, INPUT);

  // Configure chip select pins for the motors as outputs.
  pinMode(CS_PIN_RMOT, OUTPUT);

  // Configure step, direction, and enable pins for the right motor as outputs.
  pinMode(STEP_PIN_RMOT, OUTPUT);
  pinMode(DIR_PIN_RMOT, OUTPUT);
  pinMode(EN_PIN_RMOT, OUTPUT);

}


int parseCommand(String command)
{
  // === Fixed/Direct Match Commands ===
  if (command == "h:") return homing_cmd;
  if (command == "i:") return insert_cmd;
  if (command == "r:") return remove_cmd;
  if (command == "g:fw:") return get_firmware_cmd;
  if (command == "b:") return jmp_to_bootloader;

  // === Fixed Pattern Commands (no parameter, but structured) ===
  if (command == "e:g:l:i:") return get_insert_len_cmd;


  // === Parameterized Commands ===
  bool isSetInsertCmd = command.startsWith("e:s:l:i:");

  char cmdType;
  int prefixLength;
  int minVal, maxVal;
  int returnCode;

  if (isSetInsertCmd) {
    cmdType = 'i'; prefixLength = 8; minVal = 1; maxVal = INSERT_LEN; returnCode = Set_insert_len_cmd;
  } 

  else {
    Serial.println(INVALID_CMD); // Error 13
    return 0;
  }


  // Validate colon at the end
  if (command.charAt(command.length() - 1) != ':') {
    Serial.println(ARG_LEN_ERR); // Error 14
    return 0;
  }

  // Extract parameter string
  // Extract and trim parameter string
  String paramStr = command.substring(prefixLength, command.length() - 1);
  paramStr.trim(); // modifies in place
  int value = paramStr.toInt();
  if (value == 0 && paramStr != "0") {
    Serial.println(PARAM_OUT_OF_BOUND); // Not a number
    return 0;
  }


  if (value < minVal || value > maxVal) {
    Serial.println(PARAM_OUT_OF_BOUND); // Error 16
    return 0;
  }

  return returnCode;
}

#include "cmd_actions.h"
#include "main.h"
#include <EEPROM.h>

int saved_insrt_len,motor_pos;
unsigned int i;

/**
 * @brief Executes the first homing sequence for motors.
 *
 * This function performs a predefined homing sequence for the insert and swipe motors.
 * It moves the insert motor in reverse, triggers the swipe motor until it hits a limit switch,
 * and then moves the swipe motor in reverse to complete the homing process.
 */


uint8_t check_stored_value_inside_boundary()
{  
  
    // Serial.print(" from check_stored_value_inside_boundary saved_insrt_len : ");
    // Serial.println(saved_insrt_len);
    if (saved_insrt_len < MIN_INSERT_LEN || saved_insrt_len > INSERT_LEN) {
        return SAVED_VAL_OUT_OF_BOUND;
    }

    return NO_ERROR;

}
/**
  @brief Runs a swipe motor in a specified direction for a calculated number of steps.

  This function retrieves a value from EEPROM, calculates the total number of steps
  based on this value, and then drives a motor in the specified direction using
  a given number of step pulses.
  
  @param dir The direction in which the motor will run.
             Use `true` for one direction and `false` for the opposite direction.
*/

static void insert_motor_run(bool dir, uint32_t distance_mm)
{
  digitalWrite(DIR_PIN_RMOT, dir);

  const uint8_t delays[] = {130,100,80,60};
  const uint8_t steady_delay = 50;
  uint8_t accel_deaccel_stages = sizeof(delays) / sizeof(delays[0]);

  MotorStepProfile profile = calculate_step_profile(distance_mm, SWIPE_MOT_STEPS_PER_MM, accel_deaccel_stages);

  // Steady phase
  rotate_motor(steady_delay, profile.steady_steps);

  // Deceleration phase
  for (int i = accel_deaccel_stages -1 ; i >= 0; i--) {
    rotate_motor(delays[i], profile.steps_per_accel_stage);
  }
}


/**
  @brief Runs a swipe motor in a specified direction for a calculated number of steps.

  This function retrieves a value from EEPROM, calculates the total number of steps
  based on this value, and then drives a motor in the specified direction using
  a given number of step pulses.*/
uint8_t insert_card()
{

    // Serial.println(saved_insrt_len);
  if(check_stored_value_inside_boundary() == NO_ERROR )
  {
    if (motor_pos != 0){
      return INVALID_POS;
    }
    insert_motor_run(FWD ,saved_insrt_len);   
      
    motor_pos = saved_insrt_len;

    return SUCCESS_CMD;
  }
  else{
    if (motor_pos != 0){
      return INVALID_POS;
    }
    insert_motor_run(FWD,INSERT_LEN);
    motor_pos = INSERT_LEN;

    return SAVED_VAL_OUT_OF_BOUND;
  }
}


/**
  @brief Runs a swipe motor in a specified direction for a calculated number of steps.

  This function retrieves a value from EEPROM, calculates the total number of steps
  based on this value, and then drives a motor in the specified direction using
  a given number of step pulses.*/
uint8_t remove_card()
{

    // Serial.print(" printing from remove : ");
    // Serial.println(saved_insrt_len);
  if(check_stored_value_inside_boundary() == NO_ERROR )
  {
      if (motor_pos != saved_insrt_len){
          return INVALID_POS;
      }
      insert_motor_run(REV ,saved_insrt_len);

      motor_pos = 0; 

      return SUCCESS_CMD;
  }

  else{

    if (motor_pos != INSERT_LEN){
        return INVALID_POS;
    }

    insert_motor_run(REV,INSERT_LEN);
    motor_pos = 0; 
    return SAVED_VAL_OUT_OF_BOUND;
    
  }


}
/**
   @brief Runs the swipe motor until it activates a limit switch.

   This function continuously moves the swipe motor by generating step pulses
   until the limit switch is triggered. It sets the motor's direction and generates
   pulses on the step pin. The loop terminates once the limit switch is activated.
*/
void homing()
{
  digitalWrite(DIR_PIN_RMOT, HIGH);
  while (digitalRead(LIMIT_SW_PIN_RMOT) == HIGH)
  {
    
    rotate_motor(HOM_DELAY,1);
  }
  digitalWrite(DIR_PIN_RMOT, LOW);
  while (digitalRead(LIMIT_SW_PIN_RMOT) == LOW)
  {

    rotate_motor(250,HOM_POS * SWIPE_MOT_STEPS_PER_MM);

  }
    saved_insrt_len = 0;
}

bool is_eeprom_valid() {
    return EEPROM.read(EEPROM_MAGIC_ADDR) == EEPROM_MAGIC_VAL;
}
/**
   @brief Writes an integer into the EEPROM at a specified address.

   This function takes an integer value and writes it into the EEPROM at the specified
   address. It uses the EEPROM.put function, which handles the EEPROM memory
   operations to store complex data types.

   @param address The EEPROM address where the integer will be stored.
                  Must be within the EEPROM's addressable range.
   @param data The integer value to store in the EEPROM.

   @return none
*/


int write_read_IntIntoEEPROM(int address, String cmd)
{
  int data = cmd.substring(8).toInt();

  int current_value =  readIntFromEEPROM(address);
  int verify_value;

  if(current_value != data )
  {
  // Create a data structure with the integer number.
  // Store the data structure into the EEPROM at the specified address.
    EEPROM.put(address, data);

    EEPROM.get(address, verify_value);
    
    if (verify_value != data) 
    {
       return EEPROM_CONFIG_ERROR;
    }

  }
  EEPROM.write(EEPROM_MAGIC_ADDR, EEPROM_MAGIC_VAL);
  // Return the integer value stored in the data structure.
  return data;
}

/**
  @brief Reads an integer from the EEPROM at a specified address.

  This function retrieves a stored integer from the EEPROM by reading a data
  structure at the given address. It uses the EEPROM.get function to perform
  the read operation.

  @param address The EEPROM address from where the integer will be read.
                 Must be within the EEPROM's addressable range.
  @return The integer value retrieved from the EEPROM.
*/
int readIntFromEEPROM(int address)
{
  
  // Create a data structure to store the retrieved data.
  int data;

  // Read the data structure from the EEPROM at the specified address.
  EEPROM.get(address, data);

  // Optional debug output
  // Serial.print("EEPROM valid: ");
  // Serial.println(is_eeprom_valid() ? "Yes" : "No");
  // Serial.print("saved_insrt_len: ");
  // Serial.println(saved_insrt_len);

  // Return the integer value stored in the data structure.
  return data;
}



  /**
  @brief Runs an insert motor in a specified direction for a calculated number of steps.

   Generates a number of step pulses on a motor driver input pin to rotate a stepper motor..

  @param

  delay: Time in microseconds between pulse transitions (i.e., half of the full step period).

  steps: The total number of steps (pulses) to generate..

  @return - (void)
*/

static void rotate_motor( uint32_t delay,uint32_t steps)
{

  for (uint32_t i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN_RMOT, LOW);
    delayMicroseconds(delay);
    digitalWrite(STEP_PIN_RMOT, HIGH);
    delayMicroseconds(delay);
  }

}

  /**
  @brief Calculates how many steps to allocate to acceleration, steady speed, and deceleration for a given motion profile.

  @param
    distance_mm: Distance to move in millimeters.

    steps_per_mm: Motor resolution (steps per mm of motion).

    accel_stages: Number of acceleration stages (granularity of speed ramp-up).

  @return -
    A MotorStepProfile structure containing:

    accel_steps: 20% of total steps for acceleration.

    decel_steps: 20% of total steps for deceleration.

    steady_steps: Remaining steps (60%) for constant speed motion.

    steps_per_accel_stage: accel_steps divided by accel_stages.
*/

MotorStepProfile calculate_step_profile(uint32_t distance_mm, uint32_t steps_per_mm, uint8_t accel_stages)
{
  MotorStepProfile profile;

  uint32_t total_steps = distance_mm * steps_per_mm;

  profile.decel_steps = total_steps * 0.2;
  profile.steady_steps = total_steps - profile.decel_steps;
  profile.steps_per_accel_stage = profile.decel_steps / accel_stages;

  return profile;
}

void jump_to_bootloader() {
  cli(); // Disable interrupts

  ((void (*)(void))0x3C00)(); // For 512-word bootloader at top of flash

}

/**
  @brief Executes specific actions based on a numeric action_number,
   typically parsed from a command string. Each action corresponds to a 
   hardware operation or EEPROM interaction related to motor configuration or card handling.

  @param address uint8_t action_number — a number between 1 and 9 representing a command to perform.

  @return uint8_t return_status — status code indicating success or failure:

    SUCCESS_CMD action completed successfully
    INVALID_CMD unrecognized command number
  */
uint8_t cmd_action(uint8_t action_number) 
{
  uint8_t return_status;
  
  switch (action_number)
  {
      case 1:
          // Configure the insert motor height
          saved_insrt_len =   write_read_IntIntoEEPROM(MEM_ADDR_INSRT_LEN, command);
          return_status = saved_insrt_len;
          break;

      case 2:
          homing();
          return_status = SUCCESS_CMD;
          break;

      case 3:          
          
          return_status = insert_card();
          break;

      case 4:
          
          return_status = remove_card();
          break;
      
      case 5: 
          Serial.println(FW_VER, 1); // IMndicate success with "0"
          return_status = SUCCESS_CMD;
          break;

      case 6:
          return_status = readIntFromEEPROM(MEM_ADDR_INSRT_LEN);
          break;
      case 7:
          Serial.println("jumping to bootlloader code"); 
          jump_to_bootloader();
          break;
      default:
          return_status = INVALID_CMD;
          break;
    }
  return return_status;
}

What is the relationship between the two sketches that you posted ?

Where are the "MAGIC" addresses and values #defined or declared ?

System Flow Overview

  1. System startup begins with the first sketch, where the program enters a waiting state.
  2. The system listens for incoming commands via UART.
  3. Upon receiving a command, it is parsed using parseCommand() to determine what action should be taken.
  4. The resulting return_value (action code) from parseCommand() is then passed to the function cmd_action(), which is defined in the second sketch, to execute the corresponding hardware or EEPROM-related operation.
#define EEPROM_MAGIC_ADDR       (int)100
#define MEM_ADDR_INSRT_LEN      (int)(EEPROM_MAGIC_ADDR + 1)
#define EEPROM_MAGIC_VAL        0x42

If I understand correctly you are storing an int, which needs 2 bytes of storage at EEPROM address 100 and a second int at address 101

Is that what you are doing and, if so, can you see a problem ?

forget about magic address my MEM_ADDR_INSRT_LEN(101) is where i am storing the integer value and its corrupting after sometimes

Reproduce the fault by the following simple hardware/software setup.

  1. Connect your motor with Ardino UNO. What kind of motor it is? Servo Motor or DC? Intialize the motor in the setup() function and run it in the loop() function manually.

  2. Write 3-byte data into three consective loccations of the internal EEPROM beginning at address 0x0010. Do it in the setup() function.

  3. In the loop() function keep reading the EEPROM's data and show them on the Serial Monitor at 1-sec interval.

  4. Start the Motor manually. (It is assumed that the Motor as been initialized in the setup() function.)

  5. Check that the Serial Monitor shows EEPROM's data which are different.

  6. Post the screenshot of the Serial Monitor.

:high_voltage:Electrical Noise or EMI

In electrically noisy environments (e.g., motors, relays, high-current switching), electromagnetic interference can affect logic levels and possibly corrupt EEPROM data.

Best Practice: Use decoupling capacitors, shielding, and proper grounding.

thanks for the feedback i will let you know, i am using stepper motor

do you have concern in code?

You have not posted a sketch that compiles so it is difficult to say. Please post your complete sketch. For instance, where is MEM_ADDR_INSRT_LEN #defined ?

I suspect that you are writing values to EEPROM that overwrite previously written values because of the length of data being written and the addresses being written to

Card_insert_module.zip (43.2 KB)
i have shared entire code, look into it, and let me know

Please, reduce the size of the sketch and post it here with code tags.