In this project, we will interface a Winbond W25Q80DV memory IC with a Raspberry Pi 5 using the SPI (Serial Peripheral Interface) protocol. 💻 We will use Python to write and read data to and from the memory IC. 🐍 This project is ideal for those looking to expand their knowledge of SPI communication and memory interfacing with microcontrollers. 🧠💡
Use Case:Imagine you're building a data logging system for your environmental monitoring project. You need to store readings from various sensors (temperature, humidity, pressure, etc.) at regular intervals. While the Raspberry Pi's internal memory is sufficient for immediate processing, it's not ideal for long-term data storage. This is where the W25Q32 flash memory chip comes in. You can use it as a dedicated data repository, allowing your Raspberry Pi to collect and store vast amounts of data over extended periods. 🌡️📊
Get PCBs for Your Projects ManufacturedYou must check out PCBWAY for ordering PCBs online for cheap!
You get 10 good-quality PCBs manufactured and shipped to your doorstep for cheap. You will also get a discount on shipping on your first order. Upload your Gerber files onto PCBWAY to get them manufactured with good quality and quick turnaround time. PCBWay now could provide a complete product solution, from design to enclosure production. Check out their online Gerber viewer function. With reward points, you can get free stuff from their gift shop. Also, check out this useful blog on PCBWay Plugin for KiCad from here. Using this plugin, you can directly order PCBs in just one click after completing your design in KiCad.
Components Required- Raspberry Pi 5
- Winbond W25Q32 SPI Flash Memory IC
- Breadboard and jumper wires
- Python 3: The programming language used to control the SPI communication and data manipulation.
- spidev Library: A Python library specifically designed for interacting with SPI devices.
1.Enable SPI Interface:
- Open the terminal on your Raspberry Pi.
- Run
sudo raspi-config
. - Navigate to
Interfacing Options
>SPI
and enable it. - Reboot the Raspberry Pi.
2. Connect the W25Q32: Connect the W25Q32 to the Raspberry Pi's SPI pins. Refer to the Raspberry Pi 5 pinout diagram and the W25Q32 datasheet for the correct pin assignments.
- MOSI (Master Out Slave In): Transmits data from the Raspberry Pi to the W25Q32.
- MISO (Master In Slave Out): Receives data from the W25Q32 to the Raspberry Pi.
- SCK (Serial Clock): Provides the clock signal for synchronization.
- CS (Chip Select): Selects the W25Q32 on the SPI bus.
- GND (Ground): Provides the common ground connection.
- VCC (Power): Supplies power to the W25Q32 (typically 3.3V).
3. Install the spidev Library: Use the following command to install the spidev library on your Raspberry Pi:
sudo apt-get update
sudo apt-get install python3-spidev
Reading Device ID:All the Winbond devices have a dedicated manufacturer ID inside the chip. First, we need to test our connection. Let's read the Device ID and we can verify the connection.
We need to send the 90h and get EFh from the device.
Code 1: Python Code for reading device ID:import spidev
# Initialize SPI
spi = spidev.SpiDev()
spi.open(0, 0) # Open SPI bus 0, device (CS) 0
spi.max_speed_hz = 100000 # Set SPI speed
# Function to read JEDEC ID
def read_jedec_id():
# JEDEC ID command (0x9F)
jedec_id_cmd = [0x9F]
# Send command and read 3 bytes of response
response = spi.xfer2(jedec_id_cmd + [0x00, 0x00, 0x00])
return response[1:] # Ignore the first byte (command echo)
# Example usage
jedec_id = read_jedec_id()
manufacturer_id = jedec_id[0]
memory_type = jedec_id[1]
capacity = jedec_id[2]
print(f"JEDEC ID: {jedec_id}")
print(f"Manufacturer ID: {manufacturer_id:02X} (Decimal: {manufacturer_id})")
print(f"Memory Type: {memory_type:02X} (Decimal: {memory_type})")
print(f"Capacity: {capacity:02X} (Decimal: {capacity})")\
Code Response:Here is the output of reading the JEDEC ID from the W28Q32.
1.SPI Initialization:
- The
spidev
library is used to initialize the SPI interface. - The
spi.open(0, 0)
function opens the SPI bus 0 and device 0 (CS0). - The
spi.max_speed_hz
sets the SPI clock speed.
2.Reading JEDEC ID:
- The
read_jedec_id
function sends the JEDEC ID command (0x9F) to the memory IC. - It reads back three bytes of response, which include the Manufacturer ID, Memory Type, and Capacity.
- The response is returned, excluding the first byte (command echo).
3.Example Usage:
- The script prints the JEDEC ID, Manufacturer ID, Memory Type, and Capacity in both hexadecimal and decimal formats.
import spidev
import time
# Initialize SPI
spi = spidev.SpiDev()
spi.open(0, 0) # Open SPI bus 0, device (CS) 0
spi.max_speed_hz = 8000000 # Set SPI speed to 500kHz
spi.mode = 0b00 # SPI mode 0
# Define some commands for the W25Q32
WRITE_ENABLE = 0x06
WRITE_DISABLE = 0x04
READ_DATA = 0x03
WRITE_DATA = 0x02
READ_STATUS_REG1 = 0x05
SECTOR_ERASE = 0x20
DUMMY_BYTE = 0xA5
def write_enable():
spi.xfer2([WRITE_ENABLE])
def write_disable():
spi.xfer2([WRITE_DISABLE])
def read_status():
status = spi.xfer2([READ_STATUS_REG1, DUMMY_BYTE])
return status[1]
def erase_sector(address):
write_enable()
addr_bytes = [(address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF]
spi.xfer2([SECTOR_ERASE] + addr_bytes)
write_disable()
time.sleep(0.5) # Wait for the erase operation to complete
def write_data(address, data):
write_enable()
addr_bytes = [(address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF]
spi.xfer2([WRITE_DATA] + addr_bytes + data)
write_disable()
def read_data(address, length):
addr_bytes = [(address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF]
result = spi.xfer2([READ_DATA] + addr_bytes + [DUMMY_BYTE] * length)
return result[4:] # Skip the first 4 bytes (command and address)
# Debugging function to check SPI communication
def debug_spi():
status = read_status()
print(f"Status Register: {status:02X}")
if status & 0x01:
print("Device is busy.")
else:
print("Device is ready.")
# Example usage
address = 0x00000C # Address 12
data_to_write = [100] # Data to write (value 10)
# Debug SPI communication
debug_spi()
# Erase sector before writing
erase_sector(address)
# Write data to the flash memory
write_data(address, data_to_write)
time.sleep(0.1) # Wait for the write operation to complete
# Read data back from the flash memory
read_back_data = read_data(address, 1)
print(f"Read back data: {read_back_data[0]}")
# Verify the read data
if read_back_data[0] == data_to_write[0]:
print("Data verification successful: written and read correctly.")
else:
print("Data verification failed: Mismatch in written and read data.")
# Close SPI connection
spi.close()
Code Response:Here is the output of reading the read and write from the W28Q32.
1.SPI Initialization:
- The
spidev
library is used to initialize the SPI interface. - The
spi.open(0, 0)
function opens the SPI bus 0 and device 0 (CS0). - The
spi.max_speed_hz
sets the SPI clock speed.
2.Write Enable:
- The
write_enable
function sends the write enable command (0x06) to the memory IC.
3.Writing Data:
- The
write_data
function sends the write command (0x02) followed by the 24-bit address and the data to be written.
4.Reading Data:
- The
read_data
function sends the read command (0x03) followed by the 24-bit address and reads back the specified length of data.
Data Logging:
- Store sensor data over time for later analysis. For example, log temperature and humidity data from a DHT22 sensor.
Firmware Storage:
- Store firmware updates for IoT devices. The memory IC can hold the new firmware version, which the device can read and update itself.
Configuration Storage:
- Save configuration settings for devices that need to retain settings between power cycles, such as network credentials or user preferences.
Bootloader Storage:
- Use the memory IC to store a bootloader that can load the main application code from another memory location or over the network.
This project successfully demonstrated the seamless integration of a W25Q32 flash memory chip with a Raspberry Pi 5 using the SPI protocol and Python programming. 💻 By leveraging the SPI interface and the spidev library, we were able to establish reliable communication between the Raspberry Pi and the memory chip, enabling efficient data storage and retrieval. 💾
The project showcased various functionalities, including reading the JEDEC ID for chip identification, 🕵️♀️ reading default values from the entire memory, 🧠 writing and reading data to specific addresses, 📍 and writing and reading data to the complete memory. 📚 These functionalities provide a solid foundation for building data-intensive applications on the Raspberry Pi. 📊
This project serves as a valuable example of how readily available technology can be used to expand the capabilities of the Raspberry Pi. 💡 The ability to interface with external memory chips opens up possibilities for data logging, 📈 data acquisition, 📡 and other applications that require persistent storage beyond the Raspberry Pi's internal memory.
With its simplicity, versatility, and potential for further development, this project provides a great starting point for anyone interested in exploring the world of embedded systems and data storage solutions. 🚀
Comments