Настройка прерывания таймера на одну секунду происходит слишком медленно (~ 4 секунды)

Я пытаюсь использовать прерывание таймера на Arduion Uno.

Вот простой пример кода для отображения прогрессивной цифры на ЖК-дисплее, которая обновляется с помощью прерывания таймера каждую секунду:

#include <LCD_I2C.h>

LCD_I2C lcd(0x27);
volatile bool timerFlag = false; // Флаг-таймер (прерывание по таймеру)
int displayWidth = 16; // ширина отображения
int digit = 0; // текущая цифра

void setup() {
  lcd.begin();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Initializing...");

  noInterrupts();
  TCCR1A = 0;
  TCCR1B = (1 << CS12) | (1 << CS10); // устанавливаем прескалер на 1024
  TCNT1 = 0;
  OCR1A = 15624; // должно быть 1 секунда с прескалером 102
  TIMSK1 |= (1 << OCIE1A); // Включаем прерывание по таймеру
  interrupts();
}

ISR(TIMER1_COMPA_vect) {
  timerFlag = true;
}

void loop() {
  static int position = 0;
  char displayString[17]; // +1 нулевое завершение
  
  if (timerFlag) {
    lcd.clear(); // очистка дисплея
    
    // обновляем цифру и прогресс
    digit = (digit + 1) % 10;
    position = (position + 1) % (displayWidth + 1);
    memset(displayString, ' ', displayWidth);
    displayString[displayWidth] = '\0'; add null termination
    displayString[position] = '0' + digit; // Символ текущей цифры
    
    // Показ обновленного прогресса на дисплее
    lcd.setCursor(0, 1);
    lcd.print(displayString);
    
    timerFlag = false;
  }
}

Значения для настройки таймера включения 1 на 1 секунду есть повсюду. НО обновления, генерируемые этим кодом, явно происходят намного медленнее, чем 1 секунда, это больше примерно 4 секунд. Поэтому я провел сравнение с версией того же примера, основанной на задержке:

#include <LCD_I2C.h>

LCD_I2C lcd(0x27);
int displayWidth = 16; // ширина отображения

void setup() {
  lcd.begin();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Initializing...");
}

void loop() {
  for (int digit = 0; digit < 10; digit++) {
    char displayString[17]; // +1 нулевое завершение
    int position = digit % (displayWidth + 1);

    // Обновление прогресса
    memset(displayString, ' ', displayWidth);
    displayString[displayWidth] = '\0'; // добавляем нулевое завершение
    displayString[position] = '0' + digit; // Символ текущей цифры
    
    // Показ обновленного прогресса на дисплее
    lcd.clear(); // Очистить отображение
    lcd.setCursor(0, 1);
    lcd.print(displayString);

    delay(1000);
  }
}

Это работает как положено и обновляется каждую секунду.

Так что же не так с моими настройками прерываний?

, 👍1


2 ответа


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

3

В TCCR1A/B есть флаг, называемый WGM12, который устанавливает для режим генерации волн (WGM) значение Очистить таймер вкл. Сравнить совпадения (CTC). Это приводит к тому, что таймер отсчитывает значение в OCR1A, а затем автоматически сбрасывается до 0.

Этот флаг был забыт, поэтому прерывание просто игнорирует настройку OCR1A = 15624; и считает до 65535, что соответствует наблюдаемому коэффициенту около 4. Поэтому строку настройки для TCCR1B необходимо изменить на

TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10); 
// Устанавливаем режим CTC и устанавливаем Prescaler на 1024

Теперь прерывание срабатывает корректно каждую секунду.

,

«Это дало мне еще немного времени, чтобы выяснить, что это значит и что мне нужно сделать, поэтому: «Я рад, что это было для вас полезным учебным упражнением и ваше описание выглядит хорошо. Гораздо ценнее, чем получить готовое и законченное решение., @6v6gt


2

Он игнорирует этот OCR1A = 15624; и вместо этого просто переключается на 65535. Отсюда и коэффициент ошибки 4. Вам необходимо установить режим генерации волны как CTC в регистрах TCCR1A/B.

,

Спасибо за ответ. К сожалению, опробовать смогу только после выходных. Посмотрю, сработает ли это (но я вполне уверен), @maddes8cht

Это все еще дало мне время, чтобы выяснить, что это значит и что мне нужно сделать, поэтому: Режим ctc означает «Очистка таймера при сравнении» и управляется флагом WGM12 в регистре TCCR1A/B. Когда я использую TCCR1b, строка получает: TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);, @maddes8cht