STM32 SPI实战:5分钟搞定W25X16 Flash读写(附完整代码)
STM32 SPI实战5分钟搞定W25X16 Flash读写附完整代码在嵌入式开发中SPISerial Peripheral Interface是一种常见的高速全双工通信协议广泛应用于Flash存储、传感器、显示屏等外设的连接。本文将带你快速实现STM32与W25X16 Flash芯片的SPI通信从硬件连接到代码实现手把手教你完成芯片ID读取操作。1. 硬件连接与SPI基础W25X16是一款16M-bit的串行Flash存储器采用SPI接口进行通信。在开始编码前我们需要确保硬件连接正确SCK(PA5): 时钟信号线MISO(PA6): 主设备输入从设备输出MOSI(PA7): 主设备输出从设备输入CS(PA2): 片选信号低电平有效SPI通信有四种工作模式由CPOL时钟极性和CPHA时钟相位决定模式CPOLCPHA空闲时钟数据采样边沿000低电平上升沿101低电平下降沿210高电平下降沿311高电平上升沿W25X16支持模式0和模式3本文示例将使用模式3CPOL1CPHA1。2. STM32 SPI初始化配置以下是使用STM32标准外设库配置SPI1为主设备的代码void SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // 配置SPI引脚 GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; // SCK, MOSI GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; // MISO GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); // 配置CS引脚 GPIO_InitStructure.GPIO_Pin GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_2); // CS高电平 // SPI参数配置 SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_High; // 模式3 SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }提示SPI时钟预分频器BaudRatePrescaler应根据实际需求设置对于W25X16最高支持80MHz时钟频率。3. SPI数据收发函数实现SPI通信是全双工的发送和接收同时进行。以下是基本的字节收发函数uint8_t SPI_ReadWriteByte(uint8_t byte) { // 等待发送缓冲区空 while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); // 发送数据 SPI_I2S_SendData(SPI1, byte); // 等待接收完成 while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); // 返回接收到的数据 return SPI_I2S_ReceiveData(SPI1); }4. W25X16 Flash操作实战4.1 读取设备IDW25X16提供了几个重要的识别指令其中读取设备ID0x90是最常用的uint16_t W25X16_ReadID(void) { uint16_t id 0; // 拉低CS GPIO_ResetBits(GPIOA, GPIO_Pin_2); // 发送读取ID指令 SPI_ReadWriteByte(0x90); // 发送3字节空地址 SPI_ReadWriteByte(0x00); SPI_ReadWriteByte(0x00); SPI_ReadWriteByte(0x00); // 读取2字节ID id SPI_ReadWriteByte(0xFF) 8; id | SPI_ReadWriteByte(0xFF); // 拉高CS GPIO_SetBits(GPIOA, GPIO_Pin_2); return id; }W25X16的设备ID应为0xEF14其中0xEF表示厂商Winbond0x14表示设备型号。4.2 常用Flash操作指令W25X16支持多种操作指令以下是几个常用的指令名称指令代码描述写使能0x06允许写入操作写禁止0x04禁止写入操作读数据0x03读取Flash数据页编程0x02写入一页数据(256字节)扇区擦除0x20擦除4KB扇区芯片擦除0xC7擦除整个芯片注意在执行写入或擦除操作前必须先发送写使能指令0x06并且需要检查忙状态位。4.3 状态寄存器读取W25X16有一个状态寄存器可以通过以下代码读取uint8_t W25X16_ReadSR(void) { uint8_t sr; GPIO_ResetBits(GPIOA, GPIO_Pin_2); SPI_ReadWriteByte(0x05); // 读状态寄存器指令 sr SPI_ReadWriteByte(0xFF); GPIO_SetBits(GPIOA, GPIO_Pin_2); return sr; }状态寄存器中最重要的位是BUSY位bit0当芯片执行写入或擦除操作时该位为1操作完成后自动清零。5. 完整示例读取ID并打印结合USART输出我们可以创建一个完整的示例来验证SPI通信#include stm32f10x.h #include stdio.h // 初始化USART1 void USART1_Init(void) { // USART初始化代码... } // 发送一个字符 void USART1_SendChar(char ch) { // 字符发送代码... } // 发送字符串 void USART1_SendString(char *str) { while(*str) { USART1_SendChar(*str); } } int main(void) { uint16_t flash_id; char buf[32]; // 初始化外设 USART1_Init(); SPI_Init(); // 读取Flash ID flash_id W25X16_ReadID(); // 打印结果 sprintf(buf, Flash ID: 0x%04X\r\n, flash_id); USART1_SendString(buf); while(1) { // 主循环 } }在实际项目中遇到最多的问题是SPI时钟相位和极性的配置错误导致通信失败。通过逻辑分析仪抓取波形可以快速定位这类问题。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2421106.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!