news 2026/7/16 12:44:20

W25Q128 SPI闪存驱动实战:从零构建嵌入式存储系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
W25Q128 SPI闪存驱动实战:从零构建嵌入式存储系统

1. W25Q128闪存芯片基础认知

第一次接触W25Q128是在去年一个智能家居项目里,当时需要存储大量设备配置和日志数据。这个指甲盖大小的芯片居然有16MB容量,实测下来发现它比想象中强大得多。W25Q128属于SPI NOR Flash家族,采用标准的8引脚封装,通过四线SPI接口就能实现高速数据传输。我特别喜欢它的宽电压特性(2.7V-3.6V),在电池供电场景下特别稳。

芯片内部采用分级存储结构:

  • 256个块(Block),每块64KB
  • 每个块包含16个扇区(Sector),每扇区4KB
  • 每扇区又分为16页(Page),每页256字节

这种结构直接影响我们的操作方式:

  • 最小擦除单位是扇区(必须整块擦除)
  • 最小写入单位是页(单次最多写256字节)
  • 读取则可以按字节操作

注意:擦除操作会把数据全部置为0xFF,写入只能将1改为0,所以必须先擦后写

2. 硬件连接与SPI配置

上周帮学弟调试STM32F103驱动W25Q128时,发现硬件连接有讲究。以常见SOIC-8封装为例:

W25Q128引脚STM32对应引脚功能说明
/CSPB12片选信号(低有效)
DO(MISO)PB14主设备输入
/WP接VCC写保护(高禁用)
GNDGND地线
DI(MOSI)PB15主设备输出
/HOLD接VCC保持功能禁用
CLKPB13时钟信号
VCC3.3V电源

在CubeMX里配置SPI要注意三个关键点:

  1. 时钟极性(CPOL)设为High
  2. 时钟相位(CPHA)选2Edge
  3. 波特率预分频建议先用SPI_BAUDRATEPRESCALER_8
// SPI初始化代码片段 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_HIGH; hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;

3. 底层驱动开发实战

3.1 基础通信函数

先实现最核心的字节收发函数,这里有个坑要注意:STM32的SPI收发是同步的,每次发送都会同时接收数据。

uint8_t SPI_TransmitReceive(uint8_t data) { while(!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE)); HAL_SPI_TransmitReceive(&hspi1, &data, &data, 1, 100); return data; }

3.2 关键指令实现

读设备ID是最常用的调试手段,可以验证SPI通信是否正常:

