一、芯片概述与特性
基本参数
容量:128M-bit(16MB),分为256个块(Block),每块64KB;每块含16个扇区(Sector),每扇区4KB。
通信协议:支持标准SPI、Dual SPI、Quad SPI模式(最高时钟133MHz)。
寿命与存储:10万次擦写寿命,数据保存20年。
关键操作限制
擦除单位:最小擦除单位为扇区(4KB),写入前必须先擦除。
写入单位:单次最多写入256字节(一页)。
写使能要求:所有写操作(含擦除)前需发送0x06写使能命令。
封装

二、硬件连接(STM32F103与W25Q128)


关键点:
片选信号(/CS)需通过GPIO控制,其他SPI引脚复用为Alternate Function Push-Pull模式。
SPI模式必须配置为 Mode 0(CPOL=0, CPHA=0) 或 Mode 3(CPOL=1, CPHA=1) 。
三、SPI配置(标准库 vs HAL库)
1. 标准库配置(以SPI1为例)
void SPI1_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
// 配置SPI引脚(PA4)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; //SPI_NSS
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
// 配置SPI引脚(PA5/7)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置SPI引脚(PA6)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //SPI_MISO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置SPI参数
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // Mode 0
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 软件控制NSS
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 低速调试
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
}
2. HAL库配置
SPI_HandleTypeDef hspi1;
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
优化建议:
调试初期使用低速模式(如SPI_BaudRatePrescaler_256),稳定后提升至SPI_BaudRatePrescaler_4(最高18MHz)。
DMA传输可提升连续读写效率,减少CPU占用。
四、关键操作代码实现(HAL库实现)
1. SPI读写函数(HAL库)
uint8_t SPI_ReadWriteByte(uint8_t TxData) {
uint8_t RxData;
HAL_SPI_TransmitReceive(&hspi1, &TxData, &RxData, 1, 1000); // 阻塞模式
// 或使用DMA:HAL_SPI_TransmitReceive_DMA(...)
return RxData;
}
2. 片选信号
#define W25QXX_CS_L() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define W25QXX_CS_H() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
3. 读取状态寄存器
uint8_t W25QXX_ReadSR(void)
{
uint8_t byte=0;
W25QXX_CS_L(); // 片选拉低
W25Q128_ReadWriteByte(0x05); // 发送读取状态寄存器命令
byte=W25Q128_ReadWriteByte(0Xff); // 读取一个字节
W25QXX_CS_H(); // 片选拉高
return byte;
}
4. 线路忙检测
uint8_t W25QXX_Wait_Busy(void)
{
uint32_t tickstart = HAL_GetTick();
while( (W25QXX_ReadSR() & 0x01) == 1);
{
/* 超时 */
if((HAL_GetTick() - tickstart) > 1000)
{
return 0;
}
}
return 1;
}
5. 写使能
uint8_t W25QXX_Write_Enable(void)
{
W25QXX_CS_L(); // 片选拉低
SPI_ReadWriteByte(0x06); // 发送写使能命令
W25QXX_CS_H(); // 片选拉高
return W25QXX_Wait_Busy();
}
6. 扇区擦除与全片擦除
// 擦除指定扇区(4KB)
void W25QXX_Erase_Sector(uint32_t SectorAddr) {
W25QXX_Write_Enable();
W25QXX_CS_L();
SPI_ReadWriteByte(0x20); // 扇区擦除命令
SPI_ReadWriteByte((SectorAddr >> 16) & 0xFF);
SPI_ReadWriteByte((SectorAddr >> 8) & 0xFF);
SPI_ReadWriteByte(SectorAddr & 0xFF);
W25QXX_CS_H();
W25QXX_Wait_Busy(); // 等待约100ms
}
// 全片擦除(慎用!耗时约20秒)
void W25QXX_Erase_Chip(void) {
W25QXX_Write_Enable();
W25QXX_CS_L();
SPI_ReadWriteByte(0xC7); // 0x60也可用
W25QXX_CS_H();
W25QXX_Wait_Busy();
}
7. 读取芯片ID(验证通信)
uint16_t W25QXX_ReadID(void) {
uint16_t id = 0;
W25QXX_CS_L(); // 片选拉低
SPI_ReadWriteByte(0x90); // 发送读ID命令
SPI_ReadWriteByte(0x00); // 3个空字节
SPI_ReadWriteByte(0x00);
SPI_ReadWriteByte(0x00);
id |= SPI_ReadWriteByte(0xFF) << 8; // 高字节
id |= SPI_ReadWriteByte(0xFF); // 低字节
W25QXX_CS_H(); // 片选拉高
return id; // 正常值:0xEF17(W25Q128)
}
8. 唯一序列号应用
void W25QXX_ReadUniqueID(uint8_t UID[8]) {
W25QXX_CS_L();
SPI_ReadWriteByte(0x4B); // 读64位唯一ID命令
SPI_ReadWriteByte(0x00); // 4个空字节
SPI_ReadWriteByte(0x00);
SPI_ReadWriteByte(0x00);
SPI_ReadWriteByte(0x00);
for(uint8_t i=0; i<8; i++) UID = SPI_ReadWriteByte(0xFF);
W25QXX_CS_H();
} // 适用于设备加密
9. 数据读取(任意地址)
void W25QXX_Read(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t Size) {
W25QXX_CS_L();
SPI_ReadWriteByte(0x03); // 读命令
SPI_ReadWriteByte((ReadAddr >> 16) & 0xFF); // 24位地址(高位在前)
SPI_ReadWriteByte((ReadAddr >> 8) & 0xFF);
SPI_ReadWriteByte(ReadAddr & 0xFF);
for(uint16_t i=0; i<Size; i++)
pBuffer = SPI_ReadWriteByte(0xFF); // 循环读取
W25QXX_CS_H();
}
10. 数据写入(需先擦除扇区)
uint8_t W25QXX_Write_Page(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t Size) {
W25QXX_Write_Enable(); // 发送0x06写使能命令
W25QXX_CS_L();
SPI_ReadWriteByte(0x02); // 页编程命令
SPI_ReadWriteByte((WriteAddr >> 16) & 0xFF);
SPI_ReadWriteByte((WriteAddr >> 8) & 0xFF);
SPI_ReadWriteByte(WriteAddr & 0xFF);
for(uint16_t i=0; i<Size; i++)
SPI_ReadWriteByte(pBuffer); // 逐字节写入
W25QXX_CS_H();
return W25QXX_Wait_Busy();
}
关键限制:
擦除单位:扇区(4KB),写前必须擦除(使用W25QXX_Erase_Sector())。
写入单位:单次最多256字节(一页),跨页需拆分。
五、使用示例
int main(void) {
HAL_Init();
SystemClock_Config();
SPI1_Init();
// 验证通信
uint16_t flash_id = W25QXX_ReadID();
if(flash_id != 0xEF17) {
// 错误处理
}
// 数据读写示例
uint8_t write_buf[256] = {0x01, 0x02, 0x03...};
uint8_t read_buf[256];
W25QXX_EraseSector(0); // 擦除扇区0
W25QXX_WritePage(write_buf, 0x0000, 256); // 写入第一页
W25QXX_ReadData(read_buf, 0x0000, 256); // 读取数据
while(1) {
// 主循环
}
}
使用以下代码验证:
printf("\r\n SPI-W25Qxx Example \r\n\r\n");
uint8_t ID[4]; //设备ID缓存数组
/*-Step1- 验证设备ID ************************************************Step1*/
BSP_W25Qx_Read_ID(ID);
//第一位厂商ID固定0xEF,第二位设备ID根据容量不同,具体为:W25Q16为0x14、32为0x15、40为0x12、64为0x16、80为0x13、128为0x17
printf(" W25Qxx ID is : ");
for(int i=0;i<2;i++)
{
printf("0x%02X ",ID);
}
printf("\r\n");

六、调试技巧与常见问题
1. 典型问题排查

2. 性能优化
Quad SPI模式:若MCU支持,将DO/DI改为4线通信(速度提升4倍)。
缓存管理:避免频繁擦除,使用环形缓冲区或磨损均衡算法(延长Flash寿命)。
DMA传输:连续读写时采用DMA(如HAL库的HAL_SPI_TransmitReceive_DMA)。
七、高级应用场景
通过移植FAT32文件系统挂载w25q128:
实现底层扇区读写接口(disk_read/disk_write)。
使用预分配策略减少擦写次数(擦一次写多页)。
实测速度:读271KB/s,写68KB/s(STM32F103 + SPI DMA)。
唯一ID应用:读取64位唯一序列号(命令0x4B)用于设备加密。
————————————————
版权声明:本文为CSDN博主「谱写秋天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42222415/article/details/150205862
|