0

我现在使用嵌入式产品,即 PIC32 Microchip CPU。

我熟悉几个实时内核:AVIXFreeRTOSTNKernel,在所有这些内核中,我们几乎都有两个版本的函数:一个用于从任务调用,第二个用于从 ISR 调用。

当然,这对于可以切换上下文和/或睡眠的函数是有意义的:显然,ISR 不能睡眠,并且应该以不同的方式进行上下文切换。但是有几个函数既不切换上下文也不休眠:比如说,它可能返回系统滴答计数,或者设置软件计时器等。

现在,我正在实现自己的内核:TNeoKernel,它具有格式良好的代码并经过仔细测试,并且有时我正在考虑发明“通用”函数:可以从任务或 ISR 上下文调用的函数。但是由于所有上述三个内核都使用单独的功能,我担心我会做错什么。

比如说,在任务和 ISR 上下文中,TNKernel 使用不同的例程来禁用/恢复中断,但据我所知,唯一可能的区别是,如果目标平台不支持,ISR 函数可能被“编译”作为优化嵌套中断。但是如果目标平台支持嵌套中断,那么禁用/恢复中断对于任务和 ISR 上下文看起来完全一样。

所以,我的问题是:在哪些平台上,从 ISR 禁用/恢复中断的操作应该与非 ISR 上下文不同?

如果没有这样的平台,我更愿意使用“通用”功能。如果您对此方法有任何意见,我们将不胜感激。

UPD:我不喜欢有两组函数,因为它们会导致明显的代码重复和复杂化。说,我需要提供一个应该启动软件定时器的功能。这是它的样子:

enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout)
{
   /* ... real job is done here ... */
}

/*
 * Function to be called from task
 */
enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout)
{
   TN_INTSAVE_DATA;  //-- define the variable to store interrupt status,
                     //   it is used by TN_INT_DIS_SAVE()
                     //   and TN_INT_RESTORE()
   enum TN_RCode rc = TN_RC_OK;

   //-- check that function is called from right context
   if (!tn_is_task_context()){
      rc = TN_RC_WCONTEXT;
      goto out;
   }

   //-- disable interrupts
   TN_INT_DIS_SAVE();

   //-- perform real job, after all
   rc = _tn_timer_start(timer, timeout);

   //-- restore interrupts state
   TN_INT_RESTORE();

out:
   return rc;
}

/*
 * Function to be called from ISR
 */
enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout)
{
   TN_INTSAVE_DATA_INT;    //-- define the variable to store interrupt status, 
                           //   it is used by TN_INT_DIS_SAVE()                
                           //   and TN_INT_RESTORE()                           
   enum TN_RCode rc = TN_RC_OK;

   //-- check that function is called from right context
   if (!tn_is_isr_context()){
      rc = TN_RC_WCONTEXT;
      goto out;
   }

   //-- disable interrupts
   TN_INT_IDIS_SAVE();

   //-- perform real job, after all
   rc = _tn_timer_start(timer, timeout);

   //-- restore interrupts state
   TN_INT_IRESTORE();

out:
   return rc;
}

因此,对于几乎所有系统功能,我们都需要像上面这样的包装器。对于作为内核开发人员以及内核用户的我来说,这是一种不便。

唯一的区别是使用了不同的宏:对于任务,这些是TN_INTSAVE_DATA, TN_INT_DIS_SAVE(), TN_INT_RESTORE(); 对于中断,这些是TN_INTSAVE_DATA_INT, TN_INT_IDIS_SAVE(), TN_INT_IRESTORE().

对于支持嵌套中断的平台(ARM、PIC32),这些宏是相同的。对于不支持嵌套中断的其他平台,TN_INTSAVE_DATA_INT,TN_INT_IDIS_SAVE()TN_INT_IRESTORE()扩展为空。所以有点性能优化,但是在我看来成本太高了:更难维护,用起来不太方便,而且代码量变大了。

4

2 回答 2

1

这完全取决于设计和 CPU 功能。我不熟悉任何 PIC,但例如,飞思卡尔(摩托罗拉)MCU(以及许多其他)能够将条件代码寄存器(CCR)移入累加器并返回。这允许人们保存中断启用/禁用掩码的先前状态,并在最后恢复它,而不必担心在应该保持禁用状态的地方(在 ISR 内部)直接启用中断。

然而,要回答哪些平台必须在 ISR 内部和外部以不同的方式执行此操作,需要一个人熟悉所有 ISR,或者至少有一个未通过此测试的平台。如果有一个 CPU 不允许保存和恢复 CCR(如上所述),则别无选择,只能针对每种情况采取不同的做法。

于 2014-10-13T07:07:16.670 回答
0

通常导致调度发生的内核函数具有更简单的 ISR 版本,因为调度程序在从中断返回时运行(通常需要中断结尾来执行此操作),而不是从调度函数本身。

创建一个可以在任何上下文中工作的函数很简单,但是会增加一点开销。然而,不调用不适当的函数所提供的安全性可能是值得的。

例如:

OSStatus semGive( OSSem sem )
{
    return isInterrupt() ? ISR_SemGive( sem ) : OS_SemGive( sem ) ;
}

的实现isInterrupt()依赖于平台,并在安全检测中讨论,如果函数是从 ISR 调用的?

于 2014-10-13T21:10:57.357 回答