質問を明確にするために、次のものがあると仮定します。
- 静的ボタン オブジェクト:
static Button_T sButton = {0};
- Button を取得する関数:メイン ループ コンテキスト
void GetButton(Button_T * p_button);
から呼び出される - ISR ハンドラ:
void ButtonISRHandler(void);
仮定:
GetButton
実行は、実行されない割り込みによって中断される可能性がありますButtonISRHandler
ButtonISRHandler
実行は他の割り込みによって中断される可能性がありますGetButton
ButtonISRHandler
実行にかかる時間は、2 つの割り込み呼び出し間の最小時間よりも短くなります。- ボタン割り込みは、たとえば 10 ミリ秒ごとにトリガーされる周期的な割り込みです。
ButtonISRHandler
ボタンの PIN 状態の確認や、ボタンがタッチされたかどうかの検出 (タッチ ボタンの場合) などの手順があります。特定の PIN 状態が、たとえば 5 回の連続した呼び出しで安定している場合、sButton
オブジェクトの状態が更新されます。Button_T
は一般的なオブジェクトです。従来のタクト スイッチやタッチ ボタンなどの可能性があります。ScanButtonAndUpdate
Button_T オブジェクトのリストを処理できますが、GetButton
関数は 1 つのボタン オブジェクトに対してのみ動作します。
問題は次のとおりです。プログラムカウンターが内部にあるときに割り込みが発生する可能性がある古典的なケース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;
}
このソリューションはテスト済みで、問題なく動作しますが、これが隠れたバグのない最適なソリューションであるかどうかはわかりません。
EDIT 1:仮定を更新し、不足しているScanButtonAndUpdate
機能を追加しました