2026/1/5 22:43:15
网站建设
项目流程
搭建一个网站的服务器,化妆品网站设计报告,用js做的个人酷炫网站,网站建设执招标评分表前言#xff1a;在前面的文章中#xff0c;我们已经系统介绍了 SPI 通信原理 以及 W25Q64 的存储结构和操作特性。 本篇文章将进入实战阶段#xff0c;基于 STM32F103C8T6#xff0c;通过 软件 SPI#xff08;GPIO 模拟 SPI#xff09; 的方式#xff0c;实现对 W25Q64 …前言在前面的文章中我们已经系统介绍了SPI 通信原理以及W25Q64 的存储结构和操作特性。本篇文章将进入实战阶段基于STM32F103C8T6通过软件 SPIGPIO 模拟 SPI的方式实现对W25Q64 外部 Flash 的初始化、ID 读取、扇区擦除、页写入和数据读取并通过 OLED 显示结果进行验证。目录一、接线图二、硬件连接说明三、软件 SPI 分层设计思想四、软件 SPI 底层实现五、W25Q64 指令宏定义六、W25Q64 驱动实现七、主函数测试与实验现象八、总结一、接线图二、硬件连接说明本实验使用 STM32F103C8T6 与 W25Q64 通过 SPI 方式连接引脚定义如下W25Q64STM32CSPA4SCKPA5MISOPA6MOSIPA7VCC3.3VGNDGNDSPI 工作模式Mode 0CPOL 0CPHA 0三、软件 SPI 分层设计思想为了让代码结构清晰本工程将软件 SPI 分为两层1.引脚配置层GPIO 操作只关心SS / SCK / MOSI / MISO提供“写引脚 / 读引脚”的接口2. 协议层SPI 时序基于引脚操作实现SPI 起始 / 终止字节交换8 位时序W25Q64 驱动层完全不关心 GPIO 细节只调用 SPI 接口结构非常清晰。四、软件 SPI 底层实现1. SPI 引脚操作函数#include stm32f10x.h // Device header /*引脚配置层*/ void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); } void MySPI_W_SCK(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue); } void MySPI_W_MOSI(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue); } uint8_t MySPI_R_MISO(void) { return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6); }2. SPI GPIO 初始化void MySPI_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_Init(GPIOA, GPIO_InitStructure); MySPI_W_SS(1); // CS 默认拉高 MySPI_W_SCK(0); // SPI Mode0SCK 空闲为低 }3. SPI 起始 / 终止void MySPI_Start(void) { MySPI_W_SS(0); } void MySPI_Stop(void) { MySPI_W_SS(1); }4. SPI 字节交换Mode 0uint8_t MySPI_SwapByte(uint8_t ByteSend) { uint8_t i, ByteReceive 0x00; for (i 0; i 8; i) { MySPI_W_MOSI(ByteSend (0x80 i)); MySPI_W_SCK(1); if (MySPI_R_MISO()) { ByteReceive | (0x80 i); } MySPI_W_SCK(0); } return ByteReceive; }说明上升沿发送数据上升沿采样 MISO完全符合 SPI Mode 0 时序五、W25Q64 指令宏定义#ifndef __W25Q64_INS_H #define __W25Q64_INS_H #define W25Q64_WRITE_ENABLE 0x06 #define W25Q64_READ_STATUS_REGISTER_1 0x05 #define W25Q64_PAGE_PROGRAM 0x02 #define W25Q64_SECTOR_ERASE_4KB 0x20 #define W25Q64_READ_DATA 0x03 #define W25Q64_JEDEC_ID 0x9F #define W25Q64_DUMMY_BYTE 0xFF #endif六、W25Q64 驱动实现1. 初始化void W25Q64_Init(void) { MySPI_Init(); }2. 读取芯片 ID验证通信是否成功void W25Q64_ReadID(uint8_t *MID, uint16_t *DID) { MySPI_Start(); MySPI_SwapByte(W25Q64_JEDEC_ID); *MID MySPI_SwapByte(W25Q64_DUMMY_BYTE); *DID MySPI_SwapByte(W25Q64_DUMMY_BYTE); *DID 8; *DID | MySPI_SwapByte(W25Q64_DUMMY_BYTE); MySPI_Stop(); }3. 写使能与忙等待void W25Q64_WriteEnable(void) { MySPI_Start(); MySPI_SwapByte(W25Q64_WRITE_ENABLE); MySPI_Stop(); } void W25Q64_WaitBusy(void) { uint32_t Timeout 100000; MySPI_Start(); MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); while (MySPI_SwapByte(W25Q64_DUMMY_BYTE) 0x01) { if (--Timeout 0) break; } MySPI_Stop(); }4. 扇区擦除4KBvoid W25Q64_SectorErase(uint32_t Address) { W25Q64_WriteEnable(); MySPI_Start(); MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); MySPI_SwapByte(Address 16); MySPI_SwapByte(Address 8); MySPI_SwapByte(Address); MySPI_Stop(); W25Q64_WaitBusy(); }5. 页写入不跨页void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count) { uint16_t i; W25Q64_WriteEnable(); MySPI_Start(); MySPI_SwapByte(W25Q64_PAGE_PROGRAM); MySPI_SwapByte(Address 16); MySPI_SwapByte(Address 8); MySPI_SwapByte(Address); for (i 0; i Count; i) { MySPI_SwapByte(DataArray[i]); } MySPI_Stop(); W25Q64_WaitBusy(); }6. 数据读取void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count) { uint32_t i; MySPI_Start(); MySPI_SwapByte(W25Q64_READ_DATA); MySPI_SwapByte(Address 16); MySPI_SwapByte(Address 8); MySPI_SwapByte(Address); for (i 0; i Count; i) { DataArray[i] MySPI_SwapByte(W25Q64_DUMMY_BYTE); } MySPI_Stop(); }七、主函数测试与实验现象uint8_t ArrayWrite[] {0x01, 0x02, 0x03, 0x04}; uint8_t ArrayRead[4]; int main(void) { OLED_Init(); W25Q64_Init(); W25Q64_ReadID(MID, DID); W25Q64_SectorErase(0x000000); W25Q64_PageProgram(0x000000, ArrayWrite, 4); W25Q64_ReadData(0x000000, ArrayRead, 4); while (1) {} }实验现象OLED 正确显示 MID / DID写入数组与读取数组完全一致说明软件 SPI W25Q64 读写擦除功能完全正常八、总结通过本实验我们完整实现了软件 SPI 的 GPIO 模拟W25Q64 的 ID 读取扇区擦除、页写入、数据读取STM32 与外部 Flash 的稳定通信