首页 > 解决方案 > 在 Cortex M0 上不禁用中断的同步机制

问题描述

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

  1. 静态按钮对象:static Button_T sButton = {0};
  2. 获取按钮的函数:从主循环上下文void GetButton(Button_T * p_button);调用
  3. 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功能

标签: cembeddedstm32synchronisation

解决方案


有一个隐藏的同步会影响您是否有竞争条件:中断是什么门?最常见的两种情况是边沿触发和电平触发;边沿触发意味着中断将被禁止,直到设备被清除,而电平触发意味着中断将反复重新断言,直到设备被清除。

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

如果是电平触发,那么它可以在/* Update sButton object */期间重新断言,导致设备处理代码在两个上下文(中断 + 正常)中执行。大多数设备代码都不是为此而设计的。

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


推荐阅读