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

标签 c embedded stm32 synchronization

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

  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 同步?

我的目标处理器是 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 是掩码和状态标志。那样的话,你看起来还不错

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

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

关于c - Cortex M0 上无需禁用中断的同步机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55003417/

相关文章:

linux - STM32F0-发现: no tty

c - C 函数和指针的问题

c++ - 使用c或c++解码红外遥控代码

embedded - 为什么您需要可编程实时单元 (PRU) 而您可以拥有 RTOS?

winapi - RS232(Com 端口)RTS/CTS/DTR/DSR 的低级控制可能吗?

c - HAL_UART_Transmit发送错误数据

c - 为 n 个 cpu 周期添加延迟/什么都不做的最佳方法

c - 不打印时变量地址不对齐

c - Typedef 到双整数数组

c - 将字符存储在 EEPROM 中并回显