Проблема с захватом счетчика
- Войдите на сайт для отправки комментариев
Втр, 02/10/2012 - 16:53
Помогите разобраться с захватом счетчика и измерением времени с точностью до 1 мкс.
volatile unsigned long time, OVFcount; volatile byte Flag; void setup() { pinMode(7,OUTPUT); pinMode(8,INPUT); digitalWrite(7,HIGH); Flag = LOW; time = 15536; OVFcount = 0; TCCR1A = 0; TCCR1B = 0; TIMSK1 = 0; TCCR1B |= ((1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << ICNC1) | (1 << ICES1)); // режим = 12 (CTC+модуль захвата) / делитель = 8 / подавление шума / работа по фронту TIMSK1 |= ((1 << ICIE1) | (1 << TOIE1)); // разрешение прерывания захвата и переполнения TCNT1 = 15536; Serial.begin(115200); } // обработчик прерывания переполнения ISR(TIMER1_OVF_vect) { OVFcount++; // счетчик переполнений (инкремент раз в 25 мс) TCNT1 = 15536; // "ноль" счетчика для того, чтобы прерывание по переполнению срабатывало раз в 25 мс } // обработчик прерывания захвата ISR(TIMER1_CAPT_vect) { time = ICR1; // собственно копипаст значения счетчика из TCNT1 в ICR1, а затем уже в time Flag = HIGH; // флаг необходимости расчета времени в мкс и его вывода } void loop() { if (Flag == HIGH) { Flag = LOW; time = ((time-15536)/2); // кол-во микросекунд в счетчике - от 0 до 24999 мкс (деление на 2 из-за периода между тиками = 0.5 мкс) time = time + OVFcount*25000; // общее время с учетом кол-ва переполнений (раз в 25 мс) Serial.println(time); } }
В итоге печатается какая-то лажа, а не то что надо.
Еще хочу понять в чем отличие захвата от простого чтения значения регистра TCNT1?
Спасибо!
Помогите разобраться с захватом счетчика и измерением времени с точностью до 1 мкс.
[...]
В итоге печатается какая-то лажа, а не то что надо.
Как следует из комментариев, вы выбрали режим 12 работы Т/С1. Согласно даташиту, в этом режиме счетчик изменяется от нуля до значения, записанного в регистре ICR1, после чего сбрасывается ().
Событие захвата приводит к автоматической записи текущего состояния счетчика в регистр ICR1. Стало быть, очередной сброс счетчика произойдет сразу же после события захвата (при следующем инкременте регистр TCNT1 будет содержать значение, большее, чем сохранено в регистре ICR1), а не через 25 мкс (или сколько там осталось до очередного переполнения).
Еще хочу понять в чем отличие захвата от простого чтения значения регистра TCNT1?
Спасибо!
Прочитать значение счетчика TCNT1 вы можете в любой момент. Захват же (и чтение регистра TCNT1 в регистр ICR1) происходит при возникновении события захвата, т.е. в зависимости от выбранного триггера - при изменении логического уровня пина ICP1 (PB0), либо при изменении логического уровня на выходе компаратора AC0 (при изменении соотношения уровней на входах AIN0/AIN1 (PD7/PD6)). Эти события случаются в произвольные моменты времени, соответствующие участки кода (процедуры обработки прерываний) выполняются асинхронно - независимо от вашего (или программы) желания/состояния.
Верно ли я понимаю, что прерывание захвата после строки
надо дополнить строкой
???
Почему? После выполнения ICR1 = TCNT1 по логике идет TCNT = 0, и как после следующего инкремента в TCNT1 может оказаться значение большее, чем в ICR1?
Действовать следует не по логике, а по даташиту. В нем для режима(ов) CTC указано два условия очистки счетчика: достижение значения, записанного в регистре OCR1A (режим 4), либо в регистре ICR1 (режим 12 - ваш случай).
В чем отличие от способа организации чтения значения счетчика (TCNT1) по прерыванию INT0 на соответствующей ноге по тому же фронту, как это сделано в коде примера?
при использовании модуля захвата вам, собственно, никуда не надо торопиться. Главное - успеть прочитать захваченное в регистр ICR1 значение до наступления следующего фронта. В случае INT0/INT1 каждый такт промедления со считыванием значения счетчика будет искажать получаемое значение. То есть, при использовании модуля захвата значительную часть работы за вас делает железо. Практически как в UART, только буфер очень короткий - всего на одно значение.
Верно ли я понимаю, что прерывание захвата после строки
надо дополнить строкой
Можно, конечно, и так изгаляться, но почему бы вместо режима 12 не взять режим 4 и вместо ручной настройки T/C1 на счет в диапазоне от 15536 до 65535 не загрузить в OCR1A значение 50000 и забыть о подбрасывании дров в костер - прерывания будут автоматически щелкать те же самые каждые 25 мс? Единственное - вместо прерывания по переполнению придется использовать прерывание по сравнению канала A (TIMER1_COMPA_vect).
Да просто хотелось понять в чем преимущества в точности расчета периода используя алгоритм захвата, а не просто чтения счетчика когда это надо. :)
Ну так, чтобы прочитать счетчик "когда это надо" необходимо решить, "когда это надо", а потом выполнить ряд действий, каждое из которых может потребовать нескольких тактов процессора. При этом в момент чтения может запуститься прерывание более высокого уровня и дополнительно увеличить длительность считывания. Можно успеть все сделать до очередного изменения значения счетчика TCNT1. А можно и не успеть и он щелкнет - может быть, один раз, а может быть и два (три/четыре/пять).
А захват (одновременное изменение/заполнение регистров TCNT1 и ICR1) происходит на аппаратном уровне, без использования ресурсов процессора, умещается в пару тактов (наверное) и на продолжительность обработки ничто не может повлиять - в отличие от чтения счетчика из программы или через механизм прерываний.
Упростил код до нельзя:
Счетчик переполнений OVFcount все равно = 0. :( Что снова не так?
Предложенный 4 режим (СТС) - не подходит в силу необходимости замера периодов от внешнего датчика с высокой точностью.
Счетчик переполнений OVFcount все равно = 0. :( Что снова не так?
Вы выбрали режим 12 - сброс счетчика при достижении значения, сохраненного в регистре ICR1. Прерывание TIMER1_OVF вызывается при переполнении счетчика, т.е. когда он достигает значения 0xFFFF и переходит через него.
Предложенный 4 режим (СТС) - не подходит в силу необходимости замера периодов от внешнего датчика с высокой точностью.
Почему вы думаете что режим 4 (CTC, сброс счетчика при достижении значения в регистре OCR1A) будет менее точным, чем режим 12 (CTC, сброс счетчика при достижении значения в регистре ICR1)?
В любом случае счетчик переполнений = 0, т.е. вызовов данного прерывания 0!
Почему 0xFFFF если в ICR1 указано 49999 (12 строка)?
Потому что вы используете режим 12 - CTC со сбросом счетчика при достижении значения, сохраненного в регистре ICR1. При достижении значения 49999 происходит сброс счетчика TCNT1 и установка флага OCF1A. Что приводит к генерации прерывания TIMER1_COMPA (если это прерывание разрешено). И все начинается по новой - счетчик считает до 49999 и сбрасывается, считает и сбрасывается, считает... И ни одного переполнения.
В любом случае счетчик переполнений = 0, т.е. вызовов данного прерывания 0!
А с чего бы данному прерыванию генерироваться? Ведь, как сказано выше, шансов у счетчика добраться до значения 0xFFFF и вызвать срабатывание прерывания по переполнению - 0 целых и 0 в периоде.
Ловить вам надо не событие переполнения счетчика, а событие достижения им заданного в регистре ICR1 значения.
Ну так взгляните же в даташит! Или - еще лучше/проще - попробуйте режимом 4 побаловаться! Вдруг получится то, что вам необходимо?
Лично у меня - при чтении соответствующего раздела даташита по диагонали - сложилось впечатление, что модуль захвата срабатывает в любом режиме работы таймера/счетчика. Все что необходимо для этого - поступление соответствующего фронта на выводе ICP или переход компаратора в соответствующее положение. Все это настраивается отдельно от режимов работы.
Записывая при этом текущее содержимое регистра TCNT1 в регистр ICR1 и генеря прерывание TIMER1_CAPT, сообщая, так сказать, основной программе: "кушать подано".