SPI - R/W to ST95P08 EEPROM

25 views Asked by At

I am trying to help my niece with her Mercedes Sprinter W903 2000 model. The car is facing a well known "Start Error" fault, which is caused by a fault in the immobilizer unit.

I have found a immobilizer emulator which works with the CR2 ECU in the car, but in order to get this to work, I need to overwrite some data in the ST95P08 EEPROM in the ECU.

ST95P08 datasheet link: ST95P08 Datasheet(PDF) - STMicroelectronics

The EEPROM has been de-soldered and wired up to my Arduino Uno.

Using the following code, I am able to read the content of all the 1000 addresses in the EEPROM:

 #include <SPI.h>

 #define CS_PIN 10 // Chip select pin (S)
 #define MOSI_PIN 11 // Serial Data input (D)
 #define MISO_PIN 12 // Serial data output (Q)
 #define SCK_PIN 13 // Serial clock 
 #define W_PIN 7 // Write Protect (W)
 #define HOLD_PIN 6 // Hold

void setup() {
// Set the pin modes
pinMode(CS_PIN, OUTPUT);
pinMode(W_PIN, OUTPUT);
pinMode(HOLD_PIN, OUTPUT);
// pinMode(MOSI_PIN, OUTPUT); 
// pinMode(MISO_PIN, INPUT); 
// pinMode(SCK_PIN, OUTPUT);

// Start the SPI library
SPI.begin();
// Deselect the EEPROM 
digitalWrite(CS_PIN, HIGH);
// Only readmode
digitalWrite(W_PIN, LOW); // Enable write protect (FOR READING)
digitalWrite(HOLD_PIN, HIGH); // Disable HOLD
Serial.begin(9600); // Init serial comm

// Adjust SPI settings for EEPROM
SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
}

void loop() {
// Read from address 0x00 to 0x3FF (for a 1 KByte EEPROM)
for (unsigned int address = 0; address < 1024; address++) {
  byte data = readEEPROM(address);
  Serial.print("Address 0x");
  if (address < 0x10) Serial.print("0"); // Print leading zero for addresses less than 0x10
    Serial.print(address, HEX); // Print the address in HEX
    Serial.print(": 0x");
    Serial.println(data, HEX); // Print the data in HEX
    delay(50); // Short delay to not overwhelm the serial monitor
  }
  while(1); // Stop after reading the entire EEPROM
}

byte readEEPROM(byte address) {
  digitalWrite(CS_PIN, LOW); // Select the EEPROM
  SPI.transfer(0x03); // READ instruction code
  // For the ST95P08, which has 1KByte, only one address byte is needed.
  SPI.transfer(address); // Send the LSB of address to read from
  byte result = SPI.transfer(0x00); // Read the byte 
  digitalWrite(CS_PIN, HIGH); // Deselect the EEPROM
  return result; 
}

void writeEEPROM(unsigned int address, byte data) {
digitalWrite(CS_PIN, LOW);
SPI.transfer(0x02); // WRITE instruction code
SPI.transfer((address >> 8) & 0xFF); // MSB of the address
SPI.transfer(address & 0xFF); // LSB of the address
SPI.transfer(data);
digitalWrite(CS_PIN, HIGH);

// Wait for the write to complete
delay(10); // This delay depends on the EEPROM's write cycle time; adjust as necessary
}

This works fine, and the output (shortened) looks like this:

Address 0x00: 0x61
Address 0x01: 0xFF
Address 0x02: 0xFF
Address 0x03: 0x71
Address 0x04: 0xFF
Address 0x05: 0x16
Address 0x06: 0x16
Address 0x07: 0x30
Address 0x08: 0x39

The four addresses which needs new data are: 0x1D0, 0x1D1, 0x1D2, 0x1D3, 0x1D4 When the data of these addresses are read, I get:

Address 0x1D0: 0xFF
Address 0x1D1: 0xFF
Address 0x1D2: 0xFF
Address 0x1D3: 0xFF
Address 0x1D4: 0xFF

