Ниже приведена функция задержки, которую я нашел в нашей прошивке. Это выглядит немного опасно или, по крайней мере, смущает читателя.
Глобальная переменная
static int32u masterTimerCounter; // 32-bit unsigned timer counter
Обработчик прерываний системного тика
/* Called Every Millisecond */
void sysTickIrqHandler(void)
{
masterTimerCounter++;
}
Установка срока действия таймера
void setTimerExpiration(int32u *timerExpiration, int32u delay)
{
*timerExpiration = masterTimerCounter + delay;
}
Проверьте, не истечет ли срок действия таймера
boolean timerExpired(int32u timerExpiration, int32u delay)
{
if((masterTimerCounter - timerExpiration) < delay)
return TRUE; // Timer has expired
else
return FALSE; // Timer still active
}
Устанавливать выключение и блокировку таймера до истечения срока действия таймера
int32u timerExpiration;
setTimerExpiration(&timerExpiration, 15); // Set expiration timer to 15 milliseconds
while(!timerExpired(timerExpiration, 15) // Block until timer has expired
continue;
Вопрос
Как вы можете видеть в timerExpired()
, masterTimerCounter
вычитается timerExpiration
. Если таймер еще не истек, вычисление приведет к очень большому числу (поскольку оба операнда представляют собой непознанные числа). Когда таймер истек, вычисление приведет к значению, меньшему, чем сумма задержки.
Хотя кажется, что это работает нормально, похоже, что это может быть опасно или, по крайней мере, ввести в заблуждение читателя (я должен был прочитать его несколько раз, чтобы понять первоначальное намерение программиста).
Если бы мне пришлось написать что-то похожее на это, я бы определил функцию timerExpired
следующим образом:
boolean timerExpired(int32u timerExpiration)
{
if(timerExpiration > masterTimerCounter)
return FALSE; // Timer still active
else
return TRUE; // Timer has expired
}
Должен ли я переопределить "timerExpired()"?
Примечание. Имена функций и переменных были изменены для защиты невинных.
Проблема с вашим путем заключается в том, что если masterTimerCounter + delay
вызывает опрокидывание 32-битного int, то тест timerExpired
проходит сразу.
Я думаю, что самый простой способ делать целочисленные таймеры при наличии возможного опрокидывания выглядит так:
void startTimer(int32u *timerValue)
{
*timerValue = masterTimerCounter;
}
Проверьте, не истечет ли срок действия таймера
boolean timerExpired(int32u timerVal, int32u delay)
{
if ((masterTimerCounter - timerVal) >= delay)
return TRUE; // Timer has expired
else
return FALSE; // Timer still active
}
Применение:
int32u timer;
startTimer(&timer); // Start timing
while(!timerExpired(timer, 15) // Block for 15 ticks
continue;
Даже если вычитание в timerExpired
возвращает правильные результаты.
Обратите внимание, что исходная логика была чем-то вроде: это абсолютное время истечения в прошлом, но меньше, чем полная задержка. Может быть, мы могли бы выразить это свободно, как недавно этот огонь по таймеру.
Ваша модифицированная логика - это просто абсолютное время истечения в прошлом, которое отличается.
Вы можете тривиально избегать риска timerExpiration
просто добавляя timerExpiration
к каждой стороне неравенства:
boolean timerExpired(int32u timerExpiration, int32u delay)
{
// WAS: (masterTimerCounter - timerExpiration) < delay
if(masterTimerCounter < timerExpiration + delay)
return TRUE; // Timer has expired
else
return FALSE; // Timer still active
}
но это изменяет поведение, так как вы говорите, что оригинал всегда будет ложным, если masterTimerCounter < timerExpiration
. Вы можете получить исходное поведение без путаного нижнего потока, явно указав это:
boolean timerExpired(int32u timerExpiration, int32u delay)
{
if(masterTimerCounter > timerExpiration && // did it expire ...
masterTimerCounter < timerExpiration + delay) // ... recently?
return TRUE; // Timer has expired
else
return FALSE; // Timer still active
}
Этот код прошивки не имеет смысла.
int32u expire;
setTimerExpiration(&expire, 0);
timerExpired(expire, 0); // is always false, unless the timer overflows
delay
тогда?timerExpired
с задержкой в качестве параметра здесь не имеет смысла.