Поскольку 8-битный MCU не может прочитать весь 16-битный таймер за один цикл, это создает состояние гонки, при котором младшее слово может переключаться между чтениями. Есть ли у сообщества предпочтительный способ избежать этих условий гонки? В настоящее время я рассматриваю возможность остановки таймера во время чтения, но я хотел бы знать, есть ли более элегантное решение.
К вашему сведению, это для PIC16F690 .
Уинделл прав, если вы говорите о PIC, техническое описание (и аппаратное обеспечение) уже справится с этим за вас.
Если вы не используете PIC, я использую общий метод:
byte hi, lo;
word timer_value;
do {
hi = TIMER_HI;
lo = TIMER_LO;
} while(hi != TIMER_HI);
timer_value = (hi << 8) | lo;
Что это делает, так это считывает старший байт, за которым следует младший байт, и продолжает делать это до тех пор, пока старший байт не изменится. Это легко обрабатывает случай, когда младший байт 16-битного значения переполняется между чтениями. Предполагается, конечно, что многократное чтение регистра TIMER_HI не вызывает побочных эффектов. Если ваш конкретный микропроцессор этого не позволяет, пришло время выбросить его и использовать тот, который не настолько безмозглый. :-)
Этот метод ТАКЖЕ предполагает, что ваш таймер не меняется так быстро, что вы рискуете переполнить младшие 8 битов в течение одного или двух циклов выборки процессора. Если вы запускаете таймер так быстро (или микропроцессор так медленно), то пришло время переосмыслить вашу реализацию.
Проверьте свой техпаспорт! Об этом будет рассказано в кровавых подробностях для вашего конкретного чипа.
Я не уверен, что это верно для всех 16-битных регистров счетчиков/таймеров на всех PIC (может быть, кто-то еще может ответить на это!), но, по крайней мере, для PIC 18F, который я использую, в техническом описании конкретно говорится о том, как это обрабатывается. .
Когда вы читаете младший байт, он буферизует старший байт во временный регистр для старшего байта, так что, когда вы читаете следующий старший байт, он дает вам полный мгновенный снимок значения таймера.
Тот же базовый процесс используется в AVR (и, конечно, в Arduino), а также в большинстве других случаев, когда вам нужно одновременно читать или записывать двухбайтовые регистры на 8-битных микроконтроллерах.
Текст, на который вы ссылаетесь в своем комментарии (раздел 6.5.1), относится к работе асинхронного таймера, когда таймер синхронизируется с источником, внешним по отношению к микроконтроллеру.
Заявление об отказе от ответственности предлагает запускать и останавливать таймер, потому что общий метод Эндрю (на самом деле любой метод) не будет работать, потому что может быть много тиков (даже более 256) внешних часов и, следовательно, таймер в одном цикле процессора . Я предполагаю, что вы не работаете с этим крайним случаем.
Если вместо этого вы используете внутренний осциллятор в качестве эталона, все будет в порядке. Даже с предварительным делителем, равным 1, у вас есть до 255 циклов после считывания старшего байта для чтения младшего байта. Это должно быть 2 цикла операции. Вам по-прежнему нужен цикл do/while на случай, если ваш старший байт изменится (в чем разница между «привет» и «TIMER_HI», если значение «lo» равно 0?) между этими временами. С предварительным делителем 2 или выше вам больше не нужен этот тест. Только с предделителями намного меньше 1 это становится проблемой, а без внешних часов это невозможно.
В Справочном руководстве по семейству PIC Mid-Range MCU есть пример чтения и записи таймера 1 в режиме асинхронного счетчика.
Пример. Чтение 16-битного автономного таймера
; All interrupts are disabled
MOVF TMR1H, W ; Read high byte
MOVWF TMPH ;
MOVF TMR1L, W ; Read low byte
MOVWF TMPL ;
MOVF TMR1H, W ; Read high byte
SUBWF TMPH, W ; Sub 1st read with 2nd read
BTFSC STATUS,Z ; Is result = 0
GOTO CONTINUE ; Good 16-bit read
;
; TMR1L may have rolled over between the read of the high and low bytes.
; Reading the high and low bytes now will read a good value.
;
MOVF TMR1H, W ; Read high byte
MOVWF TMPH ;
MOVF TMR1L, W ; Read low byte
MOVWF TMPL ;
; Re-enable the Interrupt (if required)
CONTINUE ; Continue with your code
Дэвидкари
Стивенвх