Skip to the content.

STM32G030 SPI Flash(25Q64)芯片识别实验任务书

任务名称: 基于 STM32G030 的 SPI Flash 硬件连接与 JEDEC ID 读取实验

实验平台: STM32G030F6P6、25Q64 SPI Flash 模块

使用外设: USART2、SPI1、GPIO、25Q64

开发工具: STM32CubeMX + Keil MDK(或现有 IDE)

工程名称: G030_SPI_FLASH_Test

任务要求: 独立创建工程,完成 SPI Flash 硬件接线、UART 调试输出、SPI 外设初始化及芯片 ID 读取。分阶段调试,不直接复制旧工程代码。上一阶段验收通过后,才进入下一阶段。

适用条件:


一、实验目标

本次实验需要完成以下功能:

  1. 了解 SPI Flash 的基本引脚和 SPI 通信方式。
  2. 完成 STM32G030 与 25Q64 的硬件接线。
  3. 使用 STM32CubeMX 创建新的 STM32G030 工程。
  4. 初始化 USART,实现 printf() 串口调试输出。
  5. 配置 SPI1 为主机模式,建立 SPI 通信接口。
  6. 使用 SPI 指令 0x9F 读取 25Q64 的 JEDEC ID。
  7. 通过串口打印芯片 ID,并与器件数据手册进行比较。
  8. 完成重复读取和异常连接测试。

最终目标:

STM32G030 通过 SPI1 向 25Q64 发送 0x9F 指令,正确读取 3 字节 JEDEC ID,并通过串口显示识别结果。

当 SPI 通信正常、HAL 操作成功且返回的 ID 与实际芯片数据手册一致时,输出:

FLASH ID CHECK OK
SPI FLASH TEST PASS

如果读取失败、数据异常或 ID 与实际芯片型号不一致,应输出 SPI FLASH TEST FAIL,不能报告测试成功。


二、第一阶段:SPI Flash 硬件连接

2.1 实验目标

掌握 SPI Flash 的基本引脚定义,完成 STM32G030 与 25Q64 的连接,并验证供电、接线和信号方向。

2.2 SPI Flash 引脚介绍

以常见的 8 引脚 25Q64 SPI Flash 为例:

引脚编号 引脚名称 功能说明
1 CS# 片选,低电平有效
2 DO / IO1 SPI 数据输出,连接 MCU 的 MISO
3 WP# / IO2 写保护引脚,普通 SPI 模式下保持高电平
4 GND 电源地
5 DI / IO0 SPI 数据输入,连接 MCU 的 MOSI
6 CLK SPI 时钟输入
7 HOLD# / RESET# / IO3 具体功能取决于芯片型号,普通 SPI 模式下保持高电平
8 VCC 电源输入

注意:不同厂家和不同封装的引脚名称可能略有差异,最终以实际芯片的数据手册为准。

2.3 STM32G030 与 Flash 接线

本任务推荐使用以下 SPI1 引脚组合:

25Q64 引脚 STM32G030 引脚 MCU 物理脚号 说明
CS# PA4 11 GPIO 输出,软件控制片选
CLK PA5 12 SPI1_SCK
DO / IO1 PA6 13 SPI1_MISO
DI / IO0 PA7 14 SPI1_MOSI
WP# / IO2 3.3 V — 保持高电平
HOLD# / IO3 3.3 V — 保持高电平
VCC 3.3 V — 电源
GND GND — 共地

接线注意事项:

  1. 必须确认 Flash 为 3.3 V 供电型号,不得直接向不兼容的芯片施加 5 V。
  2. STM32 与 Flash 必须共地。
  3. MOSI、MISO 的连接方向不能接反。
  4. CS# 为低电平有效,空闲时需要保持高电平。
  5. 对于裸芯片,WP# 和 HOLD# 建议分别通过约 10 kΩ 电阻上拉至 3.3 V。对于模块,先确认是否已经具有上拉。
  6. Flash 电源附近应有 100 nF 去耦电容。如果模块已经配置,可直接使用。
  7. 接线前断电,连接完成并检查无误后再上电。

2.4 硬件检查

接线完成之后,使用万用表完成以下检查:

  1. Flash VCC 与 GND 之间是否存在短路。
  2. 上电后 Flash VCC 是否约为 3.3 V。
  3. WP#、HOLD# 是否保持高电平。
  4. CS#、SCK、MISO、MOSI 是否连接至正确的 MCU 引脚。
  5. 所有杜邦线、焊点是否接触可靠。

