为了澄清一个问题,假设我们有:
- 静态按钮对象:
static Button_T sButton = {0};
- 获取按钮的函数:从主循环上下文
void GetButton(Button_T * p_button);
调用 - ISR 处理程序:
void ButtonISRHandler(void);
假设:
GetButton
执行可以被任何不执行的中断中断ButtonISRHandler
ButtonISRHandler
执行可以被其他中断中断GetButton
执行所需的时间少于两次ButtonISRHandler
中断调用之间的最短时间。- 按钮中断是例如每 10 毫秒触发一次的循环中断。
- 在
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
功能