Курс arduino – прерывания, создание функций, советы

Курс Arduino – Прерывания, создание функций, советы

Функции и точность показаний

Помнишь свой первый эксперимент? А помнишь, как я учил делать его компактнее с помощью for? К чему я это. В этом уроке я покажу, как можно проще выполнять однотипные действия. Сильно доставляет, когда в коде нужно много раз выполнять одно и то же действие. Чтобы облегчить жизнь, я познакомлю тебя с созданием функций.

Еще мы рассмотрим такую полезную штуку, как прерывания. Чем она полезна, рассмотрим ниже.

Что понадобится?

  • Светодиоды
  • Резисторы на 220 Ом и на 10 кОм
  • Соединительные провода «ПАПА- ПАПА»
  • Фоторезистор
  • Тактовая кнопка

Функции

Соберем светофор из первого эксперимента.

Код

Пояснения

Функции – чтобы создать функцию, мы должны дать ей название, определить ее тип, передать или не передать параметры, определиться с возвращаемыми значениями.

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

После этих действий, описываем, что будет выполнять функция.

А чтобы вызвать функцию, мы должны написать ее имя с круглыми скобками вот так: function();

Внимательный товарищ заметит, что самодельные функции вызываются так же, как и стандартные функции среды Arduino IDE и функции из подключенных библиотек. Отсюда можно судить, что и библиотека, и стандартные функции – это набор сделанных кем-то функций. Логично, да?

«А могу ли я сделать собственную библиотеку из созданных мною функций?» – спросишь ты. Конечно. Эту задачу мы будем пробовать выполнить в третьей части курса. Сейчас нам это не пригодится. Есть еще много более нужной сейчас информации, не стоит забивать голову.

Прерывания

Чем полезны прерывания? Они позволяют приостановить выполнение основного кода для выполнения какого-нибудь действия. Это бывает полезно в считывании показаний с датчиков. Так как показания считываются только один раз за проход loopa. А если в нем есть паузы, то показания становятся неточными. Это важно в подсчете оборотов колеса, например.

Сейчас я докажу важность прерываний простым экспериментом.

Код

Пояснения

Volatile – для работы с прерываниями мы должны создать переменную с квалификатором. Этот квалификатор объявляется, когда переменная будет изменяться за пределами того участка, где она была объявлена.

attachInterrupt(№, Function, Mode); – Объявляем о создании прерывания.

№ – определяет номер прерывания. На Arduino Uno могут обрабатываться всего два прерывания. Прерывание 0 считывается со 2 пина, прерывание 1 – с 3.

Ко второму мы и подключили кнопку.

Function – Это функция, которая будет выполняться, когда произошло прерывание.

Она не должна иметь параметров и не должна возвращать значение. Кроме того, в функции, вызываемой прерываниями не работает delay(), значение millis() не изменяется и может быть потеря данных с Serial Monitor. А переменные, используемые в функции, должны быть volatile.

Mode – Это условие, при котором будет выполняться прерывание. Всего таких условий четыре:

LOW – когда на порту LOW.

CHANGE – когда значение меняется с LOW на HIGH и наоборот.

RISING – когда значение изменяется с LOW на HIGH.

FALLING – когда значение изменяется с HIGH на LOW.

Конец?

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

Советы

У тебя есть интернет, если ты читаешь эту статью. А интернет – это кладезь полезной информации. Нужно только знать, как ее оттуда извлечь.

Ты уже научился, как обращаться с незнакомыми микросхемами. Таким образом, можно и даже нужно, обращаться и с остальными незнакомыми штуками. Если ты чего- то не понял, не стесняйся писать мне или искать в других источниках.

Главное – захотеть. А дальше разберешься.

Подготовка

Решил собрать свою схему? Похвально! С чего начать? Начни с бумаги! Сначала определись с тем, что ты хочешь от своего устройства. После этого, придумай алгоритм выполнения поставленной задачи и запиши, а лучше – зарисуй его. Когда все продумано, определись с компонентами, необходимыми для сборки. После этого, приступай к написанию кода и сборке схемы.

Ошибки

А что делать, если код не работает?

Первым делом – проверь, подключена ли плата, выбрана ли нужная плата в IDE, выбран ли порт, к которому подключена плата.

Еще нужно перестать бояться выскакивающих ошибок.

Рассмотрим пример ошибки.

Это ошибка выскакивает, когда пропущена точка с запятой, о чем и говорит «expected ‘;’ before ‘>’ token»

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

А эта, когда ты не объявил переменную.

Если перевести текст ошибки, будет « ‘Val’ не был объявлен в этой области видимости. Еще хорошо, что не объявленная переменная подсвечивается, и нет надобности ее искать.

А такая ошибка возникает, если пропущена фигурная скобка.

