【问题标题】:Safely detect, if function is called from an ISR?安全检测,是否从 ISR 调用函数?
【发布时间】:2013-05-15 10:08:22
【问题描述】:

我正在为 ARM Cortex M3 (NXP LPC1769) 微控制器开发软件。目前我正在寻找一种机制来检测我的函数是否在 ISR 中被调用。我想我必须检查登记册。根据这些信息,我想调用困难函数。

如果有包含必要信息的寄存器,我已经检查了参考手册。

例如,我尝试根据“中断活动位寄存器”(IABR) 寄存器检测是否从 ISR(我使用 SysTick-ISR)调用了我。如果 ISR 处于活动状态,则该寄存器应为 != 0。但值是 0x00000000。这意味着没有中断处于活动状态。除了这个测试,我在参考手册中检查了 NVIC 和 SC 寄存器,寻找包含必要标志的寄存器,但我没有找到。

有人知道我的问题的合适寄存器/机制吗?

【问题讨论】:

  • 为什么不能简单地让 ISR 调用一个函数,而让程序的其余部分调用不同的函数?
  • 是的……你必须非常小心,尤其是对任务执行者。不过,它是嵌入式的,祝你好运!
  • @Lundin 也许是为了简化 RTOS 界面。例如 Keil 的 RL-RTX 有独立的中断和线程上下文 API,例如os_evt_setisr_evt_set;在编写可重用代码时,可能并不总是可以预测将来可能会使用哪些上下文,因此您可以创建一个通用的evt_set 包装器,它可以在任何地方使用。此外,如果意外调用了错误的 API,操作系统也会出现错误的行为,因此运行时选择更安全,但开销很小。

标签: embedded cortex-m3


【解决方案1】:

您需要测试中断控制状态寄存器VECTACTIVE字段。

我使用以下:

//! Test if in interrupt mode
inline bool isInterrupt()
{
    return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0 ;
}

SCM 和 SCB_ICSR_VECTACTIVE_Msk 是在 CMSIS (core_cm3.h) 中定义的,我想它们会间接包含在您的部件特定标头中(我猜是 lpc17xx.h 或类似的)。我正在使用 C++,包括 C 中的 stdbool.h 会给你一个 bool 类型,或者更改为你自己的 int 或 typedef。

然后使用它,例如:

void somefunction( char ch )
{
    if( isInterrupt() )
    {
        // Do not block if ISR
        send( ch, NO_WAIT ) ;
    }
    else
    {
        send( ch, TIMEOUT ) ;
    }
}

如果需要假设不了解架构的解决方案,请考虑以下事项:

volatile int interrupt_nest_count = 0 ;
#define ENTER_ISR() interrupt_nest_count++
#define EXIT_ISR()  interrupt_nest_count--
#define IN_ISR() (interrupt_nest_count != 0)

void isrA()
{
     ENTER_ISR() ;
     somefunction( 'a' ) ;
     EXIT_ISR() ;
}

void isrB()
{
     ENTER_ISR() ;
     somefunction( 'b' ) ;
     EXIT_ISR() ;
}

void somefunction( char ch )
{
    if( IN_ISR() )
    {
        // Do not block if ISR
        send( ch, NO_WAIT ) ;
    }
    else
    {
        send( ch, TIMEOUT ) ;
    }
}

然而问题是指安全地检测中断上下文,这依赖于添加到所有 ISR的进入/退出宏。

【讨论】:

  • 我会让 'interrupt_nest_count' 不稳定
  • @Ibrahim:我也会;只用了5年就有人注意到了!谢谢。希望对这有用的任何人都使用第一种方法。
  • C 默认情况下不保证i++ 的原子性,即使i 是易失性的。
  • @Groo :好点 - 读者练习。依赖实现/架构相关的行为而不是语言定义的行为可能有充分的理由。例如,最小化中断延迟。这是一个想法的核心。
  • @Groo :我想我在回答中说了这么多。那是很久以前的事了-如果今天回答,也许我会省略第二个。
【解决方案2】:

经过一番讨论和更多搜索,我找到了正确的寄存器: 中断程序状态寄存器:IPSR 包含异常类型编号 当前的中断服务程序(ISR)。请参见表 626 中的寄存器摘要 它的属性。

如果没有从 isr 调用函数,则寄存器的值为 IPSR == 0

【讨论】:

  • 是的,IPSR 是我过去用于此目的的寄存器。
【解决方案3】:

最简单的方法是将上下文作为参数传递给函数。它也是独立于平台的。

typedef enum _context {
    normal_context = 0,
    isr_context = 1
} context;

从 ISR 调用函数:

func(param1, param2, isr_context);

从普通代码调用函数:

func(param1, param2, normal_context);

如果 ISR 代码不受您的控制,而您只是传递一个函数指针,那么只需使用两个不同的包装函数。一个传递 isr_context,另一个传递 normal_context 作为函数的参数。

【讨论】:

  • 我认为“最简单的方法”其实就是询问处理器状态。
【解决方案4】:

最好的方法可能是创建两个不同的函数:一个从 ISR 调用,另一个从程序的其余部分调用。

如果这不是一个选项,那么您可以使用纯标准 C 来确定调用者,不需要寄存器:

inline void my_func (const char* caller);

static void isr (void)
{
  my_func(__func__);
}


inline void my_func (const char* caller)
{
  if(strcmp(caller, "isr")==0)
  {
    // was called from isr
  }
  else
  {
    // called from elsewhere
  }
}

如果你给你的 ISR 起聪明的名字,上面的代码将足够快地从一个 isr 运行。

【讨论】:

  • 处理器“知道”它何时是中断上下文。这是一种不必要、低效且容易出错的方法。
  • 这是一种观点,但值得商榷。盲目地调用相同的代码并期望它基于对硬件细节的深入了解而表现不同,这也不是每个人都认为合适的做法。这完全取决于您要做什么以及最重要的问题。也可以想象从调度程序的角度来看代码处于中断上下文中的情况,但就硬件而言不再处于中断上下文中。
  • @Clifford 不,处理器不知道这一点。 ARM Cortex M3 知道这一点。似乎 OP 正在编写某种独立于硬件的库。此代码是可移植的。它并非效率低下,它可以进一步优化为编译器可以在编译时评估然后丢弃的东西。通过一些微小的修改,您可以将预处理器输出为if('i' == 'i' && 's' == 's' && 'r' == 'r' && '\0' == '\0')。当然,任何半体面的编译器都会在编译时优化掉。这怎么会容易出错,我无法解释,需要解释一下吗?
  • @Lundin 我不敢苟同 - 这个问题是关于 Cortex M3 特定解决方案的非常具体的问题。硬件抽象包装器更好地服务于可移植性。我想知道您可能会遇到哪些硬件无法确定中断上下文。容易出错,因为它依赖于调用者传递正确的信息。
  • @Lundin 我就不说了,你开始穿了。可以说,IMO 这是一个简单问题的相当不雅的解决方案。您已将 strcmp() 添加到只需要一点测试的东西中 - 并且在可能很关键的 ISR 中。
猜你喜欢
  • 2020-12-17
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多