繁体   English   中英

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

[英]Synchronisation mechanism without disabling interrupts on 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执行可以被其他的 iterrupts 打断
  3. GetButton执行所需的时间少于两次ButtonISRHandler中断调用之间的最短时间。
  4. 按钮中断是一个循环中断,例如每 10 毫秒触发一次。
  5. ButtonISRHandler我们有一些程序,例如检查按钮 PIN 状态或检测按钮是否被触摸(在触摸按钮情况下)。 如果给定的 PIN 状态稳定,例如 5 次连续调用,则更新sButton对象状态。
  6. Button_T是通用对象 - 它可以是经典的轻触开关或触摸按钮等。
  7. ScanButtonAndUpdate可以处理一系列 Button_T 对象,但GetButton函数仅对一个按钮对象进行操作。

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

问题是:如何在GetButton中断的情况下GetButtonButtonISRHandler同步?

我的目标处理器是 Cortex M0,没有 LDREX/STREX 操作,所以我不能使用 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函数

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM