news 2026/3/18 3:54:58

STM32 SPI通信实战:从基础配置到W25Q64闪存读写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32 SPI通信实战:从基础配置到W25Q64闪存读写

1. SPI通信基础与STM32硬件配置

SPI(Serial Peripheral Interface)是一种高速全双工同步串行通信协议,由摩托罗拉公司设计,广泛应用于嵌入式系统中。它只需要四根信号线就能实现主从设备之间的数据交换,非常适合连接Flash存储器、传感器等外设。

SPI的四根信号线分别是:

  • SCK(Serial Clock):时钟信号,由主设备产生
  • MOSI(Master Output Slave Input):主设备输出,从设备输入
  • MISO(Master Input Slave Output):主设备输入,从设备输出
  • SS(Slave Select):从设备片选信号

在STM32中配置SPI外设时,我们需要重点关注几个关键参数:

  1. 时钟极性(CPOL):决定SCK空闲时的电平状态
  2. 时钟相位(CPHA):决定数据在时钟的哪个边沿采样
  3. 波特率分频:设置SPI通信速率
  4. 数据帧格式:8位或16位数据

以STM32F1系列为例,硬件SPI1的引脚通常对应:

  • PA4:SS(软件控制)
  • PA5:SCK
  • PA6:MISO
  • PA7:MOSI

2. W25Q64闪存芯片特性解析

W25Q64是Winbond公司推出的64M-bit串行Flash存储器,采用SPI接口通信,具有以下特点:

  • 支持标准SPI(模式0和模式3)
  • 最高支持104MHz时钟频率
  • 支持扇区擦除(4KB)、块擦除(32KB/64KB)和整片擦除
  • 页编程操作(每页256字节)
  • 写保护功能和保持/暂停功能

实际项目中我发现,W25Q64对时序要求比较严格。有一次调试时发现写入失败,最后发现是SPI时钟相位配置错误。正确的配置应该是:

  • CPOL=0(空闲时低电平)
  • CPHA=0(第一个边沿采样)

3. STM32 SPI外设初始化实战

下面是一个完整的SPI初始化代码示例,采用STM32标准外设库:

void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; // 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // 2. 配置GPIO // SCK和MOSI配置为复用推挽输出 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); // MISO配置为上拉输入 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &GPIO_InitStruct); // SS配置为普通推挽输出 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_SetBits(GPIOA, GPIO_Pin_4); // 默认不选中 // 3. 配置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; // 模式0 SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // 模式0 SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // 18MHz @72MHz PCLK SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStruct.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStruct); // 4. 使能SPI SPI_Cmd(SPI1, ENABLE); }

调试时有个小技巧:如果SPI通信不正常,可以先用逻辑分析仪抓取SCK、MOSI和MISO的波形,确认时序是否符合预期。我曾经遇到过因为GPIO速度配置过低导致波形畸变的问题,将GPIO_Speed提高到50MHz后解决。

4. W25Q64驱动开发与功能实现

4.1 基本读写函数封装

首先需要封装几个基础函数:

// 片选控制 void W25Q64_CS(uint8_t state) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)state); } // 发送一个字节并接收返回值 uint8_t SPI1_ReadWriteByte(uint8_t data) { while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, data); while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } // 读取设备ID(用于检测芯片是否正常) uint32_t W25Q64_ReadID(void) { uint32_t id = 0; W25Q64_CS(0); SPI1_ReadWriteByte(0x9F); // JEDEC ID指令 id |= SPI1_ReadWriteByte(0xFF) << 16; id |= SPI1_ReadWriteByte(0xFF) << 8; id |= SPI1_ReadWriteByte(0xFF); W25Q64_CS(1); return id; }

4.2 闪存操作指令实现

W25Q64的操作都需要通过特定指令实现,常见指令如下:

#define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_PageProgram 0x02 #define W25X_SectorErase 0x20

实现写使能和等待写入完成函数:

void W25Q64_WriteEnable(void) { W25Q64_CS(0); SPI1_ReadWriteByte(W25X_WriteEnable); W25Q64_CS(1); } void W25Q64_WaitBusy(void) { W25Q64_CS(0); SPI1_ReadWriteByte(W25X_ReadStatusReg); while((SPI1_ReadWriteByte(0xFF) & 0x01) == 0x01); W25Q64_CS(1); }

4.3 扇区擦除与页编程

Flash存储器必须先擦除才能写入,擦除的最小单位是扇区(4KB):

