This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
- Local
- Codespaces
HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Уроки Ардуино. Прерывания по таймеру
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
Latest commit message
Commit time
September 9, 2017 22:31
September 7, 2017 12:13
August 23, 2017 14:50
December 25, 2016 22:04
September 9, 2017 22:31
September 3, 2017 22:44
README.md
Cross-platform Arduino timer API.
- Arduino (AVR 16MHz, 16 bit)
- Arduino Due (SAM 84MHz, 32 bit)
- ChipKIT (PIC32MX 80MHz, 32 bit; PIC32MZ 200MHz — partially, wip)
Введение и руководство
Многозадачная Ардуина: таймеры без боли https://habrahabr.ru/post/337430/
cd ~/Arduino/libraries/ git clone https://github.com/sadr0b0t/arduino-timer-api.git
и перезапустить среду Arduino.
Или на странице github нажать Clone or download > Download ZIP, затем установить архив arduino-timer-api-master.zip через меню установки библиотек в среде Arduino (Скетч > Подключить библиотеку > Добавить .ZIP библиотеку. ).
Примеры должны появиться в меню File/Examples/arduino-timer-api
Встроенные варианты частот
/** * freq: 500KHz = 500000 ops/sec * period: 1sec/500000 = 2us */ void timer_init_ISR_500KHz(int timer); /** * freq: 200KHz = 200000 ops/sec * period: 1sec/200000 = 5us */ void timer_init_ISR_200KHz(int timer);s /** * freq: 100KHz = 100000 ops/sec * period: 1sec/100000 = 10us */ void timer_init_ISR_100KHz(int timer); /** * freq: 50KHz = 50000 ops/sec * period: 1sec/50000 = 20us */ void timer_init_ISR_50KHz(int timer); /** * freq: 20KHz = 20000 ops/sec * period: 1sec/20000 = 50us */ void timer_init_ISR_20KHz(int timer); /** * freq: 10KHz = 10000 ops/sec * period: 1sec/10000 = 100us */ void timer_init_ISR_10KHz(int timer); /** * freq: 5KHz = 5000 ops/sec * period: 1sec/5000 = 200us */ void timer_init_ISR_5KHz(int timer); /** * freq: 2KHz = 2000 ops/sec * period: 1sec/2000 = 500us */ void timer_init_ISR_2KHz(int timer); /** * freq: 1KHz = 1000 ops/sec * period: 1sec/1000 = 1ms */ void timer_init_ISR_1KHz(int timer); /** * freq: 500Hz = 500 ops/sec * period: 1sec/500 = 2ms */ void timer_init_ISR_500Hz(int timer); /** * freq: 200Hz = 200 ops/sec * period: 1sec/200 = 5ms */ void timer_init_ISR_200Hz(int timer); /** * freq: 100Hz = 100 ops/sec * period: 1sec/100 = 10ms */ void timer_init_ISR_100Hz(int timer); /** * freq: 50Hz = 50 ops/sec * period: 1sec/50 = 20ms */ void timer_init_ISR_50Hz(int timer); /** * freq: 20Hz = 20 ops/sec * period: 1sec/20 = 50ms */ void timer_init_ISR_20Hz(int timer); /** * freq: 10Hz = 10 ops/sec * period: 1sec/10 = 100ms */ void timer_init_ISR_10Hz(int timer); /** * freq: 5Hz = 5 ops/sec * period: 1sec/5 = 200ms */ void timer_init_ISR_5Hz(int timer); /** * freq: 2Hz = 2 ops/sec * period: 1sec/2 = 500ms */ void timer_init_ISR_2Hz(int timer); /** * freq: 1Hz = 1 ops/sec * period: 1sec */ void timer_init_ISR_1Hz(int timer);
На всех платформах доступно значение TIMER_DEFAULT, которое указывает на рабочий таймер по умолчанию.
Уроки Arduino #4 — функции времени
Если есть необходимость использовать другой таймер, можно указать его имя напрямую: _TIMER1. TIMER9, _TIMER1_32BIT. _TIMER9_32BIT
Однако для разных аппаратных платформ доступно разное количество таймеров с разными именами, поэтому указание конкретного таймера по имени может привести к потере кросс-платформенности.
- AVR ATmega1280, ATmega2560: _TIMER1, _TIMER3,_TIMER4, _TIMER5; TIMER_DEFAULT = _TIMER5
- AVR AT90USB646, AT90USB1286, ATmega128, ATmega1281, ATmega1284, ATmega1284P, AVR_ATmega2561: _TIMER1, _TIMER3; TIMER_DEFAULT = _TIMER3
- AVR ATmega32U4: _TIMER1; TIMER_DEFAULT = _TIMER1
- AVR другие чипы: _TIMER1; TIMER_DEFAULT = _TIMER1
- PIC32 (ChipKIT): _TIMER1, _TIMER2, _TIMER3, _TIMER4, _TIMER5, _TIMER2_32BIT, _TIMER4_32BIT; TIMER_DEFAULT = _TIMER4_32BIT
- SAM (Arduino Due): _TIMER1/_TIMER1_32BIT, _TIMER2/_TIMER2_32BIT, _TIMER3/_TIMER3_32BIT, _TIMER4/_TIMER4_32BIT, _TIMER5/_TIMER5_32BIT, _TIMER6/_TIMER6_32BIT, _TIMER7/_TIMER7_32BIT, _TIMER8/_TIMER8_32BIT, _TIMER9/_TIMER9_32BIT (все таймеры 32-битные, _TIMERX_32BIT == _TIMERX); TIMER_DEFAULT = _TIMER3/_TIMER3_32BIT;
Минимальные частоты и разрядность таймеров
Варианты вызовов timer_init_ISR_2Hz (2Гц, период 500мс) и timer_init_ISR_1Hz (1Гц, период 1с) на PIC32MX 80МГц будут работать только с 32-битными таймерами (_TIMER2_32BIT и _TIMER4_32BIT; TIMER_DEFAULT — по умолчанию = _TIMER4_32BIT), т.к. при 16-битных режимах таймеров PIC32MX 80МГц комбинация «делитель частоты» (prescaler — максимальный вариант 1/256) + «поправка периода» (adjustment — максимальный вариант 2^16=65536-1) дают минимальную частоту 5Гц (период — 200мс):
80000000/256/65536 = 4.8Гц
На PIC32 32-битные таймеры создаются комбинацией 2х 16-битных таймеров:
- Timer4(32бит) = Timer4(16бит)+Timer5(16бит)
- Timer2(32бит) = Timer2(16бит)+Timer3(16бит)
поэтому при использовании таймера _TIMER2_32BIT, обычные таймеры _TIMER2 и _TIMER3 будут заняты, при использовании _TIMER4_32BIT — заняты будут _TIMER4 и _TIMER5.
На AVR/Arduino можно получить частоту 1Гц стандартными делителями на 16-битном таймере.
На SAM/Arduino Due все таймеры 32-битные.
Тип данных для параметра adjustment — unsigned int.
- На PIC32 разрядность int — 32 бит, этого хватит и для 16-тибитного режима таймера (если не закладывать значение более 2^16=65536-1) и для 32-битного (пойдет любое значение до 2^32=4294967296-1).
- На SAM разрядность int — 32 бит, все таймеры 32-битные.
- На AVR разрядность int — 16 бит, это опять же, как раз достаточно для 16-битных таймеров.
Таким образом, хотя разрядность параметра adjustment с типом int будет разной на разных платформах, во всех случаях значение будет соответствовать аппаратным свойствам таймеров.
Эксперименты с высокими частотами прерываний на разных чипах
- PIC32MX 80MHz (ChipKIT Uno32)
- AVR 16MHz (Arduino Leonardo)
Для PIC32MX 80МГц самый лучший надежный вариант — 200КГц: целевой период 5мкс совпадает с замерами. На 500КГц (целевой период 2мкс) замер периода показывает 4мкс (в 2 раза больше), но если просуммировать период 1000 циклов, получается 2006мкс, т.е. всего на 6 мкс больше, чем целевое значение (2*1000=2000мкс). Судя по всему, ошибка не накапливается по циклам, а является константой, т.е. скорее всего добавляется на одном из циклов, например, при проведении замера вызовом micros (т.е. на такой частоте сам замер дает значимую погрешность). Для частоты 1МГц (целевой период 1мкс) ошибка уже накапливается — на 1000 циклов суммарный период получается те же 2005мкс против целевых 1000мкс, т.е. частота для прерываний полностью не рабочая.
Для AVR 16МГц лучший относительно надежный вариант — 20КГц (целевой период — 50мкс): есть кое-какая погрешность +/-4 (на пике 6) микросекунды, но на сумме 2х вызовов период получается почти все время ровно 100мкс. Варианты 50КГц (период 20мкс) и 100КГц (период 10мкс), вообще, тоже работают, но та же погрешность +/4 микросекунды на одном периоде уже становится существенной по сравнений с целевым периодом, хотя на сумме 1000 периодов погрешность остаётся той же константой +/-4мкс. Судя по всему, эти +/4 микросекунды являются допустимой погрешностью при вызове прерывания на этом чипе. Вариант 200КГц (целевой период 5мкс) уже полностью не рабочий: на одном периоде замер показывает те же 12-16мкс, на сумме ошибка не остаётся константой, а нарастает (10 периодов — 84мкс вместо нужных 50ти).
// period = 1us // PIC32MX (ChipKIT Uno32): 3us, x2=5us, x10=21us, x1000=2005us (fail) // AVR (Arduino Leonardo): 12/16us, x10=80/84/92us (fail) timer_init_ISR_1MHz(TIMER_DEFAULT);
// period = 2us // PIC32MX (ChipKIT Uno32): 4us, x2=6us, x10=22us, x1000=2006us (+2/6us, ~fail) // AVR (Arduino Leonardo): 12/16us, x10=84us (fail) timer_init_ISR_500KHz(TIMER_DEFAULT);
// period = 5us // PIC32MX (ChipKIT Uno32): 5us, x10=50us (ok) // AVR (Arduino Leonardo): 12/16us, x2=20us, x10=84us (fail) timer_init_ISR_200KHz(TIMER_DEFAULT);
// period = 10us // PIC32MX (ChipKIT Uno32): 10us (ok) // AVR (Arduino Leonardo): 12us, x2=20us, x10=100/104/108us, x1000=10000 (~ok) timer_init_ISR_100KHz(TIMER_DEFAULT);
// period = 20us // PIC32MX (ChipKIT Uno32): 20us (ok) // AVR (Arduino Leonardo): 12/20/24us, x2=40/44/48us, x1000=19996/20000/20004 (~ok) timer_init_ISR_50KHz(TIMER_DEFAULT);
// period = 50us // PIC32MX (ChipKIT Uno32): 50us (ok) // AVR (Arduino Leonardo): 48/52/56us, x2=100us (ok) timer_init_ISR_20KHz(TIMER_DEFAULT);
// period = 100us // PIC32MX (ChipKIT Uno32): 100us (ok) // AVR (Arduino Leonardo): 96/100/104us, x2=200/204us (ok) timer_init_ISR_10KHz(TIMER_DEFAULT);
Источник: github.com
Таймер в Arduino
Arduino UNO имеет три таймера: Timer0, Timer1 и Timer2.
Timer0 уже настроен для генерации миллисекундных прерываний, обновляя счетчик миллисекунд, передаваемый в millis ().
Timer0 являющийся 8-битным, считает от 0 до 255 и генерирует прерывание, при переполнении (превышении значения в 255). Он использует тактовый делитель на 64 по умолчанию, чтобы дать нам частоту прерываний 976.5625 Гц (достаточно близко к 1 кГц для наших целей).
В регистрах сравнения хранятся данные, которые постоянно сравниваются с состоянием таймера/счетчика. Установим регистр сравнения (OCR0A) для генерации другого прерывания где-то в середине этого счета. Приведенный ниже код будет генерировать прерывание TIMER0_COMPA всякий раз, когда значение счетчика проходит 0xAF.
// Timer0 уже используется millis() — мы создаем прерывание где-то // в середине и вызываем ниже функцию «Compare A» OCR0A = 0xAF; TIMSK0 |= _BV(OCIE0A);
// Timer0 уже используется millis() — мы создаем прерывание где-то
// в середине и вызываем ниже функцию «Compare A»
OCR0A = 0xAF; TIMSK0 |= _BV(OCIE0A);
Затем определим обработчик прерывания для вектора прерывания по таймеру, называемому TIMER0_COMPA_vect.
// Прерывание вызывается один раз в миллисекунду
SIGNAL(TIMER0_COMPA_vect)
Платы ARDUINO по низкой цене
Нажми на изображение, чтобы заказать в интернет-магазине:
Now 01.07.23 16:34:58, Your IP: 85.174.195.37; arduino.zl3p.com/basic/timer
ePN
Источник: arduino.zl3p.com
Arduino.ru
Я связался с Дэвидом Меллисом (David Mellis) из команды разработчиков Arduino и узнал, что Arduino пользуется всеми тремя таймерами ATMega168.
- Tаймер 0 (Системное время, ШИМ 5 and 6)
Используется для хранения счетчика времени работы программы. Функция millis() возвращает число миллисекунд с момента запуска программы, используя ISR глобального приращения таймера 0. Таймер 0 также используется для реализации ШИМ на выводах 5 и 6. - Tаймер 1 (ШИМ 9 и 10)
Используется для реализации ШИМ для цифровых выводах 9 и 10. - Tаймер 2 (ШИМ 3 и 11)
Используется для управления выходами ШИМ для цифровых выводов 3 и 11.
Хотя все таймеры используются, только Tаймер 0 имеет назначенную таймеру ISR. Это означает, что мы можем захватить Таймер 1 и/или Таймер2 под свои нужды. Однако в результате вы не сможете использовать ШИМ на некоторых портах ввода-вывода. Если вы планируете использовать ШИМ, имейте это ввиду. Я выбрал использование Таймера 2, что окажет влияние на выводы 3 и 11.
Моя тестовая программа полностью отключила вывод ШИМ на цифровые выводы, управляемые с таймера 2. Я подозреваю, что библиотека функций ШИМ рассчитана на работу счетчика в заданном диапазоне, который я превысил. Функция захвата сравнивает загруженные значения, и значения моего таймера просто не будут понятны для ШИМ.
Дэвид Меллис дал мне эту ссылку для изучения того, как библиотечные программы используют таймеры. Она хорошо документирована и я уверен, что она сослужит мне хорошую службу в будущем. В будущих экспериментах я планирую управлять ШИМ-модуляцией напрямую, чтобы достичь намного большей частоты, чем нормальная. Высокочастотный ШИМ может здорово пригодиться для функции аудио-ЦАП.
Ссылки, относящиеся к прерываниям в Arduino.
Здесь приведены несколько ссылок (англ.), относящихся к прерываниям на Arduino. Я просмотрел некоторые из них, пока забавлялся с прерываниями. Я должен признать, что мои познания – заслуга этих авторов.
- Обработка внешних прерываний на Arduino (
- Буду думать прежде чем писать программу
- Прерывания против последовательного опроса
- Больше внешних прерываний?
- AVR Libc : Прерывания
Тексты программ
Программы, приведенные в этой статье, доступны в zip-архиве.
Установка Таймера 2
Приведенная ниже программа показывает созданную мной функцию установки Таймера 2. Эта функция подключает прерывание по переполнению Таймера 2, устанавливает предварительно заданный масштаб для таймера подсчитывает загружаемое значение таймера, дающее желаемую частоту прерываний по времени. Эта программа основана на программе, найденной мной по вышеуказанным ссылкам. Я приобрел опыт, читая спецификации AVR, однако гораздо проще повторно использовать программу, если вы можете её найти. Рекомендую вам искать информацию по таймерам в спецификациях, только если по-другому никак не представить, как это трудно для понимания.
#define TIMER_CLOCK_FREQ 2000000.0 //2MHz for /8 prescale from 16MHz //Установка Таймера2. //Конфигурирует 8-битный Таймер2 ATMega168 для выработки прерывания //с заданной частотой. //Возвращает начальное значение таймера, которое должно быть загружено в TCNT2 //внутри вашей процедуры ISR. //Смотри пример использования ниже. unsigned char SetupTimer2(float timeoutFrequency) < unsigned char result; //Начальное значение таймера. //Подсчет начального значения таймера result=(int)((257.0-(TIMER_CLOCK_FREQ/timeoutFrequency))+0.5); //257 на самом деле должно быть 256, но я получил лучшие результаты с 257. //Установки Таймер2: Делитель частоты /8, режим 0 //Частота = 16MHz/8 = 2Mhz или 0.5 мкс //Делитель /8 дает нам хороший рабочий диапазон //так что сейчас мы просто жестко запрограммируем это. TCCR2A = 0; TCCR2B = 0
Сначала определяется тактовая частота таймера. Показано, что тактовая частота установлена 2 МГц, так как мы используем деление на 8 (делитель частоты) опорной частоты 16 МГц. Это жестко запрограммировано в функции. Это определение улучшает внешний вид программы и может быть полезно в некоторых приложениях. Наконец, оно напоминает мне, как я настроил таймер.
Функция имеет один аргумент – желаемую частоту прерываний, и возвращает значение, которое необходимо перезагружать в таймер в процедуре ISR. Функция не ограничивает требуемую частоту, но не следует слишком её завышать. Этот вопрос обсуждается ниже в этой статье.
Далее подсчитывается значение, перезагружаемое в таймер. Это очень простой подсчет, но требует операций с плавающей точкой. К счастью, нам нужно сделать это только один раз, поскольку операции с плавающей точкой очень дорого обходятся в пересчете на машинное время. Примем, что таймер будет установлен на 2 МГц при каждом счете.
Загружаемое значение – это число отсчетов, которое мы хотим произвести при 2 МГц между прерываниями. Вы можете заметить, что я использовал 257 вместо 256 для числа отсчетов в этом выражении. Я знаю, что верное значение – 256, но я получил лучшие результаты с 257. Далее в этой в этой статье я разъясню, почему.
Следующий участок секретного кода устанавливает таймер в режим 0 и выбирает делитель частоты /8. Режим 0 – это базовый режим таймера, а делитель /8 показывает, как мы получаем счетчик, считающий с частотой 2 МГц или 0,5 мкс на отсчет.
Далее подключается прерывание по переполнению. После выполнения этого кода микроконтроллер будет вызывать ISR каждый раз, когда счетчик прокрутится от 0xFF до 0×00. Это случится, когда счетчик просчитает от нашего загруженного значения через FF и назад до 00.
Наконец, мы загружаем значение счетчика в таймер и возвращаем это загруженное значение, чтобы ISR могла использовать его позже.
Я запускал таймер максимум при 50 кГц. Это очень быстро и любые действия, выполняемые в ISR, значительно тормозят выполнение основной программы. Я не рекомендую использовать частоты свыше 50 кГц, разве что вы почти ничего не делаете в ISR.
Загрузка микроконтроллера прерываниями
Чтобы дать вам представление об эффекте, предположим, что таймер ISR запускался бы каждые 20 мкс. Процессор, работающий на 16 МГц, может выполнить около 1 машинной команды каждые 63 нс или около 320 машинных команд для каждого цикла прерывания (20 мкс). Предположим также, что исполнение каждой строки программы на С может занять много машинных команд.
Каждая инструкция, используемая в ISR, отнимает время, доступное для исполнения любой другой программы. Если бы наша ISR использовала около 150 машинных циклов, мы сожрали бы половину доступного процессорного времени. При активных прерываниях главная программа откладывалась бы около ½ времени, занимаемого ей в других случаях. 150 машинных команд – не очень большая программа на С, поэтому вы должны быть внимательны.
Если у вас будет слишком длинная ISR, ваша главная программа будет исполняться крайне медленно, если же ISR будет длиннее, чем продолжительность цикла таймера, то практически никогда не выполнится ваша главная программа, и, кроме того, в конце концов произойдет сбой системного стека.
Измерение загрузки прерываниями
Поскольку мне хотелось иметь очень быстрый таймер ISR, я должен был измерить, насколько я загрузил доступные ресурсы. Я придумал трюк, выполняющий оценку загрузки и позволяющий мне вывести измерение на последовательный порт. Так как я работал с таймером ISR, я мог сохранить следы загрузки прерываниями.
Таймер не был установлен в режим, когда он перезагружается автоматически. Это значит, что ISR должна перезагрузить таймер для следующего интервала счета. Было бы точнее иметь автоматически перезагружаемый таймер, но, используя этот режим, мы можем измерить время, проводимое в ISR, и соответственно исправить время, загружаемое в таймер. Ключ в том, что при помощи этой коррекции мы при разумной точности, мы также получаем и число, показывающее, сколько времени мы проводим в ISR.
Трюк заключается в том, что таймер хранит время, даже если он переполнен и прерван. В конце нашей ISR мы можем захватить текущее значение счетчика таймера. Это значение представляет то время, которое он отнял у нас до следующей точки программы. Это суммарное время, затраченное на переход в процедуру прерывания и выполнение программы в ISR.
Небольшая ошибка будет оттого, что не подсчитывается время, затраченное на команду перезагрузки таймера, но мы можем исправить её эмпирически. Фактически именно поэтому я использовал в формуле подсчета загружаемого значения 257 вместо 256. Я обнаружил опытным путем, что это дает лучший результат. Лишний такт компенсирует команду перезагрузки таймера.
Источник: arduino.ru