Hello everyone,
In this post, you will find how to READ/WRITE SPI based serial EEPROM chips like winbond W25X10A / W25X20A / W25X40A / W25X80A, Atmel AT25HP256 / AT25HP512, etc. using Arduino Uno. Same can be done with other Arduino boards also.
I was little “Bit” off and I saw a dead desktop motherboard (ASUS P5KPL-AM/PS) lying around and covered by heavy dust underneath my sofa. Instantly, playing with these little bits instead – came to my mind. It has a winbond W25X80A EEPROM BIOS chip. I was working with Arduino and decided to play with the EEPROM chip. Long before this, I already have faced the problem that when one need to read/write a BIOS chip but do not have any external EEPROM programmer. May be you are one of them as well. If you are one of them or you want to learn how to read/write a serial EEPROM chip just by using any Arduino development board, keep reading.
By reading this article, you’ll learn the following things:
- About EEPROM chips and how they work
- SPI Protocol
- Serial communication between Arduino and computer using python pyserial
Winbond W25X80A is a 8mbit or 1MB EEPROM chip and it is very common to find. It has 8 pins like below and many EEPROM chips has exactly same pin configuration.

IN NO. | PIN NAME | I/O | FUNCTION |
1 | /CS | I | Chip Select Input |
2 | DO | O | Data Output |
3 | /WP | I | Write Protect Input |
4 | GND | – | Ground |
5 | DIO | I/O | Data Input / Output |
6 | CLK | I | Serial Clock Input |
7 | /HOLD | I | Hold Input |
8 | VCC | – | Power Supply |
Note: This chip is operated on 3.3V. Don’t apply 5V on it or it will just get fried up.
Pins and their functions
Chip Select (/CS)
The SPI Chip Select (/CS) pin enables and disables device operation.
The /CS input must track the VCC supply level at power-up. If needed a 10kΩ pull-up resister on /CS can be used to accomplish this.
Serial Data Output (DO)
The SPI Serial Data Output (DO) pin provides a means for data and status to be serially read from (shifted out of) the device. Data is shifted out on the falling edge of the Serial Clock (CLK) input pin.
Write Protect (/WP)
The Write Protect (/WP) pin can be used to prevent the Status Register from being written.
HOLD (/HOLD)
The Hold (/HOLD) pin allows the device to be paused while it is actively selected.
Serial Clock (CLK)
The SPI Serial Clock Input (CLK) pin provides the timing for serial input and output operations.
Serial Data Input / Output (DIO)
The SPI Serial Data Input/Output (DIO) pin provides a means for instructions, addresses and data to be serially written to (shifted into) the device. Data is latched on the rising edge of the Serial Clock (CLK) input pin.
What is SPI (Serial Peripheral Interface)
SPI or Serial Peripheral Interface is a synchronous serial data transfer protocol used by microcontrollers for communicating with one or more peripheral devices. Two or more microcontrollers can communicate with one another with SPI also. Each SPI devices has three common lines and one device specific line as following:
- MISO (Master In Slave Out) – The Slave line for sending data to the master. In our case its DIO or Data Input Output.
- MOSI (Master Out Slave In) – The Master line for sending data to the peripherals. In our case its DO or Data Output.
- SCK (Serial Clock) – The clock pulses which synchronize data transmission generated by the master. In our case its CLK.
- SS (Slave Select) – the pin on each device that the master can use to enable and disable specific devices. In our case it is /CS or Chip Select Bar (active low).
When a device’s Slave Select pin is low, it communicates with the master. When it’s high, it ignores the master. This allows you to have multiple SPI devices sharing the same MISO, MOSI, and CLK lines.
Modes of operation
SPI has four modes of operation, combine polarity and phase according to this table:
Mode | Clock Polarity (CPOL) | Clock Phase (CPHA) | Output Edge | Data Capture |
SPI_MODE0 | 0 | 0 | Falling | Rising |
SPI_MODE1 | 0 | 1 | Rising | Falling |
SPI_MODE2 | 1 | 0 | Rising | Falling |
SPI_MODE3 | 1 | 1 | Falling | Rising |
As per datasheet, our winbond W25X80A EEPROM chip works with both SPI_MODE0 and SPI_MODE3 i.e. raising edge of the clock.
More details on Arduino SPI operations can be found here: https://www.arduino.cc/en/Reference/SPI
Logic level conversion (5V to 3.3V)
As we are using Arduino Uno R3, by default we have 5V logic along with one 3.3V output power line. We must convert Arduino’s 5V logic outputs to 3.3V otherwise the EEPROM chip will be fried in very short time. As this is just for sending binary logic signals i.e. 1 (true) or 0 (false), not for powering up a device – few milliampere of current will be absolutely fine. Hence, a simple voltage divider will do the job done. In a 0-5V logic system, below 2.5V means 0 and above 2.5V means 1. For the EEPROM’s output pins – they are already 3.3V i.e. much higher than 2.5V of threshold. So, there will be no problem detecting 3.3V as 1 (true) and 0V as 0 (false) in the Arduino. If you use a Arduino board that operate on 3.3V, just ignore all these precautions.
Schematic (pin connections)

