Использовать timer0, не влияя на millis() и micros().

Я пишу библиотеку, которой требуется ISR для выключения светодиода через некоторое время после его включения. Поскольку все дело в том, чтобы включать и выключать светодиод, нет необходимости быть очень точным. С другой стороны, я хотел бы использовать эту библиотеку в программе, где timer1 и timer2 выдаются на другие (более важные) задачи, а также мне нужны millis() и micros( ) функции. Наконец, я использую микроконтроллер ATmega328P, который имеет только 3 таймера (и я не могу легко заменить его другим).


Поэтому мне было интересно, могу ли я подключить ISR к timer0, не затрагивая вышеупомянутые функции Arduino, и – если это можно сделать – каковы будут ограничения такого ISR (например, я полагаю, что не могу использовать все таймеры). /режимы прерывания...), а также недостатки или побочные эффекты использования этого таймера в библиотеке, отличной от Arduino, и функциях Arduino millis() и micros().

Заранее спасибо за любой ответ!

, 👍4

Обсуждение

Почему вы считаете, что вам нужно использовать прерывание для такой упрощенной и некритичной по времени операции?, @Majenko

На самом деле я в настоящее время использую функцию «checkLed()», и она отлично работает. Но 1) основной цикл программы иногда довольно длинный (например, иногда он должен выполнять другие циклы), и мне было бы проще, если бы мне не нужно было заботиться об отключении светодиода (что не критично по времени, но вполне важно), 2) теперь меня вообще очень интересует этот вопрос ;), @noearchimede

Справедливо. TBH Я только что написал подпрограмму, которая делает именно это, но на другом MCU, который имеет другую структуру таймера (он управляет светодиодами TX и RX для индикации активности USB - функции прерывания RX и TX включают светодиоды, таймер включает их через 50 мс), поэтому я понимаю необходимость., @Majenko

