Таймер Arduino в режиме ctc 4 против 14
Я пытаюсь понять работу таймера и прерываний Arduino, особенно режимов CTC 4 и 12. Я написал небольшой пример, в котором я генерирую прерывания, пока основной цикл спит в течение 1 секунды, и отображаю количество прерываний. произошло. Он может переключаться между режимами 4 и 12 с помощью #if 1.
Это отлично работает для режима 12 (50 прерываний в секунду), но в режиме 4 он дает -12 тыс. прерываний в секунду... если только я снова не установлю OCR1A на 39999 внутри ISR, тогда это действительно даст ожидаемые 50 прерываний в секунду. , но хотелось бы знать почему.
Я думаю, что мое замешательство может быть связано с непониманием заголовка таблицы, объясняющего режимы, в которых написано «Обновление OCR1x в». Что под этим подразумевается (кто его обновляет и до какого значения? или мне нужно обновить его в ISR)? Почему OCR1x обновляется в режиме 12 (в таблице, кажется, указано, что это так)? И вроде работает, даже если я его не обновляю...?
Кроме того, единственная разница между режимами 4 и 12, по-видимому, заключается в регистре, используемом для сравнения со значением таймера (OCR1A против ICR1)... но что же такого фундаментального различия между ними? Почему существует 2 варианта и каков аргумент в пользу выбора режима 4 или 12?
Обновление: похоже, проблема связана с порядком инициализации. Если OCR1A установлен на 39999 в текущем местоположении, он сбрасывается на 0 несколькими строками дальше. При его перемещении после TCCR1A = TCCR1B = 0; тогда OCR1A сохранит значение. Поэтому нет необходимости устанавливать его при каждом прерывании. Это также объясняет, почему мы получаем счетчик -12k в режиме 4 (потому что мы сравниваем OCR1A в этом режиме, сравниваем с 0 и генерируем множество прерываний)...
Будем признательны за любую информацию о том, почему порядок инициализации важен в этом случае.
Заранее спасибо!
volatile int isr1 = 0;
ISR(TIMER1_COMPA_vect)
{
// это вызывается каждый раз, когда наше сравнение таймеров соответствует ICR1 или OCR1A
isr1++;
// при использовании части #if ниже нам нужно это сделать, иначе мы получим странные значения для isr1 (около -12.000)
// Если мы установим исходное значение, мы получим 50 прерываний в секунду...
OCR1A= 39999;
}
void setup() {
Serial.begin(115200);
delay(50);
ICR1 = 39999; // сигнал 50 Гц @ тактовая частота 16 МГц
OCR1A= 39999;
//TIMSK1 |= 1 << МКПП; // бит 5: Разрешение прерывания захвата ввода (выполнение прерывания, когда установлен флаг TIFR1.ICF)
TIMSK1 |= 1 << OCIE1A; // бит 4: включить прерывание Timer-Output-CompareA-Match
TCCR1A = TCCR1B = 0; // сбрасываем флаги конфигурации таймера
// Прерывание может быть сгенерировано каждый раз, когда значение счетчика достигает значения TOP, используя флаг OCF1A или ICF1,
// в зависимости от фактического режима CTC. Если прерывание разрешено, процедуру обработчика прерывания можно использовать для обновления значения TOP.
#if 1
// Техническое описание Arduino 20.12.2. : Значение счетчика (TCNT1) увеличивается до тех пор, пока не произойдет совпадение сравнения с OCR1A, а затем TCNT1 очищается.
TCCR1B |= 1<<WGM12; // код 4 (CTC, сравнение с OCR1A, немедленное обновление OCR1x, переполнение при MAX)
#else
// Техническое описание Arduino 20.12.2. : Значение счетчика (TCNT1) увеличивается до тех пор, пока не произойдет совпадение сравнения с ICR1, а затем TCNT1 очищается.
TCCR1B |= 1<<WGM13 | 1<<WGM12; // код 12 (CTC, сравнение с ICR1, немедленное обновление OCR1x(?), переполнение при MAX)
#endif
// использование прескалера 8, который «замедляет» ход часов в 8 раз.
// 1 такт занимает 0,5 микросекунды, поэтому для 39999 тактов требуется 19999,5 микросекунд
// 1 секунда = 1.000.000 микросекунд, то есть мы получаем 50 прерываний в секунду
TCCR1B |= 1<<CS11; // таймер CS11 = clki/o/8, 1 тик каждые 0,5 микросекунды
}
void loop()
{
delay(1000); // ждем 1 секунду
Serial.println(isr1); // выводим, сколько прерываний было у нас за эту секунду
isr1 = 0; // и сброс на следующую секунду.
}
@kalmiya, 👍1
1 ответ
Как описано в части эксплуатации раздела спецификации, OC1x
имеет двойную буферизацию; код устанавливает теневой буфер на заданное значение, а затем в какой-то момент оборудование копирует теневой буфер в регистр, который управляет работой. «Немедленно» означает, что буфер полностью прозрачен, поскольку значение копируется немедленно.
Разница между режимами 4 и 12 заключается в том, что режим 4 позволяет использовать ICP1 для захвата входного сигнала, а режим 12 позволяет использовать OC1A для генерации формы сигнала.
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- 4-битный счетчик вверх и вниз
- Включить и отключить отдельные прерывания
- Измерить количество циклов и время цифрового входа
- Проблема прерывания библиотеки MPU6050 Arduino Jeff Rowberg
- генерировать два сдвинутых по фазе ШИМ-импульса, запускаемых внешним сигналом с частотным разделением, с помощью Arduino uno?
- Включение прерывания Timer1 CompareA мгновенно вызывает прерывание?
- Прерывания TIMER1 CTC не работают с avr-gcc
Спасибо за ответ, но мне все еще не совсем ясно. Режим 4 сравнивается с OCR1A, режим 12 — с ICR1. При выводе «OCR1A» на последовательный вывод в каждом цикле OCR1A устанавливается в 0 (для режимов 4 и 12), хотя он был инициализирован как 39999. Это объясняет, почему в режиме 4 (используя OCR1A для сравнения) результат -12423. Таймер сравнивается с 0 и, по сути, мгновенно получает попадание и запускает следующее прерывание, переполняя isr1. Но почему/где OCR1A устанавливается на ноль. Я не могу найти описание этого поведения в спецификации., @kalmiya
Какое устройство вы используете?, @Ignacio Vazquez-Abrams
Arduino-Uno (китайский), @kalmiya
Итак, ATmega328P?, @Ignacio Vazquez-Abrams
При загрузке: "avrdude: Подпись устройства = 0x1e950f (вероятно m328p)", так что да... ах, там также написано "Часть AVR: ATmega328P", @kalmiya
Ну, я не вижу ничего в коде или спецификации, что бы это объясняло, и я не могу проверить код самостоятельно. Возможно, что MCU на самом деле подделка, но я не могу этого доказать., @Ignacio Vazquez-Abrams
Давайте [продолжим это обсуждение в чате](http://chat.stackexchange.com/rooms/63073/discussion-between-kalmiya-and-ignacio-vazquez-abrams)., @kalmiya
Хех, похоже, это связано с порядком инициализации, если мы установим OCR1A немного позже, то все в порядке. Так что мне не нужно устанавливать его каждый раз в ISR, а просто "позже" при init - по какой-то причине... При перемещении после TCCR1A = TCCR1B = 0; тогда OCR1A сохраняет значение., @kalmiya