Link to the schematic: https://easyeda.com/arp14/spi-based-eeprom-reader-writer
List of components needed
- Arduino Uno
- winbond W25X10A/W25X20A/W25X40A/W25X80A EEPROM
- 100 Ω resistor x3
- 220 Ω resistor x1
- 1k Ω resistor x1
- Jumper wires
- Breadboard
Pin map from EEPROM chip to Arduino
- Arduino 3.3V to EEPROM pin 8 (VCC)
- Arduino 3.3V to EEPROM pin 3
- Arduino 3.3V to EEPROM pin 7
- Arduino GND to EEPROM pin 4 (GND)
- Arduino pin 10 to EEPROM pin 1
- Arduino pin 11 to EEPROM pin 2
- Arduino pin 12 to EEPROM pin 5
- Arduino pin 13 to EEPROM pin 6
Note: Don’t forget to convert logic level 5V to 3.3V.
Various functions to read/write to/from EEPROM
Full source code can be found on my GitHub page:
https://github.com/Cyberster/Arduino-SPI-EEPROM-Reader-Writer
How to read a single byte data from EEPROM
Function prototype
byte read_eeprom(long EEPROM_address);
Usage example
byte data = read_eeprom(0x0000FF); // 0x000000 to 0x100000 Serial.println(data, HEX);
Output
How to read a block data from EEPROM
Function prototype
void read_block(long starting_address, int block_size, int block_count, boolean fastread);
Usage example
// read data, block_size columns (bytes) in a row, block_count rows read_block(0x000000, 16, 10, true);
Output
How to download the entire EEPROM
Steps to download the content of EEPROM into a file:
- First, make download_rom() function only active and flash the sketch into your arduino.
- Configure serial port and baud rate in download_rom.py according to your arduino sketch.
- Press reset button in your arduino.
- Run download_rom.py by entering ‘python2.7 download_rom.py‘ in your terminal/cmd window.
- Now press ‘d‘ and hit enter in the prompt in your terminal/cmd window and wait for finish.
Function prototype
void download_rom(boolean fastread);
Usage example
download_rom(true);
Output

How to erase a 4KB block from given address in EEPROM
Function prototype
void sector_erase(long address);
Usage example
// erase 4kB from given address sector_erase(0x000000);
Output
How to erase a 64KB block from given address in EEPROM
Function prototype
void block_erase(long address);
Usage example
// erase 64kB from given address block_erase(0x000000);
Output
How to erase the entire EEPROM at once
Function prototype
void chip_erase();
Usage example
// erase the entire EEPROM chip chip_erase();
Output
How to write data to EEPROM
Function prototype
void write_eeprom(long address, byte data[], int data_size);
Usage example
// write data to the EEPROM byte buffer[256]; for (int i = 0; i < 256; i++) { buffer[i] = i; // filling buffer with 0-255 } // buffer size must be 1 to 256 // EEPROM can only be written if empty i.e. filled with 1's // use appropriate erase function before writing anything write_eeprom(0x000000, buffer, 256);
Output
How to upload a ROM file to the EEPROM
Steps to upload a file from computer to the EEPROM:
- First, make upload_rom() function only active and flash the sketch into your arduino.
- Configure serial port and baud rate in upload_rom.py according to your arduino sketch.
- Run upload_rom.py by entering ‘python2.7 upload_rom.py‘ in your terminal/cmd window.
- Press reset button in your arduino.
- Enter the file path i.e. ‘/path/to/the/file.rom‘ and hit enter and wait for finish.
Function prototype
void upload_rom();
Usage example
// upload data from file to the EEPROM upload_rom();
Output
How to read manufacturer ID and device ID from EEPROM
Function prototype
void get_device_info(byte &manufacturer_id, byte &device_id);
Usage example
// read manufacturer id and device id byte manufacturer_id; byte device_id; get_device_info(manufacturer_id, device_id); Serial.println(manufacturer_id, HEX); Serial.println(device_id, HEX);
Output
According to the Manufacturer and Device Identification table in the winbond W25X80A datasheet, EF is “Winbond Serial Flash” and 13 is “W25X80A“.
Conclusion
Yes, we can flash a standard BIOS chip with Arduino. It was fun to play with bits, specially – seeing first byte fetched from the EEPROM was just amazing. From now, I am a worry-free person from a bad BIOS flash 🙂
Motivation
References
- https://www.arduino.cc/en/Tutorial/SPIEEPROM
- https://www.youtube.com/watch?v=fvOAbDMzoks
- https://www.youtube.com/watch?v=AuhFr88mjt0
- http://www.ohmslawcalculator.com/voltage-divider-calculator
- https://www.arduino.cc/en/Reference/SPI
- https://www.arduino.cc/reference/en/language/functions/communication/serial/
- https://www.corelis.com/education/tutorials/spi-tutorial/
- https://media.digikey.com/pdf/Data%20Sheets/Winbond%20PDFs/W25X10A,20A,40A,80A.pdf – winbond W25X80A datasheet.
- https://www.mathworks.com/help/supportpkg/arduino/examples/communicating-with-an-spi-based-eeprom-using-arduino-hardware.html
Have anything in your mind? Just comment below.
Hope you learn something!
Thank you.