Связано: *[Atmel/Arduino: ISR(TIMER0_OVF_vect) не будет компилироваться («сначала определено» в __vector_16)](https://stackoverflow.com/questions/46573550)* (предоставление собственного обработчика для Timer0 и по-прежнему использовать среде Ардуино)., @Peter Mortensen


4 ответа


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

8

Поэтому мне было интересно, могу ли я подключить ISR к timer0, не затрагивая вышеуказанные функции Arduino,

Да. Несколько способов, в зависимости от вашего уровня комфорта:

  1. Вы можете объявить стандартный OVF Arduino Timer0 «слабым» и написать свой собственный, куда вы можете вставить свой ISR. Но вы должны обрабатывать взаимодействие между переменными, связанными с millis() / micros().

  2. Вы можете изменить стандартный OVF Arduino Timer0, чтобы вставить свой собственный ISR.

    И – если это возможно – каковы будут ограничения такого ISR (например, я не могу использовать все режимы таймера/прерывания...)

    Стандартный ISR будет выглядеть следующим образом:

    ISR(TIM0_OVF_vect) {
        ...
        timer0_fract = f;
        timer0_millis = m;
        timer0_overflow_count++;
    
        //insert your isr here
        _mytmr0_isrptr();
    }
    

    Где _mytmr0_isrptr() – указатель на функцию вашего собственного обработчика ISR.

  3. Проще всего использовать прерывания сравнения вывода. Вы можете заставить его вести себя как виртуальный таймер и делать множество других вещей.

Очевидно, что вы можете придумать множество других способов, и это только отправная точка.

Что это за прерывания? Как я могу их использовать? –

Timer0 имеет три связанных с ним прерывания: переполнение и сравнение канала A и канала B. Прерывание переполнения уже используется функциями синхронизации millis() и micros(), как показано ранее.

Прерывания канала сравнения A/B не используются. И это обсуждение касается их использования для целей синхронизации.

Общий идеал показан в сообщении блога Использование резервных каналов сравнения выходных данных в качестве таймеров — коллекция.

Здесь задача сложнее, чем здесь, но основной идеал тот же. Сделать то, что вы хотели, здесь гораздо проще.

Если вы все еще не можете понять это, я могу предоставить вам фрагменты кода, чтобы вы могли начать работу.

,

Извините, я не понял третий вариант... Что это за прерывания? Как я могу их использовать?, @noearchimede

Спасибо, теперь понял! Я реализовал это в своей библиотеке, и это работает., @noearchimede

Я рад, что у вас есть решение, которое работает для вас. Ключевым моментом здесь является признание того, что сравнение выходных данных по сути является таймером: он генерирует прерывание при совпадении, в отличие от таймеров, которые делают это при переполнении или нижнем уровне., @dannyf

Как только вы поймете, что можете сделать много виртуальных таймеров из выходных каналов сравнения. для большинства mcus это означает от 2x до 8x виртуальных таймеров на аппаратный таймер., @dannyf


1

Прерывание может иметь только одну процедуру обработки прерывания. Единственный способ сделать что-то еще, используя прерывание, которое уже обрабатывается в другом месте, — изменить этот существующий обработчик прерывания, чтобы он также вызывал ваш код. Не то, что практично для библиотеки.

Я не вижу никакого практического способа сделать это, кроме как изменить всю систему millis() в основном API, чтобы разрешить перехват функций обратного вызова, что было бы действительно хорошей возможностью (chipKIT имеет аналогичную систему с прерыванием Core Timer на PIC32) и отправить ее обратно в Arduino для включения в основное ядро, что, конечно же, установит базовую версию для совместимости вашей библиотеки.

,

И нет никакого способа переопределить ISR системы Millis без изменения файлов API, верно?, @noearchimede

Правильный. Если вы определите второй ISR, вы получите конфликты векторов. Вам просто нужно будет выбрать другое прерывание для использования. Некоторые библиотеки предоставляют макросы #define и т. д., чтобы выбрать, с каким таймером запускать., @Majenko


1

Поэтому мне было интересно, могу ли я подключить ISR к timer0, не затрагивая вышеуказанные функции Arduino...

Нет, потому что эти функции используют уже подключенное прерывание.

это не должно быть очень точным...

Просто проверьте текущее время (например, используя миллисы или микросекунды) в основном цикле.

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

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

,

Это невозможно с прерыванием TIMER0_OVF, но есть также прерывания TIMER0_COMPA/TIMER0_COMPB, и их можно использовать бесплатно. Единственным недостатком является то, что AnalogWrite на соответствующих контактах сдвигает время, когда происходит прерывание., @KIIV

@KIIV Итак, используя timer0_compx, я больше не мог использовать AnalogWrite для соответствующего вывода?, @noearchimede

@noearchimede Вы можете. Просто интервал будет двигаться в соответствии с этим. Но если вам не нужно точное время, это не будет проблемой. Наверняка вы можете проверить все значения ШИМ, чтобы убедиться, что он работает каждый раз., @KIIV

@KIIV Я только что написал новую версию своей библиотеки, используя TIMER0_COMPA, и она работает именно так, как я хотел. Могу я попросить вас написать полный ответ с вашими предложениями, чтобы я мог пометить вопрос как отвеченный?, @noearchimede

@noearchimede Это уже здесь, в ответе dannyf (третий вариант), @KIIV

@KIIV Вчера я не понял его третий вариант, поэтому забыл его, но теперь мне все ясно. Спасибо Вам и всем ответившим!, @noearchimede


2

Я думаю, что первое решение, предложенное ответом dannyf, неверно, поскольку ISR в AVR GCC нельзя определить как слабую функцию (почему).

Я думаю, что лучшим решением будет добавить функции-обработчики (хуки AKA) с помощью указателя функции на Timer0 ISR внутри исходного файла core wired.c, поставляемого с ядром Arduino, и назначить последний (прикрепить) этот указатель функции к любой желаемой функции. в другой библиотеке/классе или в основном коде приложения.

Подробнее об этом трюке в этой статье здесь .

,