I try to write to these addresses using the following code:

 #include <SPI.h>

 #define CS_PIN 10 // Chip select pin (S)
 #define W_PIN 7 // Write Protect (W)
 #define HOLD_PIN 6 // Hold

 bool isWritingMode = false; // Set to 'true' for writing, 'false' for reading

 // Function Prototypes
 void writeEnable();
 void writeEEPROM(unsigned int address, byte data);
 byte readEEPROM(unsigned int address);
 byte readStatusRegister();
 void waitForWriteCompletion();

 void setup() {
 pinMode(CS_PIN, OUTPUT);
 digitalWrite(W_PIN, HIGH);
 pinMode(W_PIN, OUTPUT);
 pinMode(HOLD_PIN, OUTPUT);

 digitalWrite(HOLD_PIN, HIGH); // Disable HOLD
 digitalWrite(CS_PIN, HIGH); // Ensure EEPROM is not selected

 SPI.begin();
 Serial.begin(115200); 
 SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));


 // Test WREN-able, write "0x01" to addr 0x01 (current value: 0xFF)
 testWriteEnableLatchAndWrite();
 // checkBlockProtection();

 // if (isWritingMode) {
 //   // Disable write protection for writing
 //   digitalWrite(W_PIN, HIGH);
 //   // digitalWrite(W_PIN, LOW); 
 // } else {
 //   // Enable write protection for reading
 //   // digitalWrite(W_PIN, HIGH);
 //   digitalWrite(W_PIN, LOW);
 // }
 }

 void loop() {
 }

 void testWriteEnableLatchAndWrite() {

 writeEEPROM(0x1D0, 0x02);
 waitForWriteCompletion();
 delay(100);

 byte readValue1 = readEEPROM(0x1D0);
 Serial.print("Read back value from address 0x1D0: 0x");
 Serial.println(readValue1, HEX);

 writeEEPROM(0x1D1, 0x02);
 waitForWriteCompletion();
 delay(100);

 byte readValue2 = readEEPROM(0x1D1);
 Serial.print("Read back value from address 0x1D1: 0x");
 Serial.println(readValue2, HEX);

 writeEEPROM(0x1D2, 0x00);
 waitForWriteCompletion();
 delay(100); 

 byte readValue3 = readEEPROM(0x1D2);
 Serial.print("Read back value from address 0x1D2: 0x");
 Serial.println(readValue3, HEX);

 writeEEPROM(0x1D4, 0xFF);
 waitForWriteCompletion();
 delay(100);

 byte readValue4 = readEEPROM(0x1D4);
 Serial.print("Read back value from address 0x1D4: 0x");
 Serial.println(readValue4, HEX);

 }

 byte readStatusRegister() {
 digitalWrite(CS_PIN, LOW);
 SPI.transfer(0x05); // RDSR command
 byte status = SPI.transfer(0x00); // Dummy transfer to read the status
 digitalWrite(CS_PIN, HIGH);
 return status;
 }

 void checkBlockProtection() {
 byte status = readStatusRegister();
 Serial.print("Status Register: 0x");
 Serial.println(status, HEX);

 // Extracting BP bits
 byte bpBits = (status & 0x0C) >> 2; // BP bits are b3 and b4, shifting right by 2 to get the value

 Serial.print("Block Protection Bits (BP1 BP0): ");
 Serial.println(bpBits, BIN);

 switch(bpBits) {
 case 0b00:
 Serial.println("No block protection.");
 break;
 case 0b01:
 Serial.println("Upper quarter write-protected.");
 break;
 case 0b10:
 Serial.println("Upper half write-protected.");
 break;
 case 0b11:
 Serial.println("All memory write-protected.");
 break;
 default:
 This case should not happen as BP bits are only two bits
 Serial.println("Invalid Block Protection Bits.");
 break;
 }
 }

 byte readEEPROM(unsigned int address) {
 digitalWrite(CS_PIN, LOW); 
 SPI.transfer(0x03); 
 SPI.transfer((address >> 8) & 0xFF); // MSB of the address
 SPI.transfer(address & 0xFF); // LSB of the address
 byte result = SPI.transfer(0x00);
 digitalWrite(CS_PIN, HIGH);
 return result; 
 }

 void writeEnable() {
 digitalWrite(CS_PIN, LOW);
 SPI.transfer(0x06); // WREN command
 delay(5);
 digitalWrite(CS_PIN, HIGH);
 }

 void waitForWriteCompletion() {
 Serial.println("Waiting for write to complete...");
 byte status;
 do {
 status = readStatusRegister();
 if (status & 0x01) { // If WIP bit is set, write is still in progress
 Serial.println("Write in progress...");
 }
 delay(10); // Short delay before checking again
 } while (status & 0x01); // Loop until WIP bit clears

 Serial.println("Write operation completed.");
 }

 bool checkWELCleared() {
 byte status = readStatusRegister();
 if (status & 0x02) { // If WEL bit is set, then it's not cleared yet
 Serial.println("WEL bit is not cleared yet.");
 return false;
 } else {
 Serial.println("WEL bit cleared.");
 return true;
 }
 }

 bool checkWriteEnable() {
 digitalWrite(CS_PIN, LOW);
 SPI.transfer(0x05); // RDSR command to read the status register
 byte status = SPI.transfer(0x00); // Dummy transfer to read the response
 digitalWrite(CS_PIN, HIGH);

 // Print the status register value for debugging
 Serial.print("Status register: 0x");
 Serial.println(status, HEX);

 // Check if the WEL bit is set
 bool isWELSet = status & 0x02; // 0x02 corresponds to the WEL bit
 if (isWELSet) {
 Serial.println("WEL bit is set. EEPROM is ready for write operation.");
 } else {
 Serial.println("WEL bit is not set. EEPROM is not ready for write operation.");
 }
 return isWELSet;
 }

 void writeEEPROM(unsigned int address, byte data) {
 // Serial.println("Enable EEPROM write...");
 writeEnable(); // Ensure the EEPROM is write-enabled
 delay(100); // Delay after sending WREN, ensure EEPROM has enough time to set WEL

 // Serial.println("Checking if WEL bit is set...");
 if (!checkWriteEnable()) {
 Serial.println("Write Enable Latch not set! Exiting..");
 return; // Exit if WEL not set
 }

 // Serial.println("Setting CS pin low...");
 digitalWrite(CS_PIN, LOW);

 // Serial.println("Sending WRITE instruction code...");
 SPI.transfer(0x02); // WRITE instruction code

 // Serial.println("Sending MSB of address...");
 SPI.transfer((address >> 8) & 0xFF); // MSB of the address

 // Serial.println("Sending LSB of address...");
 SPI.transfer(address & 0xFF); // LSB of the address

 // Serial.print("Sending data to address: 0x");
 // Serial.print(address, HEX);
 // Serial.print(" -> Data: 0x");
 // Serial.println(data, HEX);
 SPI.transfer(data);

 // Serial.println("Setting CS pin HIGH...");
 digitalWrite(CS_PIN, HIGH);
 delay(100);

 // delay(10); // Wait for the write to complete
 // Serial.println("Waiting for write completion...");
 // waitForWriteCompletion();
 }
0

There are 0 answers