Дальше я рассматривать не буду. Если ты не разобрался сам, можешь загуглить ошибку или написать о ней на форуме.

Вгрузил. Не работает

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

В таких случаях нужно определить, где код выполняется как надо, а где что-то уже не так. Для этого можно воспользоваться индикаторами. Например, пищалкой или Serial monitor. Для этого, вставляем tone куда-либо в программу. Если звук произошел, то эта часть программы работает как надо. А если его не было, нужно вставить звук раньше или определить, что не так. Serial может передавать мини- отчеты о выполнении тех или иных частей программы.

Стоит уделять внимание изучению значений, получаемых с пинов, отправляемых на пины, а так же правильность объявления классов переменных. Не стоит забывать о том, что переменные объявленные в одной функции, не будут определены в другой, пока их не передать или не сделать глобальными.

Еще проблема может быть на поверхности – забыл подключить к земле. Или поставил светодиод анодом к минусу и прочие неприятные оказии. Чтобы их избежать, проводи осмотр своих схем перед запуском. И да, если что-то не так, лучше вынуть кабель питания, и только после этого, менять схему.

Отдыхай!

Хватит на сегодня, пожалуй. Можешь быть горд, что познал основы Arduino. До скорого!

RoboHobby

Детали для моделирования роботов

Категории товаров

Поиск товаров

Программирование

Уроки

Оплата и доставка

Урок 7. Прерывания в Arduino

Это седьмой урок из цикла «Знакомство с Arduino». В этом уроке Вы научитесь работать с прерываниями, а также с некоторыми режимами энергосбережения в Arduino.

Для урока Вам понадобится следующие детали:

  • Arduino Nano;
  • беспаечная макетная плата (она же breadboard);
  • провода типа папа-папа;
  • резистор номиналом 10 кОм и 220 Ом;
  • тактовая кнопка;
  • светодиод;
  • дисплей OLED 0.96».

Также Вам понадобится скачать и установить библиотеку Low Power и среду Arduino IDE. Если не знаете/забыли как это сделать, то вернитесь к уроку по среде Arduino IDE.

Для начала давайте разберёмся, что же такое прерывание. Прерывание — это сигнал, который сообщает процессору о наступлении какого-либо события, которое требует немедленного внимания. Процессор реагирует на этот сигнал, прервав выполнение всех текущих команд и передаёт управление обработчику прерывания. Обработчик прерывания — это обычная функция, в которой находятся команды, которые выполняют определённые действия, как реакцию на прерывание.

С понятием прерывания разобрались. Теперь давайте разберёмся, что же при поступлении сигнала прерывания.

Когда приходит сигнал прерывания, процессор приостанавливает выполнение текущей программы и переходит к подпрограмме обработки прерывания. Далее он выполняет те команды, которые находятся непосредственно в самом обработчике. После это процессор продолжает выполнять основную программу начиная с того места, в котором остановился.

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

  • Аппаратные (на уровне микропроцессора). Само же событие может произойти в произвольный момент времени и от внешнего устройства (нажатие на кнопку и т.д.);
  • Программные (на уровне программы). Они запускаются внутри программы при помощи специальной команды и используются для того, чтобы вызвать обработчик прерываний;
  • Внутренние (синхронные). Они возникают в результате изменения или ошибки при выполнении программы (например, при обращении к недопустимому адресу, недопустимый код операции и т.д.).

В этом уроке мы рассмотрим только аппаратные прерывания.

В модулях Arduino реализовано 4 типа аппаратных прерывания. Все они различаются уровнем сигнала на выводе модуля, на котором разрешено прерывание:

  • LOW. Прерывание срабатывает когда на выводе Arduino низкий уровень напряжения (0 В);
  • CHANGE. Прерывание срабатывает когда на выводе изменяется уровень напряжения с низкого на высокий (с 0 В на 5 В) или с высокого на низкий (с 5 В на 0 В);
  • RISING. Прерывание срабатывает когда на выводе изменяется уровень напряжения с низкого на высокий (с 0 В на 5 В);
  • FALLING.Прерывание срабатывает когда на выводе изменяется уровень напряжения с высокого на низкий (с 5 В на 0 В).
Читайте также:  Светодиоды 10 Вт характеристики

Прерывания обычно используются для задач, которые должны быть выполнены автоматически при наступлении какого либо внешнего события (считывание значения какого-либо датчика, реакция на нажатие кнопки и т.д.). Зачастую просто невозможно написать программу, которая непрерывно считывает все импульсы, не пропустив ни одного из них. Чтобы исключить такую ситуацию и используются прерывания.

