STM32F3探索板SPI与I3G4250D陀螺仪寄存器级通信问题问询
问题修复方案
核心错误点及修复步骤
- 关闭不必要的DMA使能位
你配置了SPI_CR2_RXDMAEN但未使用DMA功能,该位开启时SPI接收的数据会直接交由DMA搬运,不会置位RXNE标志位,导致你读取DR寄存器拿到的始终是无效的历史值或者总线浮空值,直接删除该位的使能代码即可。 - 补全NSS(CS)引脚的控制逻辑
你开启了SPI软件从设备管理,但外接陀螺仪的CS引脚(PE3)没有做传输前后的电平切换:- 初始化PE3时配置为推挽输出,初始电平拉高(未选中传感器状态)
- 每次SPI读写操作前先把PE3拉低,操作完成后再拉高
另外删除GPIOE MODER寄存器重复赋值的冗余代码。
- 统一SPI DR寄存器的访问位宽,避免FIFO打包异常
STM32F3的SPI DR寄存器是16位宽度,访问宽度会直接触发FIFO的自动打包逻辑:写8位会往FIFO塞1字节,写16位会塞2字节,混用时会导致收发数据错位。要么全用8位指针访问DR,要么全用16位访问,不要混用两种位宽的操作。 - 正确配置CPOL/CPHA位
I3G4250D的SPI时序要求空闲时钟为高电平、数据在第二个时钟边沿采样,对应SPI_CR1_CPOL和SPI_CR1_CPHA都要置1,你之前未配置这两位,采样点偏移导致读数据错误。 - 修正SPI读操作的时序
读传感器寄存器时,发送完带读标志的寄存器地址后,需要先读取第一字节的哑元返回值,再发送哑元字节读取真实的寄存器值,你之前的读逻辑漏了第一步哑元数据的读取,导致拿到的始终是错位值。
修改后的参考代码
// 先定义CS脚操作宏,方便调用 #define SPI_CS_LOW() (GPIOE->BSRR = GPIO_BSRR_BR_3) #define SPI_CS_HIGH() (GPIOE->BSRR = GPIO_BSRR_BS_3) void SPI_Init(void) { /* 外设时钟使能 */ RCC->AHBENR |= RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOAEN; RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; /* GPIO配置 */ // SPI引脚复用配置 GPIOA->MODER |= GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1; GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5 | GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7; GPIOA->AFR[0] |= (5 << 20) | (5 << 24) | (5 << 28); // PA5/6/7复用为AF5 SPI1 // CS引脚PE3配置:推挽输出,初始拉高 GPIOE->MODER |= GPIO_MODER_MODER3_0; GPIOE->OTYPER &= ~GPIO_OTYPER_OT_3; SPI_CS_HIGH(); /* SPI配置 */ SPI1->CR1 = 0; // 先清空CR1,避免默认值干扰 SPI1->CR2 = 0; // 先清空CR2 SPI1->CR2 |= SPI_CR2_FRXTH; // 8位数据时RXNE收到1字节就触发 // 配置CPOL=1、CPHA=1,波特率6MHz,主机模式,软件NSS SPI1->CR1 |= SPI_CR1_CPOL | SPI_CR1_CPHA | SPI_CR1_BR_1 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR; SPI1->CR1 |= SPI_CR1_SPE; // 开启SPI /* 传感器初始化 */ SPI_WriteByte(CTRL_REG1_G, 0x9f); SPI_WriteByte(CTRL_REG4_G, 0x10); SPI_WriteByte(CTRL_REG5_G, 0x10); // 读测试 uint8_t test = SPI_ReadByte(CTRL_REG1_G); } // 8位SPI读写底层函数,发一个字节返回收的一个字节 static uint8_t SPI_TransmitReceive(uint8_t data) { uint8_t* spiDrPtr = (__IO uint8_t*)&SPI1->DR; *spiDrPtr = data; while(!(SPI1->SR & SPI_SR_TXE)); // 等待发送完成 while(!(SPI1->SR & SPI_SR_RXNE)); // 等待接收完成 return *spiDrPtr; } // 写传感器寄存器 void SPI_WriteByte(uint8_t regAdd, uint8_t data) { SPI_CS_LOW(); SPI_TransmitReceive(regAdd); // 写寄存器地址,最高位为0表示写操作 SPI_TransmitReceive(data); // 写寄存器值 SPI_CS_HIGH(); // 等待BUSY位清空,避免传输未完成就操作下一次 while(SPI1->SR & SPI_SR_BSY); } // 读传感器寄存器 uint8_t SPI_ReadByte(uint8_t regAdd) { uint8_t ret; SPI_CS_LOW(); SPI_TransmitReceive(regAdd | 0x80); // 最高位为1表示读操作 ret = SPI_TransmitReceive(0xff); // 发哑元读返回值 SPI_CS_HIGH(); while(SPI1->SR & SPI_SR_BSY); return ret; }
额外排查建议
如果修改后还是读不到有效值,可以用示波器抓SCK、MOSI、MISO、CS四个引脚的波形,对比传感器手册的时序要求,确认波特率是否超过传感器的SPI最高速率(I3G4250D最高支持10MHz,你配置的6MHz是符合要求的)。
内容的提问来源于stack exchange,提问作者user13909066
相关产品推荐
相关产品推荐

