news 2026/5/23 14:21:39

深入解析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/21 4:49:17

GPEN对儿童与老人面部的修复效果专项评测

GPEN对儿童与老人面部的修复效果专项评测 1. 为什么专门测试儿童和老人的脸&#xff1f; 你有没有试过用AI修图工具处理家里那张泛黄的老年合影&#xff1f;或者给刚出生不久的宝宝拍的模糊抓拍照做增强&#xff1f;很多通用人脸修复模型在面对这两类人群时&#xff0c;常常“…

作者头像 李华
网站建设 2026/5/22 2:13:05

从部署到调用,完整流程解析GLM-4.6V-Flash-WEB应用

从部署到调用&#xff0c;完整流程解析GLM-4.6V-Flash-WEB应用 你是否试过在本地跑一个视觉大模型&#xff0c;却卡在环境配置、端口映射或API调用上&#xff1f;是否下载了镜像&#xff0c;打开文档只看到“一键运行”&#xff0c;却不知道这“一键”背后到底发生了什么&…

作者头像 李华
网站建设 2026/5/8 6:03:40

视频资源本地化管理工具:技术原理与高效应用指南

视频资源本地化管理工具&#xff1a;技术原理与高效应用指南 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09…

作者头像 李华
网站建设 2026/5/19 19:15:37

突破云存储下载壁垒:高效提速全攻略

突破云存储下载壁垒&#xff1a;高效提速全攻略 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 云存储服务已成为现代工作与生活中不可或缺的文件管理工具&#xff0c;然而下载…

作者头像 李华