Существует ряд ограничений при работе с прерываниями:

  • Обработчик прерывания не должен выполняться слишком долго по времени. Проблема в том, что модули Arduino не могут обрабатывать несколько прерываний одновременно. Пока выполняется один обработчик прерывания, все остальные прерывания останутся без внимания со стороны процессора и таким образом мы просто пропустим их. Есть довольно простой выход из этой ситуации. Если нужно выполнить довольно большой объём команд в обработчике прерывания, то нужно передавать саму обработку в цикл loop(), а в обработчике прерывания установить флаг события. Тогда в loop() нужно только проверять флаг и в соответствии с его значением выполнять обработку;
  • Нужно быть очень аккуратным с переменными, значение которых изменяется в обработчике прерывания. Переменная должна быть объявлена квалификатором volatile, если её значение может быть изменено чем-либо за пределами того участка программы, где она объявлена (например, параллельно выполняющимся процессом — прерыванием). Квалификатор volatile изменяет способ интерпретации и доступа к переменной компилятором и программой. Он указывает компилятору загрузить переменную из ОЗУ, и не из запоминающего регистра, т.к. при определенных условиях значения переменных, хранящихся в регистрах, могут оказаться неточными;
  • В обработчике прерывания нельзя использовать функцию delay(), т.к. механизм определения интервала задержки использует таймеры, а они тоже работают в прерываниях, которые заблокирует обработчик. В итоге все будут ждать всех и программа зависнет. По этой же причине нельзя использовать протоколы связи, основанные на прерываниях (например, I2C);
  • Возможна потеря данный передаваемых по последовательному соединению (Serial) в момент выполнения функции обработки прерывания.

С основной теорией по прерываниям мы теперь познакомились. Теперь пришло время попытаться сделать какой-нибудь практический пример.

В качестве примера давайте сделаем управление светодиода при помощи тактовой кнопки, а обработку нажатия клавиши будем выполнять при помощи прерывания. Воспользуйтесь следующими схемами:

Электрическая принципиальная схема:

Схема подключения на макетной плате:

Итак, схема собрана. Теперь подключите модуль Arduino к компьютеру. Далее откройте среду разработки Arduino IDE и запишите в Arduino следующую программу:

Прерывания на Arduino

Большинство информации можно найти на страницах arduino.cc по ходу текста буду добавлять ссылки, где я нашел информацию.
Переходим к прерываниям.
В случае, когда основная программа имеет сравнительно небольшой размер, все события можно обрабатывать в цикле основной программы, но что если программа большая? Если ее размер множество строк, да еще и с временными задержками? В таком случае, возможна ситуация когда внешние нажатия на кнопку могут быть не обработаны или придется долго держать кнопку нажатой. А что если это не кнопка, а внешнее устройство? Чтоб преодолеть эти сложности необходимо использовать прерывания — как только было вызвано прерывание, основной цикл программы будет приостановлен и микроконтроллер начнет исполнение кода функции, которая соответствует прерыванию, а после завершения продолжится выполнение программы там где она была до этого приостановлена.

На платах Arduino есть контакты, используются для сигнализации о прерывании. Ниже в таблице указаны номера прерываний и соответствующие им контакты на плате.

Для работы с прерываниями нет необходимости подключать какие-либо библиотеки — все они уже включены автоматически.
Синтаксис:

Параметры:
interrupt — номер вызываемого прерывания (0, 1 и т.д.).
function — называние функции, которая будет вызываться при срабатывании прерывания. Важно: Данная функция не должна получать параметры при запуске или возвращать какие-либо значения!
mode — режим. Возможные значения LOW, CHANGE, FALLING, RISING

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

Чтоб понять как и когда вызывается функция можно использовать следующий скетч.

В основном цикле программы на экран выводятся нули, но когда на входе прерывания сигнал LOW (кнопка отпущена), на экран выводятся единицы.
Т.е. Нажатие на кнопку останавливает повторяющиеся прерывания и запускает основной цикл программы.

CHANGE — прерывание вызывается при смене сигнала от LOW к HIGH и наоборот. Функция выполняется только один раз при любой смене сигнала.

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

FALLING — прерывание вызывается когда значение меняется от HIGH к LOW. Разовый вызов функции, на каждый удовлетворяющий вызову переход.

Сигнал прерывания имеет внешнее по отношению к Arduino происхождение. Его можно сравнить с периферийными устройствами компьютера. Для демонстрации я использовал кнопку и думаю это весьма распространенный способ запуска прерывания.

Платы Контакты
int0int1int2int3int4int5
Arduino Uno23
Arduino Mega2321201918
Для демонстрации режима LOW я использовал следующую схему с тактовой кнопкой. —>
Данная схема включает кнопку S1 и стягивающий резистор R1 на 10кОм. Резистор обеспечит отсутствие статических разрядов создающих «шум» и запускающих прерывание в случайном порядке.
Но при этом есть один большой недостаток данной схемы — кнопка не сразу выдает значение HIGH/LOW когда была нажата/отпущена. Есть некоторые колебания сигнала перед тем как через кнопку пойдет/прекратит_движение ток.