uint16_t W25Q128_ReadID(void) { uint16_t id = 0; CS_LOW(); SPI_TransmitReceive(0x90); // 读ID指令 SPI_TransmitReceive(0x00); // 空字节 SPI_TransmitReceive(0x00); SPI_TransmitReceive(0x00); id |= SPI_TransmitReceive(0xFF) << 8; // 厂商ID id |= SPI_TransmitReceive(0xFF); // 设备ID CS_HIGH(); return id; // 正常应返回0xEF17 }

写使能指令必须在每次写入前执行:

void W25Q128_WriteEnable(void) { CS_LOW(); SPI_TransmitReceive(0x06); // 写使能指令 CS_HIGH(); }

4. 存储操作三件套

4.1 数据读取

读取是最简单的操作,支持连续读取:

void W25Q128_ReadData(uint8_t *pBuffer, uint32_t addr, uint16_t len) { CS_LOW(); SPI_TransmitReceive(0x03); // 读指令 SPI_TransmitReceive(addr >> 16); SPI_TransmitReceive(addr >> 8); SPI_TransmitReceive(addr); while(len--) { *pBuffer++ = SPI_TransmitReceive(0xFF); } CS_HIGH(); }

4.2 数据写入

写入流程复杂些,必须遵循"擦除->写使能->写入"的顺序:

void W25Q128_WritePage(uint8_t *pData, uint32_t addr, uint16_t len) { // 检查是否跨页(每页256字节) if(((addr & 0xFF) + len) > 256) { len = 256 - (addr & 0xFF); } W25Q128_WriteEnable(); CS_LOW(); SPI_TransmitReceive(0x02); // 页编程指令 SPI_TransmitReceive(addr >> 16); SPI_TransmitReceive(addr >> 8); SPI_TransmitReceive(addr); while(len--) { SPI_TransmitReceive(*pData++); } CS_HIGH(); W25Q128_WaitBusy(); // 等待写入完成 }

4.3 扇区擦除

擦除时间较长(约150ms),建议配合RTOS使用:

void W25Q128_EraseSector(uint32_t addr) { W25Q128_WriteEnable(); CS_LOW(); SPI_TransmitReceive(0x20); // 扇区擦除指令 SPI_TransmitReceive(addr >> 16); SPI_TransmitReceive(addr >> 8); SPI_TransmitReceive(addr); CS_HIGH(); W25Q128_WaitBusy(); }

5. 实战案例:日志存储系统

去年给工业传感器项目做的日志系统,正好用上了W25Q128。分享下关键设计:

  1. 存储结构设计

    • 前4KB存放元数据(日志指针、校验值等)
    • 后续空间按4KB为单位存储日志包
    • 采用循环写入方式
  2. 关键实现代码

#define LOG_START_ADDR 0x1000 // 跳过前4KB #define LOG_SECTOR_SIZE 4096 void Log_Write(uint8_t *data, uint16_t len) { static uint32_t log_ptr = LOG_START_ADDR; // 检查是否需要擦除新扇区 if((log_ptr % LOG_SECTOR_SIZE) == 0) { W25Q128_EraseSector(log_ptr); } // 写入数据 W25Q128_WritePage(data, log_ptr, len); log_ptr += len; // 到达末尾则循环 if(log_ptr >= (16*1024*1024 - LOG_SECTOR_SIZE)) { log_ptr = LOG_START_ADDR; } }
  1. 性能优化技巧
    • 使用双缓冲:一个缓冲接收新数据,另一个缓冲执行写入
    • 定期整理碎片:每月全片擦除一次
    • 添加ECC校验:每256字节增加1字节校验

6. 常见问题排查指南

问题1:读出的全是0xFF

  • 检查/CS引脚是否正常拉低
  • 确认SPI模式设置正确(模式0或3)
  • 测量CLK信号是否正常输出

问题2:写入失败

  • 确保执行了写使能指令
  • 检查/WP引脚是否被拉高
  • 验证目标区域已擦除(全为0xFF)

问题3:数据异常丢失

  • 注意供电稳定性,建议在VCC加100nF电容
  • 避免频繁写同一扇区(寿命约10万次)
  • 重要数据建议双备份存储

最近在做一个物联网网关项目,发现W25Q128在-40℃~85℃工业温度范围内表现稳定,连续运行三个月没有出现数据异常。不过要注意的是,在极端低温环境下,擦除时间会延长约20%,需要适当调整等待超时时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/16 12:43:50

H5制作终极指南:如何用开源神器h5maker零代码打造专业级页面?

H5制作终极指南&#xff1a;如何用开源神器h5maker零代码打造专业级页面&#xff1f; 【免费下载链接】h5maker h5编辑器类似maka、易企秀 账号/密码&#xff1a;admin 项目地址: https://gitcode.com/gh_mirrors/h5/h5maker 还在为制作精美的H5页面而头疼吗&#xff1f…

作者头像 李华
网站建设 2026/7/16 12:42:45

VMware虚拟机安装Ubuntu 22.04服务器版全攻略

1. VMware虚拟机环境准备在开始安装Ubuntu 22.04服务器版之前&#xff0c;我们需要先准备好VMware虚拟化环境。VMware Workstation Pro 16是目前最稳定的版本&#xff0c;实测对Ubuntu 22.04的兼容性最佳。安装时建议选择"自定义安装"模式&#xff0c;勾选所有虚拟化…

作者头像 李华
网站建设 2026/7/16 12:41:34

告别字体加密困扰:大众点评全站数据采集的Python解决方案

告别字体加密困扰&#xff1a;大众点评全站数据采集的Python解决方案 【免费下载链接】dianping_spider 大众点评爬虫&#xff08;全站可爬&#xff0c;解决动态字体加密&#xff0c;非OCR&#xff09;。持续更新 项目地址: https://gitcode.com/gh_mirrors/di/dianping_spid…

作者头像 李华
网站建设 2026/7/16 12:40:49

3步逆向工程:手机号查询QQ号的技术解密与实践指南

3步逆向工程&#xff1a;手机号查询QQ号的技术解密与实践指南 【免费下载链接】phone2qq 项目地址: https://gitcode.com/gh_mirrors/ph/phone2qq 在数字身份管理日益复杂的今天&#xff0c;我们经常面临一个看似简单却颇为棘手的问题&#xff1a;如何通过手机号找回关…

作者头像 李华
网站建设 2026/7/16 12:40:38

终极指南:5分钟彻底解决Windows激活难题

终极指南&#xff1a;5分钟彻底解决Windows激活难题 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows激活弹窗烦恼吗&#xff1f;KMS智能激活方案为你提供稳定持久的系统激活体验。…

作者头像 李华
网站建设 2026/7/16 12:40:37

从理论到实践:排列熵在MATLAB中的参数优化与工程应用

1. 排列熵&#xff1a;时间序列分析的瑞士军刀 第一次接触排列熵是在分析工业设备振动信号时。当时面对一台大型风机的振动数据&#xff0c;传统频谱分析完全找不出故障特征&#xff0c;直到尝试了排列熵算法——它像放大镜一样清晰地揭示了隐藏在噪声中的异常脉冲。排列熵&…

作者头像 李华