Прерывание сравнения Timer2 не работает должным образом

Чтобы разобраться с этим в первую очередь: я уже посмотрел на "Timer2 does not works as it should" и "Timer2 “Clear OC2B on Compare Match” not working as expected in CTC mode", но не нашел ответы там особенно полезными.

Теперь перейдем к моей проблеме:

Я пытаюсь использовать Timer2 в режиме CTC, чтобы прерывание происходило каждые 500 мкс. Для этого у меня есть следующий код:

#include "TimerTwo.h"

TimerTwo Timer2;

ISR(TIMER2_COMPA_vect) { //ISR при сравнении совпадений A
  Timer2.isrCallback();
}

void TimerTwo::initialize() {
  cli();
  TCCR2A = 0;// установить весь регистр TCCR2A в 0
  TCCR2B = 0;// то же самое для TCCR2B
  TCNT2  = 0;//инициализируем значение счетчика до 0

  // установить регистр сравнения для приращения 2 кГц
  OCR2A = 124;// = (16*10^6) / (2000*64) - 1 (должно быть < 256)

  // включить режим CTC
  TCCR2A |= _BV(WGM21);

  // Установить бит CS22 для 64-кратного предварительного делителя
  TCCR2B |=  _BV(CS22);

  // включить прерывание сравнения таймера
  TIMSK2 |= _BV(OCIE2A);
  sei();
}

void TimerTwo::attachInterrupt(void (*isr)()) {
  TIMSK2 |= _BV(OCIE2A);
  isrCallback = isr;
}

void TimerTwo::detachInterrupt() {
  TIMSK2 &= ~_BV(OCIE2A);
  isrCallback = nullptr;
}

и

#ifndef TIMERTWO_H
#define TIMERTWO_H

#include <Arduino.h>

class TimerTwo
{
  public: //методы
    void initialize();
    void attachInterrupt(void (*isr)());
    void detachInterrupt();
    void (*isrCallback)();
};

extern TimerTwo Timer2;

#endif

Вдохновением послужила библиотека TimerOne.

Однако, когда я измеряю период на выходном выводе (который я подключил внутри какого-то другого кода), я получаю только период 1 мс. Даже если я устанавливаю ORC2A на 63, этот факт не меняется.

У меня есть конструктор, который вызывается внутри моего setup(), поэтому регистры не должны перезаписываться при подключении.

Я также посмотрел техническое описание ATmega328p и проверил, что все регистры установлены правильно.

Заранее спасибо за помощь. Если вам понадобится дополнительная информация, дайте мне знать.

, 👍1


1 ответ


Лучший ответ:

1

Так что вы правы, CTC ведет себя странно. Я тоже замечал это несколько лет назад на другой плате, но я всегда подозревал асинхронный режим (я добавил 32k RTC Xtal на Arduino Mega).

Однако режим Fast PWM с OCR2A в качестве значения TOP, похоже, работает:

class TimerTwo
{
  public: //методы
    void initialize();
    void attachInterrupt(void (*isr)());
    void detachInterrupt();
    void (*isrCallback)() = nullptr;
};

TimerTwo Timer2;

ISR(TIMER2_COMPA_vect) { //ISR при сравнении совпадений A
  Timer2.isrCallback();
}

void TimerTwo::initialize() {
  cli();

  OCR2A = 124;                        // = (16*10^6) / (2000*64) - 1 (должно быть < 256)
  TCCR2A = _BV(WGM21) | _BV(WGM20);   // включаем быстрый режим ШИМ с верхним в OCR2A
  TCCR2B = _BV(WGM22) | _BV(CS22);    // Установить бит CS22 для 64-кратного предварительного делителя
  TCNT2  = 0;                         // инициализируем значение счетчика на 0

  sei();
}

void TimerTwo::attachInterrupt(void (*isr)()) {
  isrCallback = isr;
  TIMSK2 |= _BV(OCIE2A);
}

void TimerTwo::detachInterrupt() {
  TIMSK2 &= ~_BV(OCIE2A);
  isrCallback = nullptr;
}

volatile uint32_t counter = 0;
void isrHandler() {
  ++counter;
}

void setup() {
  Serial.begin(115200);
  Timer2.initialize();
  Timer2.attachInterrupt(&isrHandler);
}

void loop() {
    static uint32_t counter_old = 0;
    Serial.println(counter - counter_old);
    counter_old = counter;
    delay(500);
}

EDIT: Хорошо, после некоторого изучения этой проблемы я обнаружил, что настройка режима CTC может привести к повреждению OCR2A (я полагаю?). Кажется, это работает, если изменить порядок настроек следующим образом:

  TCCR2B = 0;          // остановить таймер и сбросить WGM22
  TCCR2A = _BV(WGM21); // включаем режим CTC с верхним в OCR2A
  OCR2A  = 124;        // = (16*10^6) / (2000*64) - 1 (должно быть < 256)
  TCNT2  = 0;          //инициализируем значение счетчика до 0
  TCCR2B = _BV(CS22);  // Установить бит CS22 для 64 предварительного делителя (для запуска таймера)
,