Значения, отображаемые в отладчике:
- IL_TimNow = 865280
- lastReceivedLed = 865055
- timeSinceLast = 4294967265
Обратите внимание, что 4294967265 - это то, что вы получаете, когда конвертируете -31 в uint32_t
.Это говорит о том, что значение IL_TimNow
, возвращаемое IL_TimTimeNow()
непосредственно перед вычитанием, на самом деле было lastReceivedLed - 31
, что составляет 865055 - 31, что составляет 865024.
Показана разница между значением IL_TimNow
в отладчике (865280), а значение IL_TimNow
непосредственно перед вычитанием (865024) равно 256. Кроме того, все младшие 8 битов обоих значений равны нулю.Это говорит о том, что значение считывалось так же, как младший байт округлялся до 0, а следующий байт увеличивался.В комментарии в IL_TimTimeNow()
написано // Incremented in timer ISR
.Поскольку 8-разрядный микроконтроллер может считывать только один байт за раз, кажется, что таймер ISR произошел, когда функция считывала четыре байта IL_TimNow
.
Существует два способа решенияпроблема.Первый способ - отключить прерывание таймера в IL_TimTimeNow()
, пока читается значение IL_TimNow
.Таким образом, функция IL_TimTimeNow()
может быть изменена на что-то вроде этого:
uint32_t IL_TimTimeNow(void)
{
uint32_t curTime;
disable_timer_interrupt();
curTime = IL_TimNow;
enable_timer_interrupt();
return curTime;
}
Однако вам нужно будет проверить, что отключение прерывания по таймеру только временно приводит к задержке прерывания и не пропускается полностью (в противном случаевы потеряете отметки времени).
Другой способ решения проблемы - продолжать читать IL_TimNow
в IL_TimTimeNow()
, пока не получите два одинаковых значения.Таким образом, функция IL_TimTimeNow()
может быть изменена на что-то вроде этого:
uint32_t IL_TimTimeNow(void)
{
uint32_t prevTime, curTime;
curTime = IL_TimNow;
do
{
prevTime = curTime;
curTime = IL_TimNow;
} while (curTime != prevTime);
return curTime;
}
Обычно будет одна итерация цикла do ... while
, читая IL_TimNow
дважды.Время от времени будет две итерации цикла, читая IL_TimNow
три раза.На практике я не ожидал бы более двух итераций цикла, но функция может справиться и с этим.
Менее безопасная, но, возможно, немного более быстрая версия выше будет читать только IL_TimNow
дважды, если младший байт равен 0:
uint32_t IL_TimTimeNow(void)
{
uint32_t curTime;
curTime = IL_TimNow;
if ((curTime & 0xFF) == 0)
{
// Least significant byte possibly just wrapped to 0
// so remaining bytes may be stale. Read it again to be sure.
curTime = IL_TimNow;
}
return curTime;
}
Если производительность не является проблемой, используйте одну из более безопасных версий.