Вот тут есть хорошая статья как этого избежать.

Вышеприведенная схема подходит для демонстрации вызова прерывания по LOW, т.к. первичные колебания не значительны. Но она абсолютно не подходит для остальных режимов. Функция будет запущена множество раз, что может привести к некорректной работе программы.
Чтоб правильно вызывать прерывания, необходимо позаботится о корректной передаче сигнала.
Подробнее о схеме:
S1 — Кнопка.
R1 — Стягивающий резистор.
C1 — Конденсатор на 1микрофарад для сглаживания сигнала иначе значения с кнопки так и будут скакать.
OR1 — логическое или. Получив сигнал с кнопки сглаженный конденсатором он выдаст логическую единицу(HIGH) или ноль (LOW).
Подойдет не только логический элемент «или», можно использовать «и», «не», триггер Шмидта или инвертирующий триггер Шмидта. Эти компоненты среагируют только при переходе сигналом определенной границы.

Теория на этом заканчивается. Переходим к практике.

Светодиод подключенный к плате постепенно увеличивает яркость свечения, потом сбрасывается на ноль и все повторяется. Нажатие на кнопку приведет к фиксации текущей яркости на 3 секунды.

Кнопка подключена по первой схеме указанной выше. Поэтому имеет все описанные недостатки.

С прерываниями связанна еще одна важная функция

Запрещает вызов определенного прерывания.
Может использоваться когда необходимо выполнять какие-либо действия без остановки на выполнение прерываний.

Использование прерываний на Arduino

Оптимизируйте ваши программы для Arduino с помощью прерываний – простого способа для реагирования на события в режиме реального времени!

Мы прерываем нашу передачу.

Как выясняется, существует отличный (но недостаточно часто используемый) механизм, встроенный во все Arduino, который идеально подходит для отслеживания событий в режиме реального времени. Данный механизм называется прерыванием. Работа прерывания заключается в том, чтобы убедиться, что процессор быстро отреагирует на важные события. При обнаружении определенного сигнала прерывание (как и следует из названия) прерывает всё, что делал процессор, и выполняет некоторый код, предназначенный для реагирования на вызвавшую его внешнюю причину, воздействующую на Arduino. После того, как этот код будет выполнен, процессор возвращается к тому, что он изначально делал, как будто ничего не случилось!

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

Прерывания по кнопке

Начнем с простого примера: использования прерывания для отслеживания нажатия кнопки. Для начала, мы возьмем скетч, который вы, вероятно, уже видели: пример « Button », включенный в Arduino IDE (вы можете найти его в каталоге « Примеры », проверьте меню Файл → Примеры → 02. Digital → Button ).

В том, что вы видите здесь, нет ничего шокирующего и удивительного: всё, что программа делает снова и снова, это прохождение через цикл loop() и чтение значения buttonPin . Предположим на секунду, что вы хотели бы сделать в loop() что-то еще, что-то большее, чем просто чтение состояния вывода. Вот здесь и пригодится прерывание. Вместо того, чтобы постоянно наблюдать за состоянием вывода, мы можем поручить эту работу прерыванию и освободить loop() для выполнения в это время того, что нам необходимо! Новый код будет выглядеть следующим образом:

Циклы и режимы прерываний

Здесь вы заметите несколько изменений. Первым и самым очевидным из них является то, что loop() теперь не содержит никаких инструкций! Мы можем обойтись без них, так как вся работа, которая ранее выполнялась в операторе if/else , теперь выполняется в новой функции pin_ISR() . Этот тип функций называется обработчиком прерывания: его работа состоит в том, чтобы быстро запуститься, обработать прерывание и позволить процессору вернуться обратно к основной программе (то есть к содержимому loop() ). При написании обработчика прерывания следует учитывать несколько важных моментов, отражение которых вы можете увидеть в приведенном выше коде:

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

