Таймер 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 и генерируем множество прерываний)...

Будем признательны за любую информацию о том, почему порядок инициализации важен в этом случае.

Заранее спасибо! Таблица данных Arduino 20-6

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;               // и сброс на следующую секунду.
} 

, 👍1


1 ответ


1

Как описано в части эксплуатации раздела спецификации, OC1x имеет двойную буферизацию; код устанавливает теневой буфер на заданное значение, а затем в какой-то момент оборудование копирует теневой буфер в регистр, который управляет работой. «Немедленно» означает, что буфер полностью прозрачен, поскольку значение копируется немедленно.

Разница между режимами 4 и 12 заключается в том, что режим 4 позволяет использовать ICP1 для захвата входного сигнала, а режим 12 позволяет использовать OC1A для генерации формы сигнала.

,

Спасибо за ответ, но мне все еще не совсем ясно. Режим 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