RISC-V技术论坛
直播中

王英

8年用户 1396经验值
私信 关注
[问答]

arty a7调试蜂鸟E203的demogpio程序重复两次进入中断是怎么回事?

大家好,有个问题想请教一下:
    如题,硬件平台为arty a7,用prebuilt的system.mcs文件,在芯来的IDE中调试demogpio程序,可是发现,每次用button1进入中断后,都是连续进入两次。昨天看公众号,有提到访问device乱序的问题。尝试加了fence的内嵌汇编:

  __asm__ __volatile__(                "fence"                  :                  :                  :"memory"  );可是还是会连续进入两次。有人遇到同样的问题吗?望解答。
                                                                                

回帖(1)

纯纯纯牛奶

2025-11-11 18:19:24

针对您在Arty A7上调试蜂鸟E203的demogpio程序时遇到的中断重复触发问题(按键一次触发两次中断),以下是系统性的分析和解决方案:


根本原因分析




  1. 中断标志清除时序问题(最常见)



    • GPIO外设清除中断标志的寄存器操作需要1-2个时钟周期才能生效

    • 若在清除标志后立即退出中断,硬件可能仍在处理清除前的挂起状态

    • 导致中断控制器再次检测到"未清除"的标志,立即二次触发中断




  2. 机械按键抖动(需硬件验证)



    • 物理按键按下时会产生5-10ms的抖动(多个边沿)

    • 若中断配置为边沿触发(非电平触发),可能捕获多个跳变

    • 典型特征:示波器能看到多次脉冲,非固定2次




  3. 内存访问乱序问题



    • 清除中断标志的写操作可能被后续指令乱序执行

    • 即使添加了fence指令,仍可能存在外设总线延迟




针对性解决方案


方案1:添加中断标志清除后的延时(推荐)


#include "hbird_sdk_soc.h"

void GPIO_IRQHandler(void)
{
    // 1. 读取中断状态
    uint32_t intr_status = GPIO_REG(GPIO_INTR_STAT);

    if (intr_status & BUTTON_INT_MASK) {
        // 2. 执行业务逻辑(如LED翻转)
        GPIO_REG(GPIO_OUTPUT) ^= LED_MASK;

        // 3. 清除中断标志(请确认寄存器操作方式)
        GPIO_REG(GPIO_INTR_CLR) = BUTTON_INT_MASK;

        // 关键延时:等待清除操作生效(约3-5个系统周期)
        for(volatile int i=0; i<10; i++); // 实测调整最佳值

        // 4. 二次确认清除(可选)
        GPIO_REG(GPIO_INTR_CLR) = BUTTON_INT_MASK;
    }
}

方案2:优化中断处理流程


void GPIO_IRQHandler(void)
{
    GPIO_REG(GPIO_INTR_CLR) = BUTTON_INT_MASK;  // 第一时间清除标志

    // 内存屏障确保清除操作完成
    __asm__ __volatile__ ("fence io, io" ::: "memory");

    // 执行业务逻辑
    GPIO_REG(GPIO_OUTPUT) ^= LED_MASK;
}

优化点



  • 中断处理中最先清除标志

  • 使用更强的内存屏障 fence io, io(强制IO操作顺序)

  • 业务逻辑后置(避免执行时间过长)


方案3:硬件消抖处理


// 在初始化代码中配置消抖
void gpio_init(void)
{
    // 开启输入滤波(若有此寄存器)
    GPIO_REG(GPIO_DBNC_CON) |= (1 << BUTTON_PIN);

    // 或配置中断为电平触发
    GPIO_REG(GPIO_INTR_MODE) &= ~(EDGE_TRIG_MASK << BUTTON_PIN);
}


? 注:需查阅《蜂鸟E203外设手册》确认寄存器支持



关键验证步骤



  1. 检查中断类型
    // 确认是否为边沿触发
    printf("INTR MODE: 0x%xn", GPIO_REG(GPIO_INTR_MODE));

  2. 监控中断标志
    void GPIO_IRQHandler(void) {
       uint32_t pre_clr = GPIO_REG(GPIO_INTR_STAT);
       GPIO_REG(GPIO_INTR_CLR) = BUTTON_INT_MASK;
       uint32_t post_clr = GPIO_REG(GPIO_INTR_STAT);
       printf("STAT: 0x%x -> 0x%xn", pre_clr, post_clr);
    }


深度调试建议




  1. 时钟域检查



    • 确认CPU时钟(如50MHz)与GPIO外设时钟(通常APB总线)是否同步

    • 跨时钟域操作需同步机制




  2. 中断控制器配置


    // 查看PLIC/CLINT状态
    printf("PLIC Claim: %dn", __plic_claim());
    __plic_complete(__plic_claim());



  3. 时序分析


    /* 在start.S中添加调试指令 */
    .macro IRQ_TRACE
    csrr t0, mcause
    sw t0, 0(t1)  // 保存到内存观察
    addi t1, t1, 4
    .endm



预期结果


经过上述优化后,按键中断行为应变为:


timeline
    title 中断处理时序优化后
    section 按键操作
    按下按钮       : a1
    section 中断响应
    第一次中断触发 : a2 : 标志立刻清除
    执行LED切换    : a3
    中断退出       : a4
    无二次中断     : -

如问题仍未解决,请提供以下信息进一步分析:



  1. GPIO中断相关寄存器定义(INTR_STAT/CLR等)

  2. 完整的中断初始化代码

  3. 示波器捕获的按键引脚波形(验证抖动)



注:蜂鸟E203常见于《手把手教你设计CPU》一书,其GPIO模块源码可参考书中"e203_subsys_perips.v"中的gpio_ctrl模块,重点关注中断清除逻辑的实现。


举报

更多回帖

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