Вам, наверное, интересно: откуда мы знаем, когда запустится прерывание? Что его вызывает? Третья функция, вызываемая в функции setup() , устанавливает прерывание для всей системы. Данная функция, attachInterrupt() , принимает три аргумента:

  1. вектор прерывания, который определяет, какой вывод может генерировать прерывание. Это не сам номер вывода, а ссылка на место в памяти, за которым процессор Arduino должен наблюдать, чтобы увидеть, не произошло ли прерывание. Данное пространство в этом векторе соответствует конкретному внешнему выводу, и не все выводы могут генерировать прерывание! На Arduino Uno генерировать прерывания могут выводы 2 и 3 с векторами прерываний 0 и 1, соответственно. Для получения списка выводов, которые могут генерировать прерывания, смотрите документацию на функцию attachInterrupt для Arduino;
  2. имя функции обработчика прерывания: определяет код, который будет запущен при совпадении условия срабатывания прерывания;
  3. режим прерывания, который определяет, какое действие на выводе вызывает прерывание. Arduino Uno поддерживает четыре режима прерывания:
    • RISING – активирует прерывание по переднему фронту на выводе прерывания;
    • FALLING – активирует прерывание по спаду;
    • CHANGE – реагирует на любое изменение значения вывода прерывания;
    • LOW – вызывает всякий раз, когда на выводе низкий уровень.

И резюмируя, наша настройка attachInterrupt() соответствует отслеживанию вектора прерывания 0 (вывод 2), чтобы отреагировать на прерывание с помощью pin_ISR() , и вызвать pin_ISR() всякий раз, когда произойдет изменение состояния на выводе 2.

Volatile

Еще один момент, на который стоит указать: наш обработчик прерывания использует переменную buttonState для хранения состояния вывода. Проверьте определение buttonState : вместо типа int , мы определили его, как тип volatile int . В чем же здесь дело? volatile является ключевым словом языка C, которое применяется к переменным. Оно означает, что значение переменной находится не под полным контролем программы. То есть значение buttonState может измениться и измениться на что-то, что сама программа не может предсказать – в этом случае, пользовательский ввод.

Еще одна полезная вещь в ключевом слове volatile заключается в защите от любой случайной оптимизации. Компиляторы, как выясняется, выполняют еще несколько дополнительных задач при преобразовании исходного кода программы в машинный исполняемый код. Одной из этих задач является удаление неиспользуемых в исходном коде переменных из машинного кода. Так как переменная buttonState не используется или не вызывается напрямую в функциях loop() или setup() , существует риск того, что компилятор может удалить её, как неиспользуемую переменную. Очевидно, что это неправильно – нам необходима эта переменная! Ключевое слово volatile обладает побочным эффектом, сообщая компилятору, что эту переменную необходимо оставить в покое.

Удаление неиспользуемых переменных из кода – это функциональная особенность, а не баг компиляторов. Люди иногда оставляют в коде неиспользуемые переменные, которые занимают память. Это не такая большая проблема, если вы пишете программу на C для компьютера с гигабайтами оперативной памяти. Однако, на Arduino оперативная память ограничена, и вы не хотите тратить её впустую! Даже C компиляторы для компьютеров будут поступать точно так же, несмотря на массу доступной системной памяти. Зачем? По той же причине, по которой люди убирают за собой после пикника – это хорошая практика, не оставлять после себя мусор.

Подводя итоги

Прерывания – это простой способ заставить вашу систему быстрее реагировать на чувствительные к времени задачи. Они также обладают дополнительным преимуществом – освобождением главного цикла loop() , что позволяет сосредоточить в нем выполнение основной задачи системы (я считаю, что использование прерываний, как правило, позволяет сделать мой код немного более организованным: проще увидеть, для чего разработан основной кусок кода, и какие периодические события обрабатываются прерываниями). Пример, показанный здесь, – это самый базовый случай использования прерываний; вы можете использовать для чтения данных с I2C устройства, беспроводных передачи и приема данных, или даже для запуска или остановки двигателя.

Есть какие-нибудь крутые проекты с прерываниями? Оставляйте комментарии ниже!

Электроника для всех

Блог о электронике

AVR. Учебный Курс. Работа на прерываниях

Одним из серьезных достоинств контроллеров AVR является дикое количество прерываний. Фактически, каждое периферийное устройство имеет по вектору, а то и не по одному. Так что на прерываних можно замутить кучу параллельных процессов. Работа на прерываниях является одним из способов сделать псевдо многозадачную среду.

Идеально для передачи данных и обработки длительных процессов.

Для примера покажу буфферизированный вывод данных по USART на прерываниях.

В прошлых примерах был такой код:

// Отправка строки vo ) < SendByte(*string); string++; >> // Отправка одного символа vo >

Данный метод, очевидно, совершенно неэффективен. Дело в том, что у нас тут есть тупейшее ожидание события — поднятие флага готовности USART. А это зависит, в первую очередь, от скорости передачи данных. Например, на скорости 600 бод передача каких то 600 знаков будет длиться 9 секунд, блокируя работу всей программы, что ни в какие ворота не лезет.

Как быть?

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

Поэтому прерывания, пожалуй, будет единственным адекватным вариантов.

Итак, если брать в пример USART то у него есть три прерывания:

  • RXT — прием байта. С этим понятно, мы его уже использовали
  • TXC — завершение отправки
  • UDRE — опустошение приемного буфера

