为澄清一个问题,我们假设:
static Button_T sButton = {0};
void GetButton(Button_T * p_button);
是从主循环上下文 void ButtonISRHandler(void);
假设:
GetButton
的执行可以被任何不执行ButtonISRHandler
的中断中断。ButtonISRHandler
的执行可能会被其他迭代中断GetButton
执行所需的时间少于两次ButtonISRHandler
中断调用之间的最短时间。ButtonISRHandler
中,我们执行诸如检查按钮PIN状态或检测按钮是否被触摸(在触摸按钮情况下)之类的过程。如果给定的PIN状态稳定,例如对于5个后续调用,则sButton
对象状态将更新。Button_T
是通用对象-可以是经典的轻触开关或触摸按钮等。ScanButtonAndUpdate
可以处理Button_T对象的列表,但是GetButton
函数仅对一个按钮对象起作用。问题是:在程序计数器位于GetButton
内时可能发生中断的经典情况
问题是:如何在不禁用中断的情况下将GetButton
与ButtonISRHandler
同步?
我的目标处理器是没有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
功能
答案 0 :(得分:2)
存在一个隐藏的同步,该同步会影响您是否具有竞态条件:是由哪个门来控制中断的?两种最常见的情况是边沿触发和电平触发。边沿触发意味着在清除设备之前将禁止中断,而电平触发则意味着中断将反复重新声明直到设备被清除。
如果您的代码使用级别触发的中断,则您完全省略了此同步,或者您假装sIsPendingISR是掩码和状态标志。在那种情况下,你看起来还不错
如果它是级别触发的,则它可以在 / *更新sButton对象* / 期间重新断言,从而导致设备处理代码在两个上下文中执行(中断+正常)。大多数设备代码并非旨在执行此操作。
顺便说一句,有一个称为“ Dekkers算法”的软件协议,它为不具有硬件支持的互斥提供了一种通用解决方案。您已经在这里集成了它的一个版本。