RT-Thread论坛
直播中

七上八下

9年用户 905经验值
私信 关注
[问答]

采用cs中断的方式接收spi的数据,第一次能成功接收,后面一直都是spi busy,怎么解决?

采用cs中断的方式接收spi的数据,第一次能成功接收,后面一直都是spi busy

这是我的代码

  • #include
  • #include
  • #include

  • extern SPI_HandleTypeDef hspi1;

  • #define CS_PIN 4 //__STM32_PIN(4 ,  A, 4 ),

  • #define LEN 16
  • uint8_t rx_data[LEN] = {0};
  • uint8_t tx_data[LEN] = {'1', '2', '3', '4', '5', '6', '7', '8', '1', '2', '3', '4', '5', 'e', 'n', 'd'};

  • void on_cs_falling(void *args)
  • {
  •     //rt_thread_mdelay(5);
  •     while (HAL_SPI_GetState(&hspi1)!=HAL_SPI_STATE_READY);
  •     HAL_StatusTypeDef ret = HAL_SPI_TransmitReceive_DMA(&hspi1, tx_data, rx_data, LEN);
  •     rt_kprintf("on_cs_falling. HAL_SPI_TransmitReceive_DMA ret=%d recv=%s!n", ret, rx_data);
  • }

  • void cs_pin_init()
  • {
  •     rt_pin_mode(CS_PIN, PIN_MODE_INPUT_PULLUP);
  •     rt_pin_attach_irq(CS_PIN, PIN_IRQ_MODE_FALLING, on_cs_falling, RT_NULL);
  •     rt_pin_irq_enable(CS_PIN, PIN_IRQ_ENABLE);
  • }

  • /* 导出到自动初始化 */
  • INIT_COMPONENT_EXPORT(cs_pin_init);



回帖(1)

贾飞世

2025-10-21 17:56:44

您的问题在于SPI状态未被正确复位,导致后续传输因BUSY状态失败。以下是解决方案及优化代码:


根本原因分析



  1. SPI状态未复位:首次传输后SPI状态机停留在BUSY状态,后续无法启动新传输。

  2. CS中断处理冲突:CS上升沿(取消选中)未正确处理,可能打断传输。

  3. 回调缺失:未实现SPI传输完成回调函数来清理状态。


解决方案


1. 修改CS中断处理(HAL_GPIO_EXTI_Callback


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  if(GPIO_Pin == GPIO_PIN_4) { // 对应CS_PIN
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET) { // CS下降沿(选中)
      // 启动SPI传输前确保SPI就绪
      if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY) {
        HAL_SPI_TransmitReceive_IT(&hspi1, tx_data, rx_data, LEN);
      }
    }
    else { // CS上升沿(取消选中)
      // 关键:传输被迫中止时复位SPI状态
      if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY) {
        HAL_SPI_Abort(&hspi1); // 强制复位SPI
      }
    }
  }
}

2. 实现SPI传输完成回调


void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
  if(hspi == &hspi1) {
    // 此处可处理接收到的数据(rx_data)
    // 状态机会被HAL库自动重置为READY
  }
}

3. 实现SPI错误回调(关键)


void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
  if(hspi == &hspi1) {
    HAL_SPI_Abort(&hspi1); // 出错时强制复位SPI
  }
}

关键配置检查



  1. SPI初始化:确保配置正确(时钟极性/相位需匹配从设备)
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;    // 常规模式1
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 常规模式0

  2. CS引脚配置

    • 配置为上拉输入(外部下拉)

    • 使能中断触发沿:双边沿触发
      GPIO_InitStruct.Pin = GPIO_PIN_4;
      GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; // 双边沿触发
      GPIO_InitStruct.Pull = GPIO_PULLUP; // 确保空闲时为高电平



工作流程说明



  1. CS拉低(选中) → 触发下降沿中断 → 启动SPI传输

  2. 传输完成 → 自动触发TxRxCpltCallback → SPI状态重置为READY

  3. CS拉高(取消选中) → 触发上升沿中断 → 若SPI仍BUSY则强制终止

  4. 传输出错 → 触发ErrorCallback → 强制复位SPI


注意事项



  1. 超时处理:在HAL_SPI_TransmitReceive_IT()前可添加超时检测
    uint32_t timeout = 100; // 适当超时值
    while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY && timeout--);

  2. 避免频繁中断:在传输完成后才允许新CS中断(可通过全局变量标志控制)

  3. SPI时钟速度:过高时钟可能导致从设备响应不及时,适当降低测试



实测建议:在HAL_SPI_Abort()后添加10μs延时,确保硬件复位完成:


HAL_SPI_Abort(&hspi1);
HAL_Delay(1); // 1ms延时(根据实际调整)


通过上述修改,可确保SPI状态机被正确复位,解决连续传输时的BUSY状态锁死问题。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分