便携式ISR安全数据传递

时间:2017-11-21 15:52:09

标签: c thread-safety embedded interrupt isr

在嵌入式C中,我正在尝试创建一种通用的方法来安全地将值从我的ISR(前景)传递到我的主循环(背景)。 “通用”我的意思是我不想暂停中断(因为这是编译器/ CPU特定的,可能有副作用),只想用繁忙的标志等来做。对于这个特殊的机制,我不需要一个队列,我只想检索ISR报告的最新值。

所以我使用的模式是一个结构和一些在结构上运行的函数。问题当然是“写”功能是基于ISR的,并且可以随时中断“读取”功能,我想消除数据损坏的可能性。这种方法是一个双插槽系统,还有几个忙标志。

会起作用吗?和/或有更简单的方法吗? (记住这是嵌入式C,我试图通用/便携。)谢谢!

typedef struct 
{
    uint8_t busy;
    int32_t valueA; 
    int32_t valueB;     
    uint8_t reading_from_A;
    uint8_t last_wrote_to_B;
} sSafeI32_Fore2Back;

void SafeI32_InitFore2Back(sSafeI32_Fore2Back * si)
{
    si->busy = 0;    
    si->valueA = 0;
    si->valueB = 0;
    si->reading_from_A = 0;
    si->last_wrote_to_B = 1;
}

int32_t SafeI32_ReadFromBack(sSafeI32_Fore2Back * si)
{
    int32_t rtn;

    si->busy = 1;
    if (si->last_wrote_to_B)
    {
        rtn = si->valueB;
    }
    else
    {
        si->reading_from_A = 1;
        rtn = si->valueA;
        si->reading_from_A = 0;
    }
    si->busy = 0;

    return rtn;
}

void SafeI32_WriteFromFore(sSafeI32_Fore2Back * si, int32_t v)
{
    if (si->busy == 0)
    {
        si->valueA = v;
        si->last_wrote_to_B = 0;      
    }
    else
    {
        if (si->reading_from_A)
        {
            si->valueB = v;
            si->last_wrote_to_B = 1;
        }
        else
        {
            si->valueA = v;
            si->last_wrote_to_B = 0;            
        }
    }    
}

2 个答案:

答案 0 :(得分:0)

这样做的一种简单,常见的方法是使读取功能确保在读取期间不被抢占。 ISR每次执行时都会更新一个计数器,并且读取功能可确保计数器在访问期间不会发生变化。如果计数器确实发生了变化,则再次执行读取。

typedef struct
{
    int32_t value;
    uint32_t counter;
} sSafeI32_Fore2Back;

void SafeI32_InitFore2Back(volatile sSafeI32_Fore2Back * si)
{
    si->value = 0;
    si->counter = 0;
}

int32_t SafeI32_ReadFromBack(volatile sSafeI32_Fore2Back * si)
{
    int32_t rtn;
    uint32_t ctr;

    do {
        ctr = si->counter;
        rtn = si->value;
    } while(ctr != si->counter);

    return rtn;
}

void SafeI32_WriteFromFore(sSafeI32_Fore2Back * si, int32_t v)
{
    si->value = v;
    si->counter++;
}

答案 1 :(得分:0)

既然你知道中断不能轮流中断(除非你有一些特殊的异国情况),这很简单:

static volatile bool changed = false;
static uint32_t value;

void isr (void) // it is an interrupt; it can't be interrupted by the caller
{
  changed = true;
  value = something;
}

void caller (void)
{
  ...
  changed = false;
  uint32_t val = value;
  if(changed)
  {
    // handle error, discard val
  }
}

changed变量甚至不需要原子访问。

但是请注意,ISR和调用者之间共享的所有变量必须是volatile,否则可能会出现各种奇怪且非常微妙的优化错误。