Отметки времени на основе аппаратного таймера со счетчиком переполнения

Я хочу реализовать функцию отметки времени для моей платформы на базе msp430.

Моя цель — использовать аппаратный таймер и подсчитать, сколько раз он переполняется, чтобы сгенерировать длинное значение метки времени (обычно это uint32 для счетчика переполнения в сочетании со значением uint16 аппаратного таймера).

Вот что у меня есть:

  • Счетчик переполнения увеличивается при прерывании,
  • его значение проверяется каждый раз, когда запрашивается метка времени (это защищено блокировкой прерывания).
  • значение счетчика переполнения объединяется с текущим значением аппаратного таймера в большую отметку времени.

У меня возникают проблемы, когда я принимаю во внимание время прерываний.


Первая наивная реализация у меня была:

uint16_t timer_value = timer_value_get();

__istate_t istate = interrupt_disable();
uint64_t overflow_count_local = overflow_count; // the volatile incremented on interrupt
interrupt_restore(istate);

return (overflow_count_local << 16u) + timer_value;    

Это приведет к сбою, когда переполнение произойдет после получения значения таймера, но до того, как прерывания будут отключены. overflow_count_local тогда будет на 1 больше, чем при присвоении timer_value.


Я попытался добавить другие проверки для обнаружения этого возможного прерывания.

uint16_t timer_value = timer_value_get();

__istate_t istate = interrupt_disable();
uint16_t second_timer_value = timer_value_get();

uint64_t overflow_count_local = overflow_count; // the volatile incremented on interrupt
interrupt_restore(istate);

if (second_timer_value < timer_value) {
    // A HW timer overflow occured just before disabling interrupts.
    overflow_count_local--;
}

return (overflow_count_local << 16u) + timer_value;    

Это тоже не сработает, на этот раз потому, что таймер мог переполниться после отключения прерываний, но до назначения second_timer_value. Тогда overflow_count_local будет слишком мало.


Как бы я ни пытался изменить это, кажется, всегда есть случай, который не охвачен. Есть ли известный способ сделать эту работу?

Некоторые ограничения:

  • Таймер используется и для других функций (с захватом/сравнением) и не может быть остановлен.
  • Модуль RTC в msp430 нельзя использовать для этой цели, поскольку он используется для сохранения фактического времени суток.
  • Нет 32-битного таймера.

person Gauthier    schedule 12.02.2014    source источник


Ответы (2)


Следующий алгоритм не блокируется (отключение прерываний не требуется):

Получить переполнение, а затем таймер (в указанном порядке), пока переполнение не будет одинаковым с обеих сторон считывания таймера.

uint32_t hi ;
uint16_t lo ;
do
{
    hi = overflow_count ;
    lo = timer_value_get() ;

} while( hi != overflow_count )

return (hi << 16 ) | lo ; 

Обычно это занимает ноль или самое большее одну итерацию, если только не происходит исключительно долгое переключение контекста на другой поток или прерывание, во время которого таймер снова переполняется.

person Clifford    schedule 12.02.2014
comment
Хороший. Поскольку порядок двух строк в теле цикла имеет значение, я думаю, было бы уместно сделать hi и lo volatiles? overflow_count уже volatile поэтому он там не нужен, но для lo... - person Gauthier; 13.02.2014
comment
@Gauthier: Это не повредит. - person Clifford; 13.02.2014
comment
Просто предупреждение о том, что это будет работать правильно, только если разрешены прерывания (или, точнее, если этот код может быть прерван таймером IRQ). Часто код MSP430 (или любого микроконтроллера) управляется прерываниями, и если это вызывается из обработчика прерываний, где MSP430 глобально отключает прерывания, то у вас будут те же условия гонки, что и в примерах OP (аппаратное переполнение происходит во время выполнения). метки времени, но нельзя вызвать IRQ для увеличения overflow_count, поскольку прерывания отключены). - person dale; 27.08.2019

Может быть, что-то вроде этого. Я не вижу необходимости отключать прерывания при этом.

timer_value_1 = timer_value_get();
overflow_count_snapshot = overflow_count;
timer_value_2 = timer_value_get();
if (timer_value_2 < timer_value_1)
{
    return (timer_value_2 + (overflow_count << 16)); // not the snapshot
}
else
{
    return (timer_value_2 + (overflow_count_snapshot << 16)); // you could use timer_value_1 or 2
}
person kkrambo    schedule 12.02.2014
comment
Дело в том, что я не уверен, что присваивание атомарно, тем более что overflow_count это 32 бита. Если присваивание не является атомарным, а прерывание происходит в его середине, результат может быть ужасным. - person Gauthier; 12.02.2014
comment
Если timer_value_2 ‹ timer_value_1, вы должны игнорировать моментальный снимок. Но вполне вероятно, что прерывание произошло только что, поэтому повторное чтение overflow_count должно быть безопасным (т. е. оно не изменится снова в течение некоторого времени). Я думаю, вы могли бы прочитать overflow_count дважды, чтобы быть уверенным. Если timer_value_2 > timer_value_1, то прерывания не было и overflow_count_snapshot в порядке. - person kkrambo; 12.02.2014
comment
Я понимаю. overflow_count_snapshot может быть ерундой, но если это так, то она все равно не используется. Интересный. - person Gauthier; 12.02.2014