本阶段验收标准

本阶段通过后,才能创建并运行 SPI 测试工程。


三、第二阶段:创建新的 STM32 工程

3.1 创建工程

打开 STM32CubeMX,完成以下操作:

  1. 新建 MCU 工程。
  2. 搜索并选择 STM32G030F6P6。
  3. 将工程命名为 G030_SPI_FLASH_Test。
  4. 配置调试接口为 Serial Wire。
  5. 使用内部 HSI 时钟作为初始时钟源。
  6. 配置 USART2 和 SPI1。
  7. 配置 PA4 为普通 GPIO 输出。
  8. 选择开发环境,生成工程代码。

如果当前开发板上的 MCU 不是 STM32G030F6P6,应先确认实物型号,再选择对应器件。

3.2 UART 配置

为了方便观察 SPI 通信结果,本实验使用 UART 输出调试信息。

推荐使用 USART2:

参数 配置
外设 USART2
TX PA2
RX PA3
Mode Asynchronous
Baud Rate 115200
Word Length 8 Bits
Parity None
Stop Bits 1
Hardware Flow Control None
中断 / DMA 暂不启用

如果现有开发板已经将其他 UART 连接到 USB 转串口模块,可以沿用实际接线,但必须同步修改 CubeMX 配置及代码中的 UART 句柄。

3.3 SPI1 配置

在 CubeMX 中启用 SPI1。

建议使用以下参数:

参数 配置 说明
Mode Full-Duplex Master STM32 作为 SPI 主机
Frame Format Motorola 标准 SPI 格式
Data Size 8 Bits 每次发送 8 位
First Bit MSB First 高位先发送
Clock Polarity (CPOL) Low 时钟空闲时为低电平
Clock Phase (CPHA) 1 Edge 在第一个有效时钟边沿采样
NSS Software 软件控制片选
Baud Rate Prescaler 16 初始采用较低的 SPI 速度
CRC Calculation Disabled 暂不使用 CRC
中断 / DMA Disabled 使用阻塞式 HAL 函数

本次实验采用 SPI Mode 0(CPOL = 0,CPHA = 0)。

如果 SPI 外设输入时钟为 16 MHz,分频系数为 16 时,SPI 时钟为 1 MHz。

实际 SPI 时钟应根据 CubeMX 时钟树中对应的外设时钟计算。

3.4 GPIO 片选配置

本实验不使用 SPI 硬件 NSS 控制,而是通过普通 GPIO 控制 Flash 片选。

设置 PA4:

参数 配置
GPIO Mode Output Push Pull
GPIO Pull-up/Pull-down No Pull
GPIO Speed Low
GPIO Output Level High
User Label FLASH_CS

为什么要将 CS# 的初始状态设置为高电平?

因为 Flash 在未进行通信时必须处于未选中状态。只有准备发送 SPI 指令时,STM32 才主动拉低 CS#。

在 STM32 初始化完成后,Flash 应保持未选中状态。

3.5 生成工程

完成配置后:

  1. 检查 CubeMX 中是否存在引脚冲突。
  2. 检查 SPI1、USART2 是否成功启用。
  3. 检查系统时钟和 SPI 时钟频率。
  4. 保存 .ioc 文件。
  5. 生成工程代码。
  6. 使用 Keil 或现有 IDE 编译并下载。

本阶段验收标准


四、第三阶段:UART printf 功能测试

4.1 实验目标

在新的 SPI Flash 工程中重新建立 UART printf() 调试输出,确保后续能够观察芯片读取结果。

4.2 实验要求

参考已经完成的 UART 和 I²C EEPROM 实验,实现 printf() 重定向。

需要完成:

  1. 在 main.c 中添加 #include <stdio.h>。
  2. 根据当前编译器环境配置 printf 重定向。
  3. 确认 UART 句柄与 CubeMX 生成的外设一致。
  4. 在串口初始化完成后打印启动信息。
  5. 使用串口助手观察输出结果。

例如,在主循环中加入:

printf("STM32G030 SPI FLASH TEST\r\n");
HAL_Delay(1000);

如果使用 USART2,UART 句柄应为:

&huart2

在适用的 Keil fputc 重定向配置下,可以使用:

int fputc(int ch, FILE *f)
{
    uint8_t data = (uint8_t)ch;

    HAL_UART_Transmit(&huart2, &data, 1, 100);

    return ch;
}