void W25Q64_SectorErase(uint32_t addr) { W25Q64_WriteEnable(); W25Q64_CS(0); SPI1_ReadWriteByte(W25X_SectorErase); SPI1_ReadWriteByte((addr >> 16) & 0xFF); SPI1_ReadWriteByte((addr >> 8) & 0xFF); SPI1_ReadWriteByte(addr & 0xFF); W25Q64_CS(1); W25Q64_WaitBusy(); }

页编程函数(最多写入256字节):

void W25Q64_PageProgram(uint32_t addr, uint8_t *data, uint16_t len) { W25Q64_WriteEnable(); W25Q64_CS(0); SPI1_ReadWriteByte(W25X_PageProgram); SPI1_ReadWriteByte((addr >> 16) & 0xFF); SPI1_ReadWriteByte((addr >> 8) & 0xFF); SPI1_ReadWriteByte(addr & 0xFF); for(uint16_t i=0; i<len; i++) { SPI1_ReadWriteByte(data[i]); } W25Q64_CS(1); W25Q64_WaitBusy(); }

5. 实际应用中的注意事项

  1. 时序问题:W25Q64对指令时序有严格要求,每个指令后需要适当延时
  2. 写保护:写入前必须发送Write Enable指令,且WP引脚需接高电平
  3. 跨页写入:单次写入不能跨页(256字节边界)
  4. 擦除时间:扇区擦除需要较长时间(典型值100ms)
  5. 电源稳定性:在写入和擦除操作期间必须保证电源稳定

一个完整的读写流程示例:

  1. 读取设备ID确认通信正常
  2. 擦除目标扇区
  3. 等待擦除完成
  4. 写入数据
  5. 读取回校验
uint8_t buf[256], rbuf[256]; memset(buf, 0xAA, 256); // 擦除第一个扇区 W25Q64_SectorErase(0x000000); // 写入数据 W25Q64_PageProgram(0x000000, buf, 256); // 读取验证 W25Q64_ReadData(0x000000, rbuf, 256); if(memcmp(buf, rbuf, 256) == 0) { printf("Verify OK!\n"); }

在项目开发中,建议将W25Q64的操作封装成独立的驱动模块,便于移植和维护。对于需要频繁读写的场景,可以考虑实现缓存机制来减少擦写次数,延长Flash寿命。

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

游戏本地化三步实现:HS2-HF Patch完整使用指南

游戏本地化三步实现&#xff1a;HS2-HF Patch完整使用指南 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch 当你在游戏世界中遇到满屏陌生文字&#xff0c;无法理…

作者头像 李华
网站建设 2026/3/14 12:59:14

告别数据焦虑:微信聊天记录备份的创新解决方案

告别数据焦虑&#xff1a;微信聊天记录备份的创新解决方案 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg …

作者头像 李华
网站建设 2026/3/14 1:29:20

3步终结文献混乱:比手动快10倍的Zotero批量处理方案

3步终结文献混乱&#xff1a;比手动快10倍的Zotero批量处理方案 【免费下载链接】zotero-addons Zotero add-on to list and install add-ons in Zotero 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-addons 你是否曾在整理文献时陷入重复操作的泥潭&#xff1f…

作者头像 李华
网站建设 2026/3/16 3:36:44

YOLOv9训练报错怎么办?这份避坑清单请收好

YOLOv9训练报错怎么办&#xff1f;这份避坑清单请收好 YOLOv9刚发布时&#xff0c;不少开发者兴奋地拉起镜像、准备数据、敲下训练命令——结果还没跑完第一个epoch&#xff0c;终端就跳出一连串红色报错&#xff1a;CUDA out of memory、KeyError: names、AttributeError: No…

作者头像 李华
网站建设 2026/3/17 1:24:48

零代码测试革新性实战指南:企业级自动化测试平台应用手册

零代码测试革新性实战指南&#xff1a;企业级自动化测试平台应用手册 【免费下载链接】testsigma A powerful open source test automation platform for Web Apps, Mobile Apps, and APIs. Build stable and reliable end-to-end tests DevOps speed. 项目地址: https://gi…

作者头像 李华
网站建设 2026/3/15 13:12:21

Lenovo刃7000k BIOS高级功能解锁与优化指南

Lenovo刃7000k BIOS高级功能解锁与优化指南 【免费下载链接】Lenovo-7000k-Unlock-BIOS Lenovo联想刃7000k2021-3060版解锁BIOS隐藏选项并提升为Admin权限 项目地址: https://gitcode.com/gh_mirrors/le/Lenovo-7000k-Unlock-BIOS 1. 性能瓶颈诊断 1.1 原厂BIOS限制分析…

作者头像 李华