Как создать прерывание по таймеру с Arduino?

9

Я пытаюсь создать прерывание с задержкой с Arduino. Я хотел бы использовать функцию interrupts (), потому что это внутреннее прерывание.

Пример: допустим, я бы хотел, чтобы индикатор включался и выключался только с учетом времени прерывания.

Пример кода есть, но он использует внешние прерывания (attachInterrupt ()). Я хотел бы продолжать использовать внутренние прерывания.

Случайный
источник
2
Я думаю, что точка зрения, которую также показал Кортук, заключается в том, что attachInterrupt - это абстрактная вещь, вы не присоединяете какие-либо внешние компоненты :)
clabacchio
Эта статья может вам помочь. engblaze.com/…
Сет Арчер Браун

Ответы:

10

В блоге Ноа Шталя есть пример мигания светодиода с помощью Timer2 . Имея это и таблицу данных, вы должны быть в состоянии адаптировать ее к любому прерыванию, которое вы хотите использовать, то есть к прерыванию, обычная функция которого вы больше всего можете оставить или хотите изменить. Таймер 2 обычно используется для некоторых функций ШИМ.

Его пример цитирует ATmega2560; Я могу подтвердить, что он работает и с ATmega328p. Посмотрите на его сайте более полезные примеры прерываний Arduino.

Редактировать:

Вот моя слегка отредактированная - в основном в комментариях - версия кода Ноя. Вызовите Timer2init () из функции setup () Arduino после инициализации любых связанных структур данных или оборудования, потому что синхронизация - и прерывание - начнутся, как только вы это сделаете.

Например, я использовал его для мультиплексирования 3-значного 7-сегментного дисплея, поэтому перед инициализацией таймера я инициализировал регистры ввода-вывода дисплея и отключил данные дисплея в том месте, где его будет искать ISR.

В комментариях есть таблица некоторых полезных временных данных из таблицы данных и мои собственные расчеты для справки, чтобы установить другую временную схему.

Макрос ISR () заботится о создании кода входа и выхода прерывания для ISR вместо нормального входа и выхода функции и связывания его с соответствующим вектором прерывания. Остальная часть этой функции: 1) код, который будет запускаться при каждом прерывании, и 2) кодовый код для сброса таймера для следующего прерывания.

Как написано, это должно быть в виде эскиза .pde или .ino (или файла .cpp, если вы используете eclipse, f / ex). Эскиз должен #define LEDPIN, а setup () должен вызвать Timer2init (). Функция цикла может быть пустой или нет; светодиод должен начать мигать при загрузке (ну, буквально, после вызова Timer2init ()).

/*
 * From sample interrupt code published by Noah Stahl on his blog, at:
 * http://arduinomega.blogspot.com/p/arduino-code.html
 * 
 */


/*** FUNC

Name:           Timer2init

Function:       Init timer 2 to interrupt periodically. Call this from 
                the Arduino setup() function.

Description:    The pre-scaler and the timer count divide the timer-counter
                clock frequency to give a timer overflow interrupt rate:

                Interrupt rate =  16MHz / (prescaler * (255 - TCNT2))

        TCCR2B[b2:0]   Prescaler    Freq [KHz], Period [usec] after prescale
          0x0            (TC stopped)     0         0
          0x1                1        16000.        0.0625
          0x2                8         2000.        0.500
          0x3               32          500.        2.000
          0x4               64          250.        4.000
          0x5              128          125.        8.000
          0x6              256           62.5      16.000
          0x7             1024           15.625    64.000


Parameters: void

Returns:    void

FUNC ***/

void Timer2init() {

    // Setup Timer2 overflow to fire every 8ms (125Hz)
    //   period [sec] = (1 / f_clock [sec]) * prescale * (255-count)
    //                  (1/16000000)  * 1024 * (255-130) = .008 sec


    TCCR2B = 0x00;        // Disable Timer2 while we set it up

    TCNT2  = 130;         // Reset Timer Count  (255-130) = execute ev 125-th T/C clock
    TIFR2  = 0x00;        // Timer2 INT Flag Reg: Clear Timer Overflow Flag
    TIMSK2 = 0x01;        // Timer2 INT Reg: Timer2 Overflow Interrupt Enable
    TCCR2A = 0x00;        // Timer2 Control Reg A: Wave Gen Mode normal
    TCCR2B = 0x07;        // Timer2 Control Reg B: Timer Prescaler set to 1024
}



/*** FUNC

Name:       Timer2 ISR

Function:   Handles the Timer2-overflow interrupt

Description:    Maintains the 7-segment display

Parameters: void

Returns:    void

FUNC ***/

ISR(TIMER2_OVF_vect) {
    static unsigned int led_state = 0; // LED state

    led_state = !led_state;         // toggles the LED state
    digitalWrite(TOGGLE_PIN, led_state);

    TCNT2 = 130;     // reset timer ct to 130 out of 255
    TIFR2 = 0x00;    // timer2 int flag reg: clear timer overflow flag
};
JRobert
источник
(@Kortuk: комментарий, на который вы ссылаетесь, был моим наблюдением за несколькими комментаторами здесь, и он не был нацелен на вас лично, и это было ненужно. Я извиняюсь и удалил его.) Я расширил свой ответ, как вы предложили, и я надеюсь, что это теперь не только показательный, но и поучительный. Он включает в себя комментарии, которые я написал в код для моего собственного использования (имеется в виду: если я смогу понять их через 6 месяцев, кто-то другой тоже сможет это сделать), а также несколько инструкций «как использовать» в ответ. Спасибо за ваши предложения.
JRobert
Обратите внимание, что предварительные шкалы 32 и 128 недоступны для timer0 и timer1 (по крайней мере, с atmega328).
Tuupola
Это приятно знать - спасибо. Я использую это для Timer2 (пока), и это в основном выпадающий список.
JRobert
5