不同编译器及 C 库的重定向方式可能不同。使用 GCC 时,应根据工程配置选择 _write() 等适用方法。

4.3 预期输出

STM32G030 SPI FLASH TEST
STM32G030 SPI FLASH TEST
STM32G030 SPI FLASH TEST

每隔约 1 秒输出一次。

本阶段验收标准

本阶段通过后,才能开始 SPI Flash 识别。


五、第四阶段:SPI Flash JEDEC ID 读取

5.1 实验目标

通过 STM32G030 的硬件 SPI1 向 25Q64 发送 JEDEC ID 读取指令,并接收 Flash 返回的 3 字节身份信息。

5.2 SPI 基础知识

SPI 是一种同步串行通信协议。

本实验需要使用四根主要信号线:

信号 作用
SCK 主机提供通信时钟
MOSI 主机向 Flash 发送指令或数据
MISO Flash 向主机返回数据
CS# 主机选择需要通信的设备

与之前学习的 I²C 不同,SPI Flash 没有 I²C 那种设备地址和 ACK 应答机制。

STM32 需要通过 CS# 选择 Flash,然后发送命令,由 Flash 返回对应的数据。

因此,不能通过 HAL_I2C_IsDeviceReady() 这样的方式判断 Flash 是否存在。

本次使用 JEDEC ID 读取指令 0x9F 实现设备识别。

5.3 JEDEC ID 指令介绍

JEDEC ID 指令为:

0x9F

主机发送该指令后,Flash 会返回三个字节:

字节 内容 说明
Byte 1 Manufacturer ID 制造商标识
Byte 2 Memory Type 存储器类型
Byte 3 Capacity ID 容量标识

例如,对于部分 Winbond W25Q64:

EF 40 17

含义:

64 Mbit 相当于 8 MiB 存储容量。

注意:不同版本的 Winbond W25Q64,也可能返回 EF 70 17;其他厂家生产的 25Q64 兼容芯片也可能具有不同的制造商及类型 ID。

必须根据实际芯片的数据手册确定正确的 ID,不能仅凭 EF 40 17 判断所有 25Q64。

5.4 SPI 通信流程

JEDEC ID 读取需要完成以下操作:

  1. STM32 将 CS# 拉低,选中 Flash。
  2. STM32 通过 MOSI 发送指令 0x9F。
  3. STM32 继续输出 SPI 时钟。
  4. Flash 通过 MISO 依次返回 3 字节 JEDEC ID。
  5. STM32 接收并保存返回值。
  6. STM32 将 CS# 拉高,结束通信。
  7. 使用 printf() 输出读取结果。

可以将本次通信理解为:

CS#    ______                         ______
             |_______________________|

MOSI         [0x9F] [0xFF] [0xFF] [0xFF]

MISO         [无效] [厂商] [类型] [容量]

SCK          每字节 8 个时钟,共 32 个时钟

在全双工 SPI 模式下,主机发送数据的同时也在接收数据。

因此,在发送 0x9F 时,接收到的第一个字节通常没有实际意义。

后面再发送三个占位字节,主要目的是继续产生时钟,让 Flash 返回三个 ID 字节。

0x9F 指令本身不需要发送存储地址,也不需要额外插入 Dummy Clock 周期。

5.5 学习 HAL SPI 函数

本次主要学习:

HAL_SPI_TransmitReceive();

函数原型:

HAL_StatusTypeDef HAL_SPI_TransmitReceive(
    SPI_HandleTypeDef *hspi,
    const uint8_t *pTxData,
    uint8_t *pRxData,
    uint16_t Size,
    uint32_t Timeout
);

参数说明:

参数 含义 本实验要求
hspi SPI 外设句柄 &hspi1
pTxData 待发送数据缓冲区 指向发送数组
pRxData 接收数据缓冲区 指向接收数组
Size 传输的数据帧数量 4 个 8 位数据帧
Timeout 最大等待时间 例如 100 ms
返回值 HAL 操作结果 检查是否为 HAL_OK

需要理解:

5.6 编程任务

首先定义必要变量。

例如:

uint8_t tx_buffer[4] = {
    0x9F, 0xFF, 0xFF, 0xFF
};

uint8_t rx_buffer[4] = {0};

HAL_StatusTypeDef status;

