news 2026/5/12 23:39:45

深入解析SPI总线协议:从基础配置到Flash存储实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析SPI总线协议:从基础配置到Flash存储实战

1. SPI总线协议基础解析

SPI(Serial Peripheral Interface)是一种高速全双工同步串行通信协议,由摩托罗拉在1980年代提出。它凭借简单高效的特性,在嵌入式系统中广泛应用,尤其适合与Flash存储器、传感器等外设进行数据交换。我第一次接触SPI是在调试一个温湿度传感器时,当时被它"四线制"的精巧设计所吸引。

SPI采用主从架构,仅需4根信号线即可完成通信:

  • SCLK(Serial Clock):主设备产生的同步时钟,像乐队的指挥棒一样协调数据传输节奏。我在调试中发现,时钟频率可高达数十MHz,远超I2C的400KHz。
  • MOSI(Master Out Slave In):主设备输出数据线,如同单向行驶的高速公路,专门输送主设备发出的指令和数据包。
  • MISO(Master In Slave Out):从设备输出数据线,与MOSI方向相反,形成完美的双向数据通道。
  • CS/SS(Chip Select):从设备使能信号,低电平有效。就像点名时的举手应答,只有被选中的从设备才会响应通信。

实际项目中曾遇到一个经典问题:当多个SPI设备共用总线时,CS信号切换不及时会导致数据冲突。后来通过增加5μs的延时解决了这个问题,这也让我深刻理解了CS信号的重要性。

2. SPI工作模式深度剖析

SPI最让人着迷也最容易出错的就是它的四种工作模式,这由CPOL(时钟极性)和CPHA(时钟相位)两个参数决定:

模式CPOLCPHA时钟空闲状态数据采样边沿
000低电平上升沿
101低电平下降沿
210高电平下降沿
311高电平上升沿

在调试W25Q128 Flash时,我最初因为模式设置错误导致读取的数据全是0xFF。后来用逻辑分析仪抓取波形才发现,Flash要求模式3(CPOL=1, CPHA=1),而我的初始化代码设置成了模式0。这个教训让我养成了查阅器件手册的好习惯。

时钟极性和相位的配合

  • CPOL=0时,时钟空闲为低电平,第一个边沿是上升沿
  • CPOL=1时,时钟空闲为高电平,第一个边沿是下降沿
  • CPHA决定采样时刻:0表示第一个边沿采样,1表示第二个边沿采样

3. STM32硬件SPI配置实战

下面以STM32F407驱动W25Q128为例,展示完整的SPI初始化流程:

void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; // 使能时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // 配置GPIO复用功能 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5; // PB3~5 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); // 引脚复用映射 GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1); // 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_High; // 模式3 SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // 模式3 SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 软件控制CS SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // 21MHz SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStruct); SPI_Cmd(SPI1, ENABLE); }

这段代码有几个关键点需要注意:

  1. 使用软件控制NSS(CS)信号更灵活
  2. 预分频系数设为4,当APB2时钟为84MHz时,SPI时钟为21MHz
  3. 模式3配置适合大多数SPI Flash器件
  4. 必须使能GPIO的复用功能(AF)

4. W25Q128 Flash操作详解

W25Q128是Winbond推出的16MB SPI Flash,采用标准的SPI指令集。其存储结构组织为:

  • 256个块(Block),每块64KB
  • 每个块包含16个扇区(Sector),每扇区4KB
  • 最小擦除单位是扇区

Flash操作三大基本操作

4.1 读取器件ID

