之前使用SPI方式下,测试RP2040读取W25Q64模块,结果失败。换用W25Q128芯片搭建电路依旧失败。
1、电路连接


2、代码如下:
from machine import Pin, SPI
import time
# === 配置 SPI 引脚 ===
# 使用 SPI0 外设,使用软件控制片选引脚(CS)
# 19 - TX MOSI
# 16 - RX MISO
# 18 - SCK
cs_pin = Pin(17, Pin.OUT)
cs_pin.value(1) # 初始状态设为高电平,即不选中设备
# 创建 SPI 实例
# 参数说明: id=1(使用SPI1), baudrate=5_000_000(时钟频率5MHz),
# polarity=0, phase=0 (SPI模式0), firstbit=MSB
spi = SPI(0, baudrate=5000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB)
# === W25Q64 常用指令 ===
CMD_WRITE_ENABLE = 0x06 # 写使能
CMD_WRITE_DISABLE = 0x04 # 写禁止
CMD_READ_STATUS_REG1 = 0x05 # 读状态寄存器1
CMD_READ_DATA = 0x03 # 读数据
CMD_PAGE_PROGRAM = 0x02 # 页编程(写数据)
CMD_SECTOR_ERASE = 0x20 # 扇区擦除(4KB)
CMD_READ_JEDEC_ID = 0x9F # 读取JEDEC ID
# === 辅助函数 ===
def cs_low():
cs_pin.value(0)
def cs_high():
cs_pin.value(1)
def spi_wait_ready():
"""等待芯片内部操作完成,通过读取状态寄存器1的BUSY位(bit 0)"""
cs_low()
spi.write(bytes([CMD_READ_STATUS_REG1]))
status = spi.read(1, 0xFF)[0]
while status & 0x01: # 判断最低位是否为1
status = spi.read(1, 0xFF)[0]
time.sleep_ms(1)
cs_high()
def spi_flash_write_enable():
"""发送写使能命令"""
cs_low()
spi.write(bytes([CMD_WRITE_ENABLE]))
cs_high()
# === 核心功能 ===
def read_jedec_id():
"""读取JEDEC ID (制造商和设备ID)"""
cs_low()
# 发送命令
spi.write(bytes([CMD_READ_JEDEC_ID]))
# 读取3个字节: Manufacturer ID, Memory Type, Capacity
data = spi.read(3, 0xFF)
cs_high()
return data
def read_data(address, length):
"""从指定地址读取数据"""
# 构建命令字节数组: 命令(0x03) + 3字节地址(MSB)
cmd = bytearray(4)
cmd[0] = CMD_READ_DATA
cmd[1] = (address >> 16) & 0xFF
cmd[2] = (address >> 8) & 0xFF
cmd[3] = address & 0xFF
cs_low()
spi.write(cmd)
data = spi.read(length, 0xFF)
cs_high()
return data
def write_page(address, data):
"""
页写入函数。W25Q64每页256字节,写入前地址必须对齐到页边界。
注意:写入前必须先擦除。
"""
if len(data) > 256:
raise ValueError("数据长度不能超过256字节")
# 使能写操作
spi_flash_write_enable()
# 发送页编程命令
cmd = bytearray(4)
cmd[0] = CMD_PAGE_PROGRAM
cmd[1] = (address >> 16) & 0xFF
cmd[2] = (address >> 8) & 0xFF
cmd[3] = address & 0xFF
cs_low()
spi.write(cmd)
spi.write(data)
cs_high()
# 等待写入完成
spi_wait_ready()
def sector_erase(address):
"""
扇区擦除 (4KB)。地址可以是该扇区范围内的任意地址。
"""
# 使能写操作
spi_flash_write_enable()
# 发送扇区擦除命令
cmd = bytearray(4)
cmd[0] = CMD_SECTOR_ERASE
cmd[1] = (address >> 16) & 0xFF
cmd[2] = (address >> 8) & 0xFF
cmd[3] = address & 0xFF
cs_low()
spi.write(cmd)
cs_high()
# 等待擦除完成 (扇区擦除需要较长时间,约150-400ms)
spi_wait_ready()
# === 主函数测试 ===
print("--- Test Start ---")
while True:
jedec = read_jedec_id()
if len(jedec) == 3:
print(f"JEDEC ID: 0x{jedec[0]:02X}, 0x{jedec[1]:02X}, 0x{jedec[2]:02X}")
# W25Q64 的典型ID是: Manufacturer=0xEF, Memory Type=0x40, Capacity=0x17
if jedec[0] == 0xEF and jedec[1] == 0x40 and jedec[2] == 0x17:
print("Chip ID 匹配 W25Q64.")
else:
print("Chip ID 错误! 检查连接......")
else:
print("读JEDEC ID.失败。")
# 如果读取失败,停止执行
raise SystemExit
time.sleep(0.01)运行结果:

读不出来 。然后使用逻辑分析仪检测四个信号(CS,SCK,MOSI,MISO)

可以看到,在CS信号给出后,没有看到SCK、MOSI、MISO信号有任何变化。似乎是SPI没有工作啊。
对比用软件模拟操作的检测:

明显不一样。是不是因为使用软件模拟彻底信号使用了GPIO17,与硬件SPI0 的预设引脚重复了?
将CS换到GPI20上,结果依旧读不出来。
忽然明白了。RP2040的SPI是可以使用几组引脚配置的。在没有致命的情况下,他也不知道使用那些引脚。所以是不是必须要预先指定呢,据此该SPI初始化代码为:
spi = SPI(0, baudrate=5_000_000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
果然,问题解决了。
我要赚赏金