接下来独立编写 SPI 通信程序,按顺序完成以下任务:

  1. 确认 UART 和 SPI1 已经完成初始化。
  2. 使用 HAL_GPIO_WritePin() 将 CS# 拉低。
  3. 调用 HAL_SPI_TransmitReceive(),发送 4 字节并接收 4 字节。
  4. 使用 HAL_GPIO_WritePin() 将 CS# 拉高。
  5. 判断 HAL 函数的返回状态。
  6. 如果通信操作成功,提取有效的 3 字节 JEDEC ID。
  7. 使用 printf() 输出十六进制 ID。
  8. 根据实际芯片型号比较返回 ID。
  9. 正确时输出 FLASH ID CHECK OK,否则输出失败信息。

必须注意:

5.7 预期输出

假设实际芯片为返回 EF 40 17 的 Winbond W25Q64。

成功时:

STM32G030 BOOT OK
UART PRINTF OK
SPI1 INIT OK

FLASH ID READ START
JEDEC ID = EF 40 17
MANUFACTURER ID = 0xEF
MEMORY TYPE     = 0x40
CAPACITY ID     = 0x17

FLASH ID CHECK OK

如果实际 Flash 的 JEDEC ID 与示例不同,应按实物数据手册调整预期值及打印结果。

读取操作失败时:

FLASH ID READ START
FLASH SPI TRANSFER FAIL

SPI 操作完成,但返回 ID 不正确时:

FLASH ID READ START
JEDEC ID = FF FF FF
FLASH ID CHECK FAIL

上面的 FF FF FF 只是异常数据示例。实际接线故障也可能导致 00 00 00 或其他不稳定的数据。

本阶段验收标准


六、第五阶段:读取结果验证及附加测试

6.1 实验目标

确认 Flash ID 读取具有稳定性和可重复性,排除偶然接收到数据或程序错误判断的情况。

6.2 验证要求

当第四阶段能够成功获取 JEDEC ID 后,进一步完成以下测试。

测试 A:重复读取

  1. 将 Flash ID 读取操作放入周期性执行流程中。
  2. 每隔约 1 秒读取一次 JEDEC ID。
  3. 连续读取至少 10 次。
  4. 比较每次读取的结果是否一致。

预期输出:

FLASH ID = EF 40 17
FLASH ID = EF 40 17
FLASH ID = EF 40 17
FLASH ID = EF 40 17
...

要求:

测试 B:修改 SPI 通信速度

在 CubeMX 中修改 SPI1 的 Baud Rate Prescaler。

例如:

Prescaler = 16

修改为:

Prescaler = 32

重新生成代码并下载程序。

再次读取 Flash ID。

要求:

测试 C:断开通信线路

该测试用于确认程序能够检测 Flash 通信异常。

操作步骤:

  1. 关闭 STM32 和 Flash 的电源。
  2. 断开 Flash 的 MISO 信号线。
  3. 保持其他线路正确连接。
  4. 重新上电并运行程序。
  5. 观察串口输出。

由于 MISO 无法正常返回 Flash 数据,读取结果通常会异常或与数据手册不一致。

程序应能报告:

FLASH ID CHECK FAIL
SPI FLASH TEST FAIL

而不能继续报告 SPI FLASH TEST PASS。

测试完成后,断电恢复 MISO 接线,再重新验证设备是否能够正常识别。

注意: 不应在设备通电时随意插拔杜邦线。本测试重点是验证 ID 数据检查逻辑,而不是依赖 HAL SPI 返回错误。

6.3 最终判断逻辑

PASS 条件:

  1. UART 调试输出正常。
  2. SPI1 已正确初始化。
  3. HAL SPI 传输返回 HAL_OK。
  4. 已成功接收 3 字节 JEDEC ID。
  5. JEDEC ID 与实际器件数据手册一致。
  6. 连续读取 10 次,结果全部正确。
  7. 修改 SPI 分频系数后,仍然可以正确识别。

只有全部满足,才能输出最终 PASS。

6.4 最终预期输出

以下以返回 EF 40 17 的器件为例:

================================
STM32G030 SPI FLASH TEST
================================
UART PRINTF OK
SPI1 INIT OK

FLASH ID READ START
MANUFACTURER ID = 0xEF
MEMORY TYPE     = 0x40
CAPACITY ID     = 0x17

JEDEC ID = EF 40 17
FLASH ID CHECK OK

REPEAT TEST START
REPEAT TEST COUNT = 10
REPEAT TEST OK

SPI FLASH TEST PASS
================================

实际程序可以调整信息格式,但必须包含 ID 原始数据、判断结果以及最终 PASS / FAIL。


