【问题标题】:Unimplemented Interrupt Handler Behavior未实现的中断处理程序行为
【发布时间】:2013-10-27 22:01:20
【问题描述】:

过去几天我一直在调试一个不会中断 SMBUS 字符的小程序。我终于找到了一个未实现的中断处理程序。不是我需要handler,而是8051F330被错误配置为硬件中断,没有中断handler来捕捉中断。

现在,这让我有点吃惊。如果没有处理程序,我会期待一个 NOP,但我猜缺少 RETI 会导致 IRQ 锁定?在这种情况下,主循环似乎工作。我知道需要重置中断标志,但 timer0 IRQ 与我的 SMBUS IRQ 没有任何共同之处。

我的解释正确吗?无意的 IRQ 启用会导致 IRQ 停止工作吗?

【问题讨论】:

    标签: 8051


    【解决方案1】:

    参考the C8051F330 datasheet,表 15.1。

    每个中断源(复位、定时器 0 溢出、SMB0 等)都有一个硬编码的中断向量地址。当中断被断言时,微控制器生成一个LCALL 指令到那个向量地址。 SMB0 为 0x003B,例如 Timer 0 溢出为 0x000B。当您编写中断处理程序时(Keil 中的interrupt 关键字,其他示例参见this question),您的链接器通常会在向量地址处放置某种JMP 指令以到达您的处理程序。

    如果您忽略为启用的中断编写处理程序,微控制器仍将向量到该地址。然后会发生什么取决于链接器在代码空间中的位置。我发现小函数往往会被塞入向量地址之间,特别是如果你没有使用很多中断并且有几个连续的向量地址没有被使用。您可以通过查看反汇编程序中 0x003B(SMB0 向量地址)处的代码空间来找出答案。

    如果您不小心在正确的时间将某个此类函数引导到了位于错误位置的中间,您将获得该函数运行部分的任何副作用,然后是末尾的 RET该功能将使您回到中断前的位置。除了由于您没有经过RETI,因此不会清除中断。任何未来相同或更低优先级的中断都将被阻止,因为微认为您仍在为 SMB0 中断服务。

    您建议NOP 可能会替换未实现的处理程序,但这无法完成,因为您的工具链不知道在编译时启用了哪些中断。如果您不编写处理程序并将其定位到特定的向量地址,则假定该部分代码空间是公平的游戏,并将在其中放置其他内容。

    【讨论】:

      【解决方案2】:

      CPU 供应商假设您永远不会忘记编写处理程序,我想我从未见过一个驱动程序包含所有可能的处理程序。

      我只是在重新启用 Timer4 IT 时遇到了使用错误掩码的问题,以便改为启用 Timer5 IT(在我的情况下为 C8051F580)

      所以我最终写了一个像下面这样的文件。

      这还不够,正如其他答案中所说的那样,因为我没有重置每个处理程序中的特定中断(例如为 Timer5 中断清除 TF5),但它可以让您快速找到问题所在。

      宏 SPEC_INT(x) 让我在一行中为每个中断创建一个处理程序。注释行对应于我在应用程序代码中真正使用的中断。 每个处理程序调用一个函数 int_general_function( )

      小心,8051 不喜欢可重入函数(通常意义上的没有堆栈)。当每个处理程序调用相同的函数时,链接器 (Keil C51/BL51) 会发出 L15 警告。必须在 Linker 选项、Overlay 类别中添加以下语句

      int_general_function !*

      为了防止(潜在)局部变量和(潜在)参数不共享它们在内存中的位置。

      #define ALLINTERRUPT_C
      
      #include <my_types.h>
      #include <my_platform.h> //contains #include "C8051F580.h"
      
      //A global var
      u8 global_unexp_int = 0;
      
      void int_general_function(u8 int_nb)
      {
          global_unexp_int = int_nb;
      
          //Do here other useful things 
          //at least place a breakpoint 
          //if you are in debug
      }
      
      #define SPEC_INT(x) \
      void i##x (void) interrupt x                \
      {                                           \
          int_general_function(x);                \
      }
      
      //C8051F580
      SPEC_INT(INTERRUPT_INT0)
      SPEC_INT(INTERRUPT_TIMER0)
      SPEC_INT(INTERRUPT_INT1)
      SPEC_INT(INTERRUPT_TIMER1)
      //SPEC_INT(INTERRUPT_UART0)
      SPEC_INT(INTERRUPT_TIMER2)
      SPEC_INT(INTERRUPT_SPI0)
      SPEC_INT(INTERRUPT_SMBUS0)
      SPEC_INT(INTERRUPT_ADC0_WINDOW)
      SPEC_INT(INTERRUPT_ADC0_EOC)
      //SPEC_INT(INTERRUPT_PCA0)
      SPEC_INT(INTERRUPT_COMPARATOR0)
      SPEC_INT(INTERRUPT_COMPARATOR1)
      SPEC_INT(INTERRUPT_TIMER3)
      SPEC_INT(INTERRUPT_LIN0)
      SPEC_INT(INTERRUPT_VREG)
      //SPEC_INT(INTERRUPT_CAN0)
      SPEC_INT(INTERRUPT_PORT_MATCH)
      SPEC_INT(INTERRUPT_UART1)
      SPEC_INT(INTERRUPT_PCA1)
      SPEC_INT(INTERRUPT_COMPARATOR2)
      //SPEC_INT(INTERRUPT_TIMER4)
      SPEC_INT(INTERRUPT_TIMER5)
      

      希望对你有帮助...

      【讨论】:

        猜你喜欢
        • 2013-09-11
        • 2013-02-21
        • 2010-09-22
        • 2013-04-29
        • 1970-01-01
        • 2017-04-01
        • 2021-12-29
        • 2011-07-20
        • 2014-02-23
        相关资源
        最近更新 更多