不禁用Cortex M0上的中断的同步机制

时间:2019-03-05 12:59:42

标签: c embedded stm32 synchronisation

为澄清一个问题,我们假设:

  1. 静态按钮对象:static Button_T sButton = {0};
  2. 获取按钮的功能:void GetButton(Button_T * p_button);是从主循环上下文
  3. 调用的
  4. ISR处理程序:void ButtonISRHandler(void);

假设:

  1. GetButton的执行可以被任何不执行ButtonISRHandler的中断中断。
  2. ButtonISRHandler的执行可能会被其他迭代中断
  3. GetButton执行所需的时间少于两次ButtonISRHandler中断调用之间的最短时间。
  4. 按钮中断是例如每10毫秒触发一次的循环中断
  5. ButtonISRHandler中,我们执行诸如检查按钮PIN状态或检测按钮是否被触摸(在触摸按钮情况下)之类的过程。如果给定的PIN状态稳定,例如对于5个后续调用,则sButton对象状态将更新。
  6. Button_T是通用对象-可以是经典的轻触开关或触摸按钮等。
  7. ScanButtonAndUpdate可以处理Button_T对象的列表,但是GetButton函数仅对一个按钮对象起作用。

问题是:在程序计数器位于GetButton内时可能发生中断的经典情况

问题是:如何在不禁用中断的情况下将GetButtonButtonISRHandler同步?

我的目标处理器是没有LDREX / STREX操作的Cortex M0,因此我不能使用C11中的原子,在这种情况下这将是一个很好的解决方案。

我的建议解决方案

使用GetButton中的关键部分。

如果程序计数器在关键部分内部时发生中断,则不要在中断中处理ScanButtonAndUpdate,而要在ExitCriticalSection上对其进行处理。推迟执行ScanButtonAndUpdate

不可能从中断和主上下文中同时调用ScanButtonAndUpdate函数-这种行为受到信号量的保护

实施

#define SEMAPHORE_GIVEN                             0
#define SEMAPHORE_TAKEN                             1

typedef uint32_t BaseType_T;
typedef struct Button_T;

static volatile BaseType_T sSemaphore = SEMAPHORE_GIVEN;
static volatile bool sIsPendingISR = false;
static volatile Button_T sButton = {0};

void GetButton(Button_T * p_button)
{
    EnterCriticalSection();

    memcpy(p_button, &sButton, sizeof(Button_T))
    /* Other procedures on sButton... */

    ExitCriticalSection();
}

/* Cyclic executed handler */
void ButtonISRHandler(void)
{
    if (!BinarySemaphoreTake()) {
        SetISRPending();
    }
    else {
        ScanButtonAndUpdate();

        BinarySemaphoreGive();
    }
}

void ScanButtonAndUpdate(void)
{
    /* Scan for instance a current PIN state and update sButton object
       if state is stable in next calls */
}

static void EnterCriticalSection(void)
{
    while(false == BinarySemaphoreTake()) continue;
}

static void ExitCriticalSection(void)
{
    BinarySemaphoreGive();

    if (IsPendingISR()){
        ScanButtonAndUpdate();
        ResetISRPending();
    }
}

static bool BinarySemaphoreTake(void)
{
    if (SEMAPHORE_GIVEN == sSemaphore) {
        /* Value Store operation is atomic on the architecture native type */
        sSemaphore = SEMAPHORE_TAKEN;
        return true;
    }
    else {
        return false;
    }
}

static void BinarySemaphoreGive(void)
{
    sSemaphore = SEMAPHORE_GIVEN;
}

static void SetISRPending(void)
{
    sIsPendingISR = true;
}

static void ResetISRPending(void)
{
    sIsPendingISR = false;
}

static bool IsPendingISR(void)
{
    return sIsPendingISR;
}

此解决方案已经过测试,可以很好地工作,不会出现问题,但是我不确定这是没有隐藏错误的最佳解决方案。

编辑1::更新了假设并添加了缺少的ScanButtonAndUpdate功能

1 个答案:

答案 0 :(得分:2)

存在一个隐藏的同步,该同步会影响您是否具有竞态条件:是由哪个门来控制中断的?两种最常见的情况是边沿触发和电平触发。边沿触发意味着在清除设备之前将禁止中断,而电平触发则意味着中断将反复重新声明直到设备被清除。

如果您的代码使用级别触发的中断,则您完全省略了此同步,或者您假装sIsPendingISR是掩码和状态标志。在那种情况下,你看起来还不错

如果它是级别触发的,则它可以在 / *更新sButton对象* / 期间重新断言,从而导致设备处理代码在两个上下文中执行(中断+正常)。大多数设备代码并非旨在执行此操作。

顺便说一句,有一个称为“ Dekkers算法”的软件协议,它为不具有硬件支持的互斥提供了一种通用解决方案。您已经在这里集成了它的一个版本。