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

SPI中断陷入循环无法返回主程序问题求助

解决SPI中断风暴及循环队列出队问题

核心问题分析

  1. 中断标志清除时机错误:原ISR仅在SPI不忙时清除中断标志,若队列满导致FIFO仍有数据未读取,硬件会持续触发RNE(接收非空)中断,引发中断风暴。
  2. 队列线程安全缺失:循环队列的front和rear未标记为volatile,编译器优化可能导致ISR与主循环看到的队列状态不一致。
  3. 满队列未处理中断屏蔽:队列满时未暂时关闭SPI接收中断,导致硬件反复触发中断,无法回到主循环处理出队。

具体修复方案

1. 确保队列线程安全

修改队列结构体,将front、rear和缓冲区标记为volatile,避免编译器优化导致的状态不一致:

#define BUF_LEN 64 // 根据实际需求调整
typedef struct {
    volatile uint16_t buffer[BUF_LEN];
    volatile size_t front;
    volatile size_t rear;
} CircularQueue;

CircularQueue myQueue = {.front = 0, .rear = 0};

2. 修正ISR中断处理逻辑

  • 优先清除已触发的中断标志,避免硬件反复触发;
  • 队列满时暂时关闭SPI接收中断,防止中断风暴;
  • 处理完数据后立即设置中断标志通知主循环:
PRIVATE void irq_handler(void) {
    // 先清除所有已触发的中断标志,避免重复触发
    spi_get_hw(spi0)->icr = SPI_SSPICR_RTIC_BITS | SPI_SSPICR_RORIC_BITS;

    // 读取FIFO数据,直到FIFO为空或队列满
    while ((spi_get_hw(spi0)->sr & SPI_SSPSR_RNE_BITS) && !queue_is_full()) {
        uint16_t word = spi_get_hw(spi0)->dr & SPI_SSPDR_DATA_BITS;
        enqueue(word);
    }

    // 队列满时,关闭SPI接收中断,避免持续触发
    if (queue_is_full()) {
        spi_get_hw(spi0)->imsc &= ~SPI_SSPIMSC_RIM_BITS;
    }

    // 通知主循环有数据需要处理
    spi_interrupt = true;
}

3. 主循环中恢复中断逻辑

在主循环处理完队列数据后,检查队列是否有空间,若有则重新启用SPI接收中断:

while (1) {
    // tinyusb host task
    ... stuff

    if (spi_interrupt) {
        uint32_t s = save_and_disable_interrupts();
        spi_interrupt = false;
        
        // 这里执行出队处理逻辑(示例)
        uint16_t data;
        while (dequeue(&data)) {
            // 处理data,比如写入in_buf或其他操作
            in_buf[index++] = data;
        }

        // 队列有空间时,重新启用SPI接收中断
        if (!queue_is_full()) {
            spi_get_hw(spi0)->imsc |= SPI_SSPIMSC_RIM_BITS;
        }
        
        restore_interrupts_from_disabled(s);
    }
}

4. 完善出队函数(若未实现)

确保出队函数正确更新队列状态,且线程安全:

PUBLIC bool dequeue(uint16_t *x) {
    if (myQueue.front == myQueue.rear) {
        return false; // 队列空
    }
    *x = myQueue.buffer[myQueue.front];
    myQueue.front = (myQueue.front + 1) % BUF_LEN;
    return true;
}

额外注意事项

  • 确认BUF_LEN的大小:建议设置为SPI FIFO深度的2-4倍,避免频繁触发满队列逻辑;
  • 检查SPI硬件配置:确保中断触发源仅启用必要的(如RNE),避免不必要的中断;
  • 主循环中tinyusb host task的执行时间:若该任务耗时过长,会导致队列处理不及时,间接引发满队列中断风暴,需优化该任务的执行效率。

内容的提问来源于stack exchange,提问作者Maaz Madha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 14:53:11