七、常见问题与排查

现象 优先排查
UART 无输出 UART 引脚、波特率、共地、printf 重定向
UART 输出乱码 波特率、时钟配置、串口数据格式
SPI 无时钟输出 SPI1 是否初始化、引脚复用、SPI 时钟配置
CS# 无法拉低 PA4 是否配置为 GPIO 输出、代码是否控制正确引脚
读取结果全为 FF MISO 是否断开、Flash 是否供电、CS# 是否有效、输入是否浮空
读取结果全为 00 MISO 信号、电源、接线短路、SPI 工作模式
ID 数值不正确 SPI Mode、MISO/MOSI 接线、实际芯片型号
ID 偶尔正确、偶尔错误 杜邦线接触、SPI 时钟过高、供电稳定性
HAL 返回 OK,但 ID 错误 HAL 不会自动验证从设备是否存在,应检查实际接收数据
更改 SPI 速度后无法识别 分频系数、SPI 实际时钟、接线质量、通信时序
复位后偶尔识别失败 CS# 初始电平、Flash 供电启动时间、初始化执行顺序

建议排查顺序:

  1. 先确认 UART 输出正常。
  2. 再测量 Flash 的 VCC 和 GND。
  3. 检查 CS#、WP#、HOLD# 电平。
  4. 检查 SCK、MOSI、MISO 的实际连接。
  5. 核对 CubeMX SPI 模式及引脚复用。
  6. 降低 SPI 通信速度后再次测试。
  7. 如果仍无法识别,使用示波器或逻辑分析仪观察 CS#、SCK、MOSI、MISO 波形。

八、实验完成标准

只有同时满足以下要求,才认为本次任务完成:


九、需要提交的实验成果

  1. 完整工程: 包含 .ioc 配置文件、全部必要源码和工程配置。
  2. 硬件接线照片: 能清楚看到 STM32G030、25Q64 模块及 SPI 信号接线。
  3. UART 测试截图: 证明 printf() 功能正常。
  4. CubeMX 配置截图: 展示 SPI1 配置参数及引脚分配。
  5. Flash ID 读取截图: 展示 3 字节 JEDEC ID 和芯片识别结果。
  6. 附加测试结果: 包含连续读取、SPI 时钟调整及异常连接测试。
  7. 实验总结: 说明实际芯片型号、读取的 JEDEC ID、使用的 SPI 频率,以及遇到的问题和解决方法。

十、思考题

实验结束后,尝试独立回答以下问题:

  1. SPI 与之前学习的 I²C 有什么区别?为什么 SPI 不需要 I²C 那样的设备地址?
  2. SPI 的 MOSI、MISO、SCK 和 CS# 分别有什么作用?
  3. 什么是 SPI 的 CPOL 和 CPHA?SPI Mode 0 与 Mode 3 有什么区别?
  4. 为什么读取 SPI Flash 的 JEDEC ID 需要发送 0x9F 指令?
  5. 为什么发送 4 字节数据,却只获得 3 字节有效 ID?
  6. HAL_SPI_TransmitReceive() 中的 pTxData、pRxData 和 Size 分别代表什么?
  7. 为什么 SPI 全双工模式下,即使只想从 Flash 接收数据,也需要输出时钟?
  8. 为什么 SPI Flash 的 CS# 必须在整个指令执行过程中保持低电平?
  9. 为什么 SPI 传输函数返回 HAL_OK,仍然不能证明 Flash 通信成功?
  10. 为什么同样是 25Q64,不同厂家或版本的 JEDEC ID 可能不同?
  11. 如果读取结果始终为 FF FF FF,应该按照什么顺序排查?
  12. 如果使用示波器观察 SPI 通信,应该怎样通过 CS#、SCK、MOSI 和 MISO 判断通信是否正常?

本次实验重点:独立完成 SPI Flash 硬件接线、STM32CubeMX SPI 初始化、片选控制、SPI 全双工通信、JEDEC ID 读取、串口调试及设备识别结果验证。


参考资料

  1. STM32G030x6/x8 官方数据手册(STMicroelectronics)
  2. W25Q64JV 数据手册及 JEDEC ID 说明
  3. SPI Flash JEDEC ID 指令说明(Bus Pirate)
  4. STM32 学习任务书模板

下一阶段预告: 本次完成后,可以继续学习 SPI Flash 的状态寄存器读取、指定地址数据读取,以及扇区擦除、页编程与数据读回验证。