Байты TXC и UDRE обычно вызывают путаницу. Поясню разницу.

Дело в том, что регистр передачи данных UDR в AVR на самом деле куда хитрей чем кажется, он двухэтажный. На первом ярусе, собственно UDR, а ниже находится конвейер сдвигового регистра. Первый байт, попавший в пустой регистр UDR тут же проваливается на конвейер, а UDR снова опустошается. После чего конвейер неторопливо, в соответствии с битрейтом, выплевывает данные в линию, а потом снова зажевывает байт из UDR. Поэтому, фактически, в UDR за короткое время влезает сразу два байта — первый тут же проваливается, а второй ждет.

  • Флаг пустого регистра UDRE выставляется тогда, когда мы можем загнать байт в UDR,
  • Флаг окончания передачи TXC появляется только тогда, когда у нас конвейер опустел, а новых данных в UDR нет.

Да, можно слать данные и по флагу TXC, но тогда у нас будет лишняя пауза между двумя разными байтами — время на опустошение буфера. Некошерно.

Вот как это можно сделать корректней.

Вначале выводим данные в массив, либо берем его из флеша — не важно. Для простоты запихну массив в ОЗУ. Код возьму из прошлой статьи:

#define buffer_MAX 16 // Длина текстового буффера char buffer[buffer_MAX] = “0123456789ABCDEF”; // А вот и он сам u08 buffer_index=0; // Текущий элемент буффера

Инициализация интерфейса выглядит стандартно:

//InitUSART UBRRL = LO(bauddiv >

Обратите внимание, что прерывания UDRE мы не разрешаем. Это делается потом. Иначе сразу же, на старте, контроллер ускачет на это прерывание, т.к. при пуске UDR пуст и мы получим черти что.

Отправка выглядит элементарно. Вначале пихаем первый байт нашего сообщения, не забыв выставить правильно индекс. А дальше разрешаем прерывание по UDRE оно само улетит куда надо, по прерываниям:

(1 >8) #define LO(x) ((x)& 0xFF) //Init UART UBRRL = LO(bauddiv >

Если грузануть его в Pinboard, предварительно подключив USART к FT232 и законнектиться терминалкой, то будет мигать наш LED4, а в терминалку от стрелятся байты ASCII кодов нашей строки. В это же время будет неторопливо тикать наш цикл с мигалкой.

118 thoughts on “AVR. Учебный Курс. Работа на прерываниях”

UDRE — опустошение приемного буфера
Может «передающего» или имелось ввиду приёмный для отправки?

Да это и имелось в виду. Прием данных для последующей отправки.

buffer_index дожен быть помечен как volatile, если мы работам с ним из прерывания. Это очень важный момент. Если данная программа скорее всего будет нормально работать, то при работе с buffer_index вне прерывания при включенной оптимизации могут начаться косяки. ИМХО, стоит указать это в статье и объяснить, почему так. Нубы, которые только начинают изучать Си, часто с этим сталкиваются 🙂

В данном случае нет. Т.к. не меняется нигде кроме как в прерывании 🙂

так в этом то и фишка! в коде программы оно (buffer_index) грузицца в регистр и там и остается (оптимизация), а в это время оно, в прерывании, меняется, а значение, которое в регистре, осталось без изменений. вот чтоб этого не случилось и имеет смысл (акадэмический) объявлять переменные, изменяемые в прерывательских процедурах как volatile.

Нифига. Переменная глобальная. Так что любые модификации ее затрагивают именно ее изменения.

Мы вошли в прерывание, изменили и вышли. Компилятор не может ее сныкать в регистр, т.к. ему четко сказано записать ее в память — глобальную переменную. Она тут никак не останется только на уровне регистров. Волатиль тут не нужна!

А вот если бы была конструкция

Ну в данном случае, может и покатит, но если вдруг захочется в main сделать что-то ещё с этой переменной могут вылезти неожиданные глюки =) Так что лучше сразу объявить её volatile и не бояться подземных грабель.
Самое смешное, что GCC оптимизирует даже регистровые переменные — если интересно, я как-то писал про то, как наступил на эти грабли: http://gremlinable.livejournal.com/6808.html

Да, забавно ещё заметить, что в IAR ключевое слово volatile обозначает вообще совершенно другое… =)

Захочу в майн ее вкорячить, то тогда и добавлю.

А на всякий случай можно вообще оптимизацию вырубить 😉

Ну что ты добавишь, я не сомневаюсь, а вот ньюб, который учится по твоим статьям — не факт, только если наши комменты читать будет, а на это не у каждого хватит времени и нервов… =)

Читать статьи надо по порядку 😉 Вначале Сишного курса я рассказывал про эту фишку.

