W25Q128:一颗SPI接口的外部NOR Flash储存器
容量128M-bit=16MB
作用特点:
1.储存程序/大容量数据
2.断电不丢失
一,内部结构
W25Q128 内部划分如下:
-
16MB 总容量
-
分成 256 个 Block(每个 64KB)
-
每个 Block 里 16 个 Sector(每个 4KB)
-
Sector 里很多 Page(每个 256B)
结构如下:

Chip (16MB)
└── Block (64KB)
└── Sector (4KB)
└── Page (256B)
二,读写机制
1,擦除的最小单位:4KB Sector
2,数据只能从1➡0,不能从0➡1
所以写数据之前要擦除4kb区域(全部变成0XFF)
写Page(256字节)
写入数据的最小单位是Page(256B)
一次最多写256字节,超过会自动翻页
读取数据(不需要擦除)
读写字节很快,可以连续读多个字节;
常见指令:
-
0x03:普通读(低速)
-
0x0B:快速读(高速)
-
0x3B / 6B / EB:双/四线高速读(QSPI)
4.状态寄存器
用于判断:
-
Flash 是否忙(BUSY 位)
-
是否写使能
-
安全保护状态
读写状态寄存器的指令也非常常用。
SPI通信协议
SPI:一种高速,全双工,同步的串行通信协议,支持一主多从;
接线:一一对应;通过DATASHEET文件查看结构框图

SPI的结构框图

SPI的工作模式

全双工:可以同时发送和接受
半双工:同一时刻要么发送,要么接收;
单工:单向通信
| 类型 | 是否能同时收发? | 数据方向 | 例子 |
|---|---|---|---|
| 全双工 | ✔ 能同时 | 双向独立 | SPI、UART |
| 半双工 | ✘ 不能同时(轮流) | 双向,共用通道 | I²C、RS485 |
| 单工 | ✘ 永远单向 | 单向 | 红外遥控、广播 |
SPI四根线的作用
| 引脚 | 名称 | 含义 | 谁驱动? |
|---|---|---|---|
| SCK | Serial Clock | 时钟信号 | 主机(Master)输出 |
| MOSI | Master Out Slave In | 主 → 从 | 主机输出 |
| MISO | Master In Slave Out | 从 → 主 | 从机输出 |
| CS / NSS | Chip Select | 片选 | 主机输出 |
SPI由主机产生时钟,从机只在时钟节拍下接受或输出数据
SPI的数据流:
当 /CS 拉低(选中从机)之后:
-
在 SCK 的每个跳变沿,从机和主机都会交换 1 bit 数据
-
MOSI 上的数据先写给从机
-
MISO 上的数据从从机返回主机
-
一次时钟 = 数据同时双向飞一次
SPI = 每个时钟都同时读 + 写。
SPI的四种工作模式
| 模式 | CPOL | CPHA | 官方名字 |
|---|---|---|---|
| Mode 0 | 0 | 0 | 低电平空闲,上升沿采样 |
| Mode 1 | 0 | 1 | 低电平空闲,下降沿采样 |
| Mode 2 | 1 | 0 | 高电平空闲,下降沿采样 |
| Mode 3 | 1 | 1 | 高电平空闲,上升沿采样 |
最常见的:Mode 0(W25Q128 也是)
SPI怎么使用?
1,拉低片选CS
2,发送命令/地址/数据
3,接受数据
4,拉高片选CS
SPI使用注意事项
1,时序模式必须对其
2,片选CS必须手动控制
SPI是全双工的,一定会收到东西
4,速度不要太快:
-
最大频率 80MHz
-
写操作推荐 < 20MHz
5,上电前几个ms不要操作SPI,避免外设未初始化好
STM32F407ZGT6的SPI软件思路:1,用的是SPI的哪个接口?2,是主机还是从机?3,发送一次数据的大小是多少?4,SPI的工作模式?5,CS由软件控制还是硬件控制?6,时钟的配置?7,数据传输是MSB还是LSB?8,主关闭TI,从打开TI.9,crc校验打开了吗?
引脚模式为什么要用复用推挽输出
复用:把IO口的控制权交给SPI
推挽输出:强驱动、明确的高低电平、抗干扰
CS推挽输出,其余三个复用推挽输出
数组复习知识
如果定义一个数组长度20,但是我赋值abc,只有三个字节,那后面的20个字节会是什么数据呢?
- 第 3 字节:
'\0'(确定,strcpy自动加);
| 数组定义方式 | 未赋值部分(第 4~19 字节) | 是否推荐写入 Flash? |
|---|---|---|
| 未初始化的局部数组 | 随机垃圾数据 | 不推荐(数据混乱) |
| 全局数组(未手动初始化) | 全 0x00 | 可选(但 0x00 后续修改需擦除) |
| 手动用 memset 初始化为 0xFF | 全 0xFF | 强烈推荐(适配 Flash "先擦后写" 规则) |
是否留 1 字节存\0,只看你后续是否用「字符串函数(如strcpy、printf、strlen)操作数据」------ 用字符串函数才需要留