uint16_t W25Q_ReadID(void) { uint16_t id = 0; W25Q_CS(0); // 使能器件 SPI_ReadWriteByte(0x90); // 发送读ID指令 SPI_ReadWriteByte(0x00); // 发送3个空字节 SPI_ReadWriteByte(0x00); SPI_ReadWriteByte(0x00); id |= SPI_ReadWriteByte(0xFF)<<8; // 读取制造商ID id |= SPI_ReadWriteByte(0xFF); // 读取设备ID W25Q_CS(1); // 禁用器件 return id; }

正常返回值应为0xEF17,其中EFh代表Winbond,17h表示128Mbit容量。

4.2 扇区擦除

Flash编程前必须先擦除,这是由其物理特性决定的:

void W25Q_EraseSector(uint32_t addr) { W25Q_WriteEnable(); // 使能写操作 W25Q_CS(0); SPI_ReadWriteByte(0x20); // 扇区擦除指令 SPI_ReadWriteByte(addr>>16); // 发送24位地址 SPI_ReadWriteByte(addr>>8); SPI_ReadWriteByte(addr); W25Q_CS(1); W25Q_WaitForWriteEnd(); // 等待擦除完成 }

擦除一个4KB扇区通常需要50-200ms,期间可以读取状态寄存器判断是否完成。

4.3 数据读写操作

void W25Q_Read(uint8_t *buf, uint32_t addr, uint16_t len) { W25Q_CS(0); SPI_ReadWriteByte(0x03); // 读数据指令 SPI_ReadWriteByte(addr>>16); // 地址 SPI_ReadWriteByte(addr>>8); SPI_ReadWriteByte(addr); while(len--) *buf++ = SPI_ReadWriteByte(0xFF); W25Q_CS(1); } void W25Q_Write(uint8_t *buf, uint32_t addr, uint16_t len) { W25Q_WriteEnable(); W25Q_CS(0); SPI_ReadWriteByte(0x02); // 页编程指令 SPI_ReadWriteByte(addr>>16); // 地址 SPI_ReadWriteByte(addr>>8); SPI_ReadWriteByte(addr); while(len--) SPI_ReadWriteByte(*buf++); W25Q_CS(1); W25Q_WaitForWriteEnd(); }

注意Flash的页编程限制:单次写入不能跨页(每页256字节)。实际项目中我封装了一个自动处理跨页写入的函数,大大提高了开发效率。

5. SPI通信优化技巧

经过多个项目的积累,我总结出以下SPI优化经验:

  1. 时钟配置:在信号质量允许的情况下,尽量使用更高的时钟频率。曾通过将SPI时钟从1MHz提升到18MHz,使Flash读写速度提升近20倍。

  2. DMA传输:对于大数据量传输,使用DMA可以显著降低CPU负载:

// STM32 DMA配置示例 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)txBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_Init(DMA1_Channel3, &DMA_InitStructure); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
  1. 信号完整性
  • 保持SCK和MOSI/MISO线等长
  • 在高速传输时添加33Ω串联电阻
  • 避免信号线平行走线过长
  1. 错误处理
  • 增加超时机制防止死等
  • 定期检查SPI状态寄存器
  • 重要操作前读取状态寄存器确认设备就绪

记得有一次硬件同事将SPI走线布得过于靠近射频模块,导致通信误码率飙升。后来通过重新布局和增加屏蔽层解决了这个问题,这也让我意识到高速数字信号完整性的重要性。

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

可视化中文语义计算|GTE模型WebUI+API双接口详解

可视化中文语义计算&#xff5c;GTE模型WebUIAPI双接口详解 1. 引言&#xff1a;为什么你需要一个“看得见”的语义相似度工具&#xff1f; 你有没有遇到过这样的场景&#xff1f; 写完一段产品描述&#xff0c;想快速判断它和竞品文案是否雷同&#xff0c;但只能靠人工逐字…

作者头像 李华
网站建设 2026/5/10 14:41:45

HG-ha/MTools一键部署优势:快速验证AI功能可行性

HG-ha/MTools一键部署优势&#xff1a;快速验证AI功能可行性 1. 开箱即用&#xff1a;三步完成AI能力验证 你有没有过这样的经历&#xff1a;花一整天配置环境&#xff0c;结果卡在某个依赖版本上&#xff0c;连第一行代码都没跑起来&#xff1f;HG-ha/MTools彻底改变了这个过…

作者头像 李华
网站建设 2026/5/10 9:24:02

Chandra OCR惊艳效果:手写笔记转Markdown实测

Chandra OCR惊艳效果&#xff1a;手写笔记转Markdown实测 1. 这不是普通OCR&#xff0c;是能读懂你手写的“排版翻译官” 你有没有过这样的经历&#xff1a; 手写数学推导的草稿纸堆成山&#xff0c;想整理进笔记却要逐字敲键盘&#xff1b;会议速记本上密密麻麻的思维导图和…

作者头像 李华
网站建设 2026/5/9 18:05:11

实测Qwen-Image-Lightning:40秒生成电影级质感图片的完整流程

实测Qwen-Image-Lightning&#xff1a;40秒生成电影级质感图片的完整流程 最近在AI图像生成领域&#xff0c;一个名字频繁出现在开发者社区——Qwen-Image-Lightning。它不像某些模型靠堆参数博眼球&#xff0c;而是用一套“轻量但不妥协”的思路&#xff0c;把文生图体验拉回…

作者头像 李华
网站建设 2026/5/9 21:09:58

GLM-4v-9b效果展示:医疗报告截图文字识别+医学术语解释案例集

GLM-4v-9b效果展示&#xff1a;医疗报告截图文字识别医学术语解释案例集 1. 这不是普通OCR——它能“读懂”医生写的报告 你有没有试过把一张手机拍的CT报告截图发给AI&#xff0c;结果只得到一堆错字、漏行、格式混乱的文字&#xff1f;或者更糟——AI把“左肺下叶磨玻璃影”…

作者头像 李华
网站建设 2026/5/9 17:11:37

AI净界在电商设计中的应用:批量处理商品主图背景

AI净界在电商设计中的应用&#xff1a;批量处理商品主图背景 1. 为什么电商设计师需要“秒级换背景”能力 你有没有遇到过这样的场景&#xff1a;凌晨两点&#xff0c;运营突然发来200张新品商品图&#xff0c;要求明天一早上线——但所有图片都是白底不够干净、灰底影响质感…

作者头像 李华