
1. SPI与IIC协议基础对比在嵌入式系统中SPI和IIC是两种最常用的串行通信协议。先打个比方SPI就像两个人在用对讲机通话全双工而IIC更像是轮流发言的会议半双工。这两种协议在STM32F103上操作W25Q128 Flash时底层实现差异非常明显。SPI协议特点四线制MOSI/MISO/SCLK/CS全双工同步通信时钟速率可达18MHzSTM32F103无设备地址机制靠片选信号选择设备IIC协议特点两线制SDA/SCL半双工通信标准模式100kHz快速模式400kHz通过设备地址寻址W25Q128的IIC地址是0x50实测发现SPI模式下W25Q128的页写入256字节仅需1.2ms而IIC模式需要8ms左右。这是因为SPI的硬件移位寄存器直接处理数据交换而IIC需要软件模拟每个时钟周期的时序。2. 硬件连接与初始化配置2.1 SPI硬件连接方案以STM32F103ZET6为例推荐使用SPI1或SPI2接口// SPI2引脚配置PB12-15 GPIOB-CRH ~(0xF000F000); // 清除PB13/15设置 GPIOB-CRH | 0xB000B000; // PB13/15复用推挽输出 GPIOB-CRH | 0x00030000; // PB12普通推挽输出(CS)特别注意W25Q128的HOLD和WP引脚需要上拉到VCC否则可能无法写入数据。我在调试时就遇到过因为WP引脚接触不良导致写操作失败的坑。2.2 IIC硬件连接方案虽然W25Q128主要支持SPI但通过转换芯片可以用IIC控制// IIC1引脚配置PB6/7 GPIOB-CRL ~(0xFF000000); GPIOB-CRL | 0xFF000000; // PB6/7开漏输出IIC模式下需要特别注意上拉电阻通常4.7kΩ过小的阻值会导致信号上升沿不满足时序要求。曾经因为用了1kΩ电阻导致通信不稳定折腾了半天才发现问题。3. 底层驱动实现对比3.1 SPI基础函数实现SPI的优势在于STM32硬件原生支持uint8_t SPI_ExchangeByte(uint8_t data) { while(!(SPI2-SR SPI_SR_TXE)); // 等待发送缓冲区空 SPI2-DR data; while(!(SPI2-SR SPI_SR_RXNE)); // 等待接收完成 return SPI2-DR; }这个函数同时完成发送和接收体现了SPI全双工特性。实测发现使用DMA传输大批量数据时SPI速率可以达到IIC的20倍以上。3.2 IIC基础函数实现IIC需要完全用GPIO模拟时序void IIC_WriteBit(uint8_t bit) { SDA bit ? 1 : 0; Delay_us(2); // 建立时间 SCL 1; Delay_us(5); // 高电平保持 SCL 0; Delay_us(2); // 低电平保持 }每个字节传输都需要9个时钟周期8位数据1位ACK这就是IIC速度慢的根本原因。在调试IIC时逻辑分析仪是必备工具可以清晰看到每个时钟沿的数据变化。4. W25Q128操作实战4.1 关键操作指令对比操作SPI指令码IIC等效操作耗时对比写使能0x06发送控制字节0x50SPI快3倍页编程0x02写地址数据SPI快8倍扇区擦除0x20特殊命令序列SPI快5倍4.2 SPI模式下的页编程这是最常用的写入操作void W25Q128_SPI_WritePage(uint32_t addr, uint8_t *data) { SPI_CS_LOW(); SPI_ExchangeByte(0x02); // 页编程指令 SPI_ExchangeByte(addr 16); SPI_ExchangeByte(addr 8); SPI_ExchangeByte(addr); for(int i0; i256; i) { SPI_ExchangeByte(data[i]); } SPI_CS_HIGH(); W25Q128_WaitBusy(); // 等待写入完成 }注意点W25Q128的写入操作必须按页对齐256字节跨页写入会导致数据回卷。我曾经因为没注意这点导致前256字节被意外覆盖。4.3 IIC模式下的写入操作虽然非标准但可以实现void W25Q128_IIC_Write(uint32_t addr, uint8_t data) { IIC_Start(); IIC_SendByte(0x501); // 设备地址写 IIC_WaitAck(); IIC_SendByte(addr16); // 24位地址 IIC_WaitAck(); IIC_SendByte(addr8); IIC_WaitAck(); IIC_SendByte(addr); IIC_WaitAck(); IIC_SendByte(data); IIC_WaitAck(); IIC_Stop(); Delay_ms(5); // 写入周期较长 }5. 工程选型建议根据实测数据给出建议高速数据记录必须选择SPI模式最高18MHz时钟DMA支持批量传输适合固件存储、音频数据等引脚资源紧张考虑IIC模式仅需2个GPIO适合低速配置存储多设备共享总线混合使用场景SPI硬件CS多个SPI设备时分复用通过片选切换设备注意总线负载能力特别提醒当使用硬件SPI时CLK相位和极性必须与W25Q128设置一致模式0或模式3否则会出现数据错位。配置示例SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 时钟极性 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 时钟相位6. 常见问题排查SPI模式下的典型问题数据全为0xFF检查CS信号是否有效随机数据错误确认CLK极性/相位设置写入失败确保WP引脚已上拉IIC模式下的典型问题无ACK响应检查设备地址和上拉电阻时序错误用示波器查看SCL/SDA波形写入超时适当延长等待时间有个容易忽略的点W25Q128上电后默认处于写保护状态无论哪种接口都需要先发送写使能命令0x06才能修改数据。7. 性能优化技巧SPI DMA传输DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)SPI2-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)buffer; DMA_InitStructure.DMA_BufferSize 256; DMA_Cmd(DMA1_Channel5, ENABLE); SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);四线快速读取需硬件支持SPI_ExchangeByte(0xEB); // Fast Read Quad I/O SPI_ExchangeByte(addr16); // ...发送地址后切换IO模式扇区预擦除在空闲时提前擦除待用扇区实测发现使用DMA传输四线模式读取速度可以提升到标准SPI的4倍这对大容量数据读取非常有利。8. 代码架构设计建议推荐的分层架构应用层 ├─ 文件系统层FatFS ├─ Flash操作层 │ ├─ SPI接口实现 │ └─ IIC接口实现 └─ 硬件抽象层 ├─ SPI驱动 └─ IIC驱动这种架构下更换通信接口只需修改Flash操作层的实现上层应用代码无需变动。我在实际项目中就遇到过需要从SPI切换到QSPI的情况良好的分层设计让迁移工作变得非常轻松。最后提醒W25Q128的扇区擦除4KB需要较长时间典型值400ms建议在系统空闲时执行擦除操作或者使用双Bank交替工作模式来避免等待。