Практика жизни такова, что возникшую проблему решают побыстрее, не вчитываясь в предыдущие статьи. Поэтому мне хорошо понятна позиция комментатора)
А за статью — спасибо!

И мне кажется более красивым способ, если сделать buffer_index типа char*.
Тогда вначале мы присваиваем ему адрес буфера: buffer_index=buffer;
А потом делаем так:
ISR (USART_UDRE_vect)
<
if(*buffer_index) // Вывели весь буффер?
<
UDR = *buffer_index; // Берем данные из буффера.
buffer_index++; // Увеличиваем индекс
> else <
UCSRB &=

Да, согласен. Но менее наглядно для нуба 🙂

И да, для данного случая это применимо. А если у нас не ASCIIZ? Или нуль в буффере вылезет?

Это применимо только для текста, да.

Объяснение базисов, что как работает это очень хорошо, но в жизни советую использовать что нибудь вроде usart из gcc-libavr

И чем оно лучше? Вряд ли будет компактней чем самописное минималистичное решение.

Зато код некрасивой платформозависимой грязюкой не заляпан.

Плюс часто позволяет иногда не наступать на неочевидные грабли

Попутно расставляя другие. Тем более данные функции, ЕМНИП, существуют только в avr-libc и при переносе на IAR или CVAVR вылезут в полный рост.

А все платформозависимые функции выносятся в HAL и там и остаются.

Ну на самом деле это дело вкуса/надобности:
Хочешь изучать платформу, и.т.д — пиши сам.
Нужно писать продукт (и как часто бывает — быстро писать) — юзай либы.

Спасибо за статью. В свое время очень нужна была эта информация, потом разобрался сам, писал на ассемблере.

а вот очень простая реализация буфера FIFO на асме. bufferstart и bufferend — адрес начала и конца буфера в СРАМ, buffervalue — регистр для обмена данными с буфером. буфер закольцован, когда он полон данные перестают записываться и выставляется флаг bufferfull, а когда пуст перестают считываться и выставляется bufferempty.

