Для уточнения вопроса предположим, что имеем:
- Объект Static Button:
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
без отключения прерываний ?
Моим целевым процессором является 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
функция