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();
}