; BUFFER_WRITE: sbrc flag,fbufferfull ;если не установлен флаг “буфер полон” пропускаем rjmp BUFFER_WRITE_EXIT ;иначе на выход st X+,buffervalue ;заносим значение в буфер, увеличиваем указатель записи cbr flag,(1

    таким образом, при передаче пакета данных можно выделить буфер в памяти и в основном цикле после проверки флага «буфер полон» заполнять его необходимыми данными для отправки и разрешать прерывание UDR, а в прерывании по UDR считывать значение из буфера и если он опустошился — запрещать прерывание UDR. это позволит отправить пакет данных без пауз.

    Курс arduino – прерывания, создание функций, советы

    Программируем Arduino. Профессиональная работа со скетчами

    Переводчик А. Макарова

    Технический редактор Н. Суслова

    Литературный редактор Н. Рощина

    Художники Л. Егорова, С. Маликова

    Корректоры С. Беляева, Н. Витько

    Верстка Л. Егорова

    Программируем Arduino. Профессиональная работа со скетчами . — СПб.: Питер, 2017.

    © ООО Издательство “Питер”, 2017

    Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.

    Доктор Саймон Монк (Dr. Simon Monk; Престон, Соединенное Королевство) имеет степень бакалавра в области кибернетики и информатики, а также доктора наук в области программной инженерии. Доктор Монк несколько лет занимался академической наукой, прежде чем уйти в промышленность. Является одним из основателей компании Momote Ltd, специализирующейся на разработке программного обеспечения для мобильных устройств. Со школьных лет активно увлекается электроникой и много пишет для радиолюбительских журналов об электронике и открытом аппаратном обеспечении. Автор многочисленных книг по электронике, посвященных в основном открытым аппаратным платформам, особенно Arduino и Raspberry Pi. В соавторстве с Полом Шерцем написал третье издание книги «Practical Electronics for Inventors».

    Вы можете последовать за Саймоном в Twitter, где он зарегистрирован как @simonmonk2.

    Хочу выразить большую признательность издательству McGraw-Hill Education, сотрудники которого приложили массу усилий, чтобы выпустить эту книгу. Отдельное спасибо моему редактору Роджеру Стюарту (Roger Stewart), а также Ваставикте Шарма (Vastavikta Sharma), Джоди Маккензи (Jody McKenzie), Ли-Энн Пикрелл (LeeAnn Pickrell) и Клер Сплан (Claire Splan).

    Хочу также поблагодарить компании Adafruit, SparkFun и CPC за предоставленные модули и компоненты, использовавшиеся при подготовке этой книги.

    И напоследок, но не в последнюю очередь, спасибо Линде за ее терпение и великодушие, благодаря которым я смог написать эту книгу.

    Arduino — стандартный микроконтроллер, получивший широкое признание у инженеров, мастеров и преподавателей благодаря своей простоте, невысокой стоимости и большому разнообразию плат расширения. Платы расширения, подключаемые к основной плате Arduino, позволяют выходить в Интернет, управлять роботами и домашней автоматикой.

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

    Эта книга задумана как продолжение бестселлера «Programming Arduino: Getting Started with Sketches»1. Несмотря на то что эта книга включает краткое повторение основ из книги «Programming Arduino», она познакомит читателя с более продвинутыми аспектами программирования плат Arduino. В частности, эта книга расскажет, как:

    • обеспечить эффективную работу при минимальном объеме доступной памяти;

    • решать сразу несколько задач без помощи механизмов многопоточного выполнения;

    • упаковывать код в библиотеки, чтобы им могли пользоваться другие;

    • использовать аппаратные прерывания и прерывания от таймера;

    • добиться максимальной производительности;

    • уменьшить потребление электроэнергии;

    • взаимодействовать с последовательными шинами разных типов (I2C, 1-Wire, SPI и последовательный порт);

    • взаимодействовать с портом USB;

    • взаимодействовать с сетью;

    • выполнять цифровую обработку сигналов (Digital Signal Processing, DSP).

    Книга включает 75 примеров скетчей, которые распространяются в открытом виде и доступны на веб-сайте автора www.simonmonk.org. Перейдя по ссылке на страницу этой книги, вы сможете загрузить исходный код примеров, а также самый актуальный список ошибок и опечаток, найденных в книге.

    Что необходимо для чтения книги

    Данная книга в первую очередь посвящена вопросам программирования. Поэтому для опробования большинства примеров будет достаточно платы Arduino, светодиода и мультиметра. Если у вас имеются дополнительные платы расширения Arduino, они тоже пригодятся. Для рассмотрения примеров из главы 12 вам понадобится плата Ethernet или Wi-Fi. На протяжении всей книги мы будем использовать разные модули для демонстрации разных интерфейсов.

    В центре внимания находится Arduino Uno — наиболее широко используемая плата Arduino, но в главах, посвященных программированию порта USB и цифровой обработке сигналов, рассматриваются некоторые особенности других плат Arduino, таких как Leonardo и Arduino Due.

    В приложении в конце книги вы найдете список поставщиков, у которых сможете приобрести все эти компоненты.

    Как работать с этой книгой

    Каждая глава посвящена отдельной теме, связанной с программированием Arduino. Главы книги, кроме главы 1, где приводится краткий обзор основ Arduino, можно читать в любом порядке. Если вы опытный разработчик, начните с главы 14, чтобы вникнуть в некоторые особенности программирования Arduino.

    Далее следует краткое описание глав.

    Глава 1 «Программирование Arduino». Эта глава содержит сводную информацию о программировании Arduino. Это учебник для тех, кому требуется быстро ознакомиться с основами Arduino.

    Глава 2 «Под капотом». В этой главе мы заглянем под капот и посмотрим, как работают программы для Arduino и откуда они берутся.

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

    Глава 4 «Ускорение Arduino». Платы Arduino оснащены маломощными процессорами с невысоким быстродействием, поэтому иногда требуется выжать из них все, что только можно. Например, встроенная функция digitalWrite надежна и проста в использовании, но неэффективна, что особенно заметно, когда требуется одновременно включить несколько выходов. В этой главе вы узнаете, как увеличить ее производительность, а также познакомитесь с другими приемами создания быстродействующих скетчей.

    Глава 5 «Снижение потребления электроэнергии». Когда для питания платы Arduino используются аккумуляторы или солнечные батареи, желательно уменьшить потребление электроэнергии. Этого можно добиться не только оптимизацией конструкции устройства, но и применением особых приемов программирования.

    Глава 6 «Память». В этой главе мы посмотрим, как уменьшить потребление памяти, а также познакомимся с достоинствами и недостатками, связанными с динамическим распределением памяти в скетчах.

    Глава 7 «Интерфейс I2C». Интерфейс I2C на плате Arduino может существенно упростить взаимодействие с модулями и компонентами и позволит обойтись меньшим числом контактов на плате. Эта глава описывает, как действует интерфейс I2C и как им пользоваться.

    Глава 8 «Взаимодействие с устройствами 1-Wire». В этой главе рассказывается о шине 1-Wire для связи с устройствами, такими как датчики температуры компании Dallas Semiconductor, которые часто применяются с платами Arduino. Здесь вы узнаете, как действует эта шина и как ею пользоваться.

    Рейтинг
    ( Пока оценок нет )
    Загрузка ...
    Adblock
    detector