Winbond W25Q与STM32F411 Blackpill SPI通信无响应问题
STM32F411 Blackpill W25Q SPI通信无MISO响应排查方案
核心故障原因
PB4引脚默认复用为JTAG调试接口的NJTRST功能,现有代码未释放该引脚的JTAG占用,导致PB4无法切换为SPI1_MISO复用功能,闪存返回的MISO信号完全无法被MCU采集。
修复步骤
- 释放JTAG占用的PB4引脚
在GPIO初始化流程最开头增加SYSCFG时钟使能、JTAG禁用配置,保留SWD调试功能(不影响ST-Link正常下载调试),即可释放PA15、PB3、PB4三个被JTAG占用的引脚。 - 匹配SPI数据位宽
当前SPI配置为8位帧格式(DFF=0),但收发函数使用uint16_t类型访问数据寄存器,存在触发帧错位的风险,建议统一改为8位类型,强制按字节访问数据寄存器。 - 补全SPI引脚速率配置
现有代码仅配置了CS引脚(PA4)的输出速率,SCK、MOSI、MISO引脚默认工作在低速模式,建议统一配置为最高速率,适配后续更高SPI时钟的运行需求。 - 清空SPI初始化残留数据
SPI外设使能后,数据寄存器可能残留上电随机值,建议初始化完成后主动读一次数据寄存器清空缓冲区,避免首次接收数据出错。
修正后的关键代码
uint32_t w25_pin_spi_init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // 新增:开SYSCFG时钟,释放JTAG占用引脚 RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; SYSCFG->CFGR &= ~SYSCFG_CFGR_SWJ_CFG; SYSCFG->CFGR |= SYSCFG_CFGR_SWJ_CFG_1; // 关闭JTAG,保留SWD /* 配置PA4(CS)为输出、PA5/PA7/PB4为复用功能 */ GPIOA->MODER &= ~(GPIO_MODER_MODE4 | GPIO_MODER_MODE5 | GPIO_MODER_MODE7); GPIOB->MODER &= ~GPIO_MODER_MODE4; GPIOA->MODER |= GPIO_MODER_MODE4_0; GPIOA->MODER |= (GPIO_MODER_MODE5_1 | GPIO_MODER_MODE7_1); GPIOB->MODER |= GPIO_MODER_MODE4_1; // 新增:配置所有SPI相关引脚为最高速率 GPIOA->OSPEEDR |= (GPIO_OSPEEDR_OSPEED4 | GPIO_OSPEEDR_OSPEED5 | GPIO_OSPEEDR_OSPEED7); GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED4; /* 推挽模式为复位默认值,无需额外配置 */ GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD4 | GPIO_PUPDR_PUPD5 | GPIO_PUPDR_PUPD7); GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD4; /* MISO配置上拉 */ GPIOB->PUPDR |= GPIO_PUPDR_PUPD4_0; // AF5对应SPI1功能,原配置正确 GPIOA->AFR[0] |= (GPIO_AFRL_AFSEL5_0 | GPIO_AFRL_AFSEL5_2); GPIOA->AFR[0] |= (GPIO_AFRL_AFSEL7_0 | GPIO_AFRL_AFSEL7_2); GPIOB->AFR[0] |= (GPIO_AFRL_AFSEL4_0 | GPIO_AFRL_AFSEL4_2); return 0; } void w25_CS_enable(void) { GPIOA->BSRR = GPIO_BSRR_BR4; // BSRR写1有效,直接赋值即可无需|= } void w25_CS_disable (void) { GPIOA->BSRR = GPIO_BSRR_BS4; } uint32_t w25_spi_init(void) { RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; SPI1->CR1 = 0; // 先清空寄存器原有配置 SPI1->CR1 &= ~SPI_CR1_DFF; // 8位帧格式 SPI1->CR1 |= (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0); // 256分频,约390kHz SPI1->CR1 |= SPI_CR1_MSTR; // 主机模式 SPI1->CR1 |= SPI_CR1_SSM; // 软件控制CS SPI1->CR1 |= SPI_CR1_SSI; // 内部NSS拉高,避免误进入从模式 SPI1->CR1 |= SPI_CR1_SPE; // 使能SPI外设 // 新增:清空接收缓冲区残留数据 volatile uint8_t dummy = *(volatile uint8_t*)&SPI1->DR; (void)dummy; return 0; } // 修正为8位数据类型,强制按字节访问DR寄存器 uint8_t w25_send_receive_data(uint8_t data) { while(!(SPI1->SR & SPI_SR_TXE)); *(volatile uint8_t*)&SPI1->DR = data; while(!(SPI1->SR & SPI_SR_RXNE)); return *(volatile uint8_t*)&SPI1->DR; }
验证方法
- 引脚释放验证:可临时将PB4配置为推挽输出,反复写高/写低,用万用表或示波器测量引脚电平是否随配置变化,确认引脚已脱离JTAG控制。
- 功能验证:运行读JEDEC ID代码,正常返回值为:制造商ID
0xEF、存储类型0x40、容量码0x17(对应W25Q64)。 - 硬件排查:若仍无响应,测量W25Q的VCC引脚电压为3.3V、GND引脚可靠接地,排除芯片焊接虚焊问题。
内容的提问来源于stack exchange,提问作者Wojciech Krupski
相关产品推荐
相关产品推荐

