You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F3探索板SPI与I3G4250D陀螺仪寄存器级通信问题问询

问题修复方案

核心错误点及修复步骤

  • 关闭不必要的DMA使能位
    你配置了SPI_CR2_RXDMAEN但未使用DMA功能,该位开启时SPI接收的数据会直接交由DMA搬运,不会置位RXNE标志位,导致你读取DR寄存器拿到的始终是无效的历史值或者总线浮空值,直接删除该位的使能代码即可。
  • 补全NSS(CS)引脚的控制逻辑
    你开启了SPI软件从设备管理,但外接陀螺仪的CS引脚(PE3)没有做传输前后的电平切换:
    1. 初始化PE3时配置为推挽输出,初始电平拉高(未选中传感器状态)
    2. 每次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 05:24:05