Настройка прерывания таймера на одну секунду происходит слишком медленно (~ 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);
}
}
Это работает как положено и обновляется каждую секунду.
Так что же не так с моими настройками прерываний?
@maddes8cht, 👍1
2 ответа
Лучший ответ:
В TCCR1A/B
есть флаг, называемый WGM12
, который устанавливает для режим генерации волн (WGM)
значение Очистить таймер вкл. Сравнить совпадения (CTC)
.
Это приводит к тому, что таймер отсчитывает значение в OCR1A, а затем автоматически сбрасывается до 0.
Этот флаг был забыт, поэтому прерывание просто игнорирует настройку OCR1A = 15624;
и считает до 65535, что соответствует наблюдаемому коэффициенту около 4.
Поэтому строку настройки для TCCR1B необходимо изменить на
TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
// Устанавливаем режим CTC и устанавливаем Prescaler на 1024
Теперь прерывание срабатывает корректно каждую секунду.
Он игнорирует этот OCR1A = 15624;
и вместо этого просто переключается на 65535. Отсюда и коэффициент ошибки 4. Вам необходимо установить режим генерации волны как CTC в регистрах TCCR1A/B.
Спасибо за ответ. К сожалению, опробовать смогу только после выходных. Посмотрю, сработает ли это (но я вполне уверен), @maddes8cht
Это все еще дало мне время, чтобы выяснить, что это значит и что мне нужно сделать, поэтому:
Режим ctc означает «Очистка таймера при сравнении» и управляется флагом WGM12 в регистре TCCR1A/B. Когда я использую TCCR1b, строка получает:
TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
, @maddes8cht
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- Как правильно использовать volatile переменные в Arduino?
- Как прервать функцию цикла и перезапустить ее?
- 4-битный счетчик вверх и вниз
- Включить и отключить отдельные прерывания
- Управление функцией включения на драйвере микрошагового устройства
- Захват прерывания на обоих фронтах, когда он установлен на RISING или FALLING
«Это дало мне еще немного времени, чтобы выяснить, что это значит и что мне нужно сделать, поэтому: «Я рад, что это было для вас полезным учебным упражнением и ваше описание выглядит хорошо. Гораздо ценнее, чем получить готовое и законченное решение., @6v6gt