Как обновить переменную в ISR с помощью таймеров

8

Я пытаюсь проверить частоту Timer3 с помощью счетчика. Значение счетчика, объявленное как volatile, увеличивается в ISR, и каждую секунду сумма отображается в основном цикле, а значение сбрасывается в ноль.

Таймер был настроен правильно. (Если я выберу таймер 3 Гц, я вижу мигание светодиода)

Проблема

Счетчик не увеличивается. Вот вывод:

Setup Completed
tick: 1
tick: 0
tick: 0
tick: 0

КОД

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);

  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B = 20; // 800Hz 5; // 3 Hz
  // turn on CTC mode:
  TCCR3B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();

  Serial.println("Setup completed");
}

void loop()
{
  if (millis() % 1000 == 0)
  {
    Serial.print(" tick: ");
    Serial.println(cont);
    cont = 0;
  }
}

ISR(TIMER3_COMPB_vect)
{
  //digitalWrite(13, digitalRead(13) ^ 1);
  cont++;
}

РЕДАКТИРОВАТЬ Этот таймер используется для считывания значения журнала из акселерометра и сохранения его в массиве с плавающей точкой. Но на данный момент я застрял в этой проблеме обновления.

РЕШЕНИЕ 1 Благодаря Гербену

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3A = 20; // 20; //800Hz 5; // 3 Hz
  // turn on CTC mode:
  TCCR3B |= (1 << WGM32);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();
  Serial.println("Setup completed");
}

void loop()
{
  delay(1000);
  Serial.println(cont);
  cont = 0;
}

ISR(TIMER3_COMPB_vect)
{
  cont++;
}

РЕШЕНИЕ 2 Благодаря BrettM

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B =  20; //800Hz 5; // 3 Hz
  // turn on CTC mode:
  //TCCR3B |= (1 << WGM32);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();
  Serial.println("Setup completed");
}

void loop()
{
  Serial.println(cont); 
  cont = 0;
  delay(1000);

}

ISR(TIMER3_COMPB_vect)
{
  TCNT3 = 0;
  cont++;
}
UserK
источник
И если вы раскомментируете digitalWriteлинию, вы увидите, что светодиод мигает примерно раз в секунду (каждые 0,66 с)?
Рикардо
Да, если я раскомментирую digitalWriteи установлю OCR3B = 5;светодиод мигает примерно на этой частоте.
UserK
Тогда это загадка. Вы пробовали комментировать cont = 0;внутри цикла? Что происходит потом?
Рикардо
1
Попробуйте увеличить частоту. Я думаю, что ваш оператор if может очищать счетчик чаще, чем вызывается прерывание. Но тогда вы должны увидеть больше в выводе. Также позвольте, если работать дольше (скажем, 1 минута) и вставьте результаты. Кроме того, когда вы обновляете вопрос, оставьте старый вывод, чтобы ваш вопрос имел смысл (без истории редактирования).
Рикардо
1
Я подозреваю, что подпрограмма прерывания вызывается только один раз, а затем отключается. Я где-то читал, что прерывания отключены, когда выполняется код прерывания, и в некоторых случаях вы должны повторно включить его, но я действительно не уверен, так ли это. Надеюсь, кто-то более знающий придет нам на помощь ...
Рикардо

Ответы:

5

В режиме CTC вершина есть OCR3A, нет OCR3B!

После этого TIMSK3 |= (1 << OCIE3B);также следует заменить на TIMSK3 |= (1 << OCIE3A);, и ISR(TIMER3_COMPB_vect)вISR(TIMER3_COMPA_vect)

Для 3 Гц OCR3Aдолжно быть 5208, а не 20.

Технически TCCR3B |= (1 << WGM12);должно бытьTCCR3B |= (1 << WGM32);

Gerben
источник
В вашей конфигурации счетчик не обновляется, и каждую секунду отображается предложение «Установка завершена» (записано в функции setup ()!). Действительно странное поведение.
UserK
Решено с помощью TIMSK3 |= (1 << OCIE3B);. Спасибо Гербен! Пожалуйста, измените свой ответ, и я приму его в качестве решения.
UserK
1
Я забыл упоминание, вам также нужно изменить вектор ISR. ISR(TIMER3_COMPB_vect)должно быть ISR(TIMER3_COMPA_vect). Если ISR не определен, AVR будет сброшен сам, как вы испытывали. Рад, что ты получил это работает.
Гербен
3

Похоже, мой ответ на этот вопрос ранее был неполным, спасибо, что указали, что режим CTC работает только с OCR3A Gerben. Я прошу прощения за то, что не протестировал ответ, прежде чем опубликовать его

Учитывая информацию только в этом вопросе, ответ Гербена полон, но поскольку ваш другой вопрос подразумевает, что вы не можете использовать OCR3A из-за библиотеки сервоприводов, я добавлю немного. (Я также отредактировал этот ответ)

Вы можете эмулировать поведение режима CTC, установив TCNT3 на 0 в своей подпрограмме прерывания. Не забудьте удалить строку, которая включает режим CTC в вашем коде.

Я проверил ваш код с этим ISR:

ISR(TIMER3_COMPB_vect)
{
  TCNT3 = 0;
  cont++;
}

и эта конфигурация таймера записывается

OCR3B = 5208; // 800Hz 5; // 3 Hz
// Set CS10 and CS12 bits for 1024 prescaler:
TCCR3B |= (1 << CS30) | (1 << CS32);
// enable timer compare interrupt:
TIMSK3 |= (1 << OCIE3B);

Это может быть немного менее точно на высоких частотах, чем CTC, я не уверен, но на 3 Гц он работал отлично. Обратите внимание, что 5208 было правильным значением OCR, а не 20 (опять же, благодаря Гербену).

BrettAM
источник
Я пробовал ваш код, но счетчик не увеличивается. Я добавил TCNT3=0;в ISR () и удалил //TCCR3B |= (1 << WGM32);в setup (), как вы сказали. Я также пытался cont=0;
закомментировать
1
Убедитесь, что код соответствует тому, что опубликовано в вопросе любым другим способом. Попробуйте изменить свой цикл на просто println(cont); delay(1000);. Также вы все еще включаете биты с cli (), TCCR3A и т. Д. Правильно?
BrettAM
Хорошо, спасибо. На 800 Гц все еще точно!
UserK