attachInterrupt () на самом деле прикрепляет прерывание к внешнему изменению состояния на выводе, у него нет никаких других опций.

На же странице параметры режима перечислены как:

Режим определяет, когда прерывание должно быть запущено. Четыре константы предопределены как допустимые значения:

  • НИЗКИЙ чтобы вызвать прерывание всякий раз, когда вывод низкий,
  • ИЗМЕНЕНИЕ для запуска прерывания всякий раз, когда вывод меняет значение
  • ПОВЫШЕНИЕ для срабатывания, когда штифт переходит от низкого к высокому,
  • ПАДЕНИЕ когда штифт идет от высокого к низкому.

Извините, что несу плохие новости, это одна из первых вещей, которые я тоже искал.

Kortuk
источник
Я думаю, что он имеет в виду, что он хочет использовать внутренний таймер вместо внешнего устройства ... но я не очень хорошо знаю Arduino, поэтому я не могу сказать, возможно ли это
clabacchio
@clabacchio, я говорю, что единственный вариант - использовать внешний триггер, там нет функции внутреннего таймера.
Кортук
Ах, хорошо :) но, по крайней мере, у плат Arduino есть таймеры?
Клабаккио
Да, именно так они выполняют такие вещи, как задержка.
Кортук
1
@ icarus74 ATMega328 действительно имеет 3 таймера (один 16b и два 8b), но все они используются Arduino. Одна используется для таких функций, как delay () и millis (), а все три используются для ШИМ (дополнительную информацию можно найти в функции 'init ()', файле 'wiring.c' в Arduino IDE).
Васко
2

Эта статья о ШИМ прояснит многие ваши сомнения относительно использования таймеров Arduino. На Arduino есть два 8-битных таймера и один 16-битный таймер. Не существует высокоуровневого API для подключения функции ISR непосредственно к таймерам, который поставляется с Arduino SDK (то есть в качестве стандартной библиотеки), но есть несколько более низкоуровневый метод установки Special-Function-Registers и bit-arithmetic / операции на них. Однако есть пользовательская библиотека под названием Timer one .

icarus74
источник
На самом деле существует несколько различных комбинаций таймеров, в зависимости от того, к какому Arduino обращаются. Ответ вводит в заблуждение.
Кажется, так
@ Кажется, хочешь уточнить? Если вы говорите об оборудовании Arduino, обратите внимание, что ответ находится в контексте вопроса, а также в момент, когда вопрос задан.
icarus74
Arduino Mega (на базе ATmega1280) была выпущена 26 марта 2009 года, а Mega 2560 (ATmega2560) была выпущена 24 сентября 2010 года, задолго до того, как был задан этот вопрос. Оба микроконтроллера имеют более 2x 8-битного и 1x 16-битного таймера / счетчика, указанного в ответе.
Казалось бы, так
Большинство взаимодействий, которые я видел до сих пор, имеют однозначную ссылку на Arduino, что означает нечто вроде Duemilanove или Uno, то есть платы на основе серии 328. Другие платы всегда были явно квалифицированы в серии uP. или Мега, Нано, Микро и т. д. В любом случае, я смиренно приму исправление. В этом контексте уточнение лучше.
icarus74
1

Arduino использует все три таймера в ATMega328. Timer1(16 бит) используется для таких функций, как delay()и millis()для вывода ШИМ на контактах 5 и 6. Два других таймера -Timer0 и Timer2используются для вывода ШИМ на контактах 3, 9, 10, 11.

Таким образом, нет функции Arduino для прерывания по таймеру. Но есть способ. Вы можете использовать этот код для включения прерывания по таймеру Timer2:

ISR(TIMER2_OVF_vect) {
  // Interrupt routine.
}

void setup() {
  // Enable Timer2 interrupt.
  TIMSK2 = (0<<OCIE2A) | (1<<TOIE2);
}

void loop() {
  // Your main loop.
}

Я написал этот код без тестирования, поэтому, возможно, я ошибся. В этом случае проверьте таблицу данных, стр.156 .

Если вы хотите изменить частоту таймера (прескалер), просто измените регистр TCCR2A. Для получения дополнительной информации обратитесь к странице технических данных 153. Но если вы измените частоту таймера, вы также измените частоту ШИМ-сигнала на двух выходных контактах!

Васко
источник
AFAIK на ATmega328 Timer0и Timer28 бит и только Timer116 бит.
Tuupola
Нет, это не правильно. Это Timer0, а не Timer1, который используется для delay () и millis () и для вывода ШИМ на контактах 5 и 6. Timer0 - это 8-битный таймер. Смотрите, например, Arduino Таймеры и прерывания .
Питер Мортенсен