Способы чтения и управления портами ввода-вывода Arduino

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

Способ первый – стандартный язык для Arduino IDE

Всем известно, что Ардуино программируется на C++ с некоторой адаптацией и упрощениями для новичков. Он называется Wiring. Изначально все порты ардуино определяются как входы, и нет нужды задавать это в коде.

Порты обычно прописываются в функции инициализации переменных:

void setup ()

{

// код

}

Для этого используется команда pinMode, у неё достаточно простой синтаксис, сначала указывается номер порта, затем его роль через запятую.

pinMode (nomer_porta, naznachenie)

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

Есть три режима в которых может работать порт: INPUT – вход, в этом режиме происходит считывание данных с датчиков, состояния кнопок, аналогового и цифрового сигнала. Порт находится в т.н. высокоимпедансном состоянии, простыми словами – у входа высокое сопротивление. Устанавливается это значение, на примере 13 пина платы, при необходимости так:

pinMode (13, INPUT);

OUTPUT – выход, в зависимости от команды прописанной в коде порт принимает значение единицы или нуля. Выход становится своего рода управляемым источником питания и выдаёт максимальный ток (в нашем случае 20 мА и 40 мА в пике) в нагрузку к нему подключенную. Чтобы назначить порт как выход на Arduino нужно ввести:

pinMode (13, OUTPUT);

INPUT_PULLUP – порт работает как вход, но к нему подключается т.н. подтягивающий резистор в 20 кОм.

Условную внутреннюю схему порта в таком состоянии вы видите ниже. Особенностью этого входа является то, что входной сигнал воспринимается как проинвертированный («единица» на входе воспринимается микроконтроллером как «ноль»). В таком режиме вы можете не использовать внешние подтягивающие резисторы при работе с кнопками.

pinMode (13, INPUT_PULLUP);

Данные принимаются с портов и передают на них командами:

digitalWrite(пин, значение) – переводит выходной пин в логическую 1 или 0, соответственно на выходе появляется или исчезает напряжение 5В, например digitalWrite (13, HIGH) – подаёт 5 вольт (логическую единицу) на 13 пин, а digitalWrite (13, low) – переводит 13 пин в состояние логического ноля (0 вольт);

digitalRead(пин) – считывает значение со входа, пример digitalRead (10), считывает сигнал с 10 пина;

analogRead(пин) – считывает аналоговый сигнал с аналогового порта, вы получаете значение в диапазоне от 0 до 1023 (в пределах 10-битного АЦП), пример analogRead (3).

Способ два – управление портами через регистры Atmega и ускорение работы кода

Такое управление конечно простое, но в этом случае есть два недостатка – большее потребление памяти и низкое быстродействие при работе с портами. Но вспомните что такое Arduino независимо от варианта платы (uno, micro, nano)? В первую очередь, это микроконтроллер AVR семейства ATMEGA, в последнее время используется МК atmega328.

В Arduino IDE вы можете программировать на родном для этого семейства языке C AVR, так, как если бы вы работали с отдельным микроконтроллером. Но обо всем по порядку. Чтобы управлять портами Ардуино таким образом вам нужно сначала внимательно рассмотреть следующую иллюстрацию.

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

Здесь вы видите соответствие выводов Ардуино и названий портов атмеги. Итак, у нас есть 3 порта:

PORTB;

PORTC;

PORTD.

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

У Atmega есть три регистра длиной в 8 бит, которые управляют состоянием портов, например, порта B разберемся в их назначении проведя аналогии со стандартными средствами wiring описанными в начале статьи:

PORTB – Управление состоянием вывода. Если пин находится в режиме «Выхода», то 1 и 0 определяют наличие этих же сигналов на выходе. Если же пин находится в режиме «Входа», то 1 подключает подтягивающий резистор (тоже что и INPUT_PULLUP рассмотренный выше), если 0 – высокоимпедансное состояние (аналог INPUT);

PINB – регистр чтения. Соответственно в нём находится информация о текущем состоянии выводов порта (логическая единица или ноль).

DDRB – регистр направления порта. С его помощью вы указываете микроконтроллеру чем является порт – входом или выходом, при этом «1» - выход, а «0» - вход.

Вместо буквы «В» может быть любая другая согласно названиям портов, например, PORTD или PORTC аналогично работают и другие команды.

Научитесь разрабатывать устройства на базе микроконтроллеров и станьте инженером умных устройств с нуля: Инженер умных устройств

Помигаем светодиодом, заменим стандартную функцию digitalWrite(). Для начала вспомним как выглядит исходный пример из библиотеки Arduino IDE.

Это код всем известного «blink», который демонстрирует мигание светодиодом, встроенным в плату.

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

Команда PORTB B00100000 переводит PB5 в состояние логической единицы, смотрим, а те картинки и таблицу что расположены ниже и видим, что PB5 соответствует 13 пину Ардуины.

Буква «В» перед цифрами говорит о том, что мы в записываем значения в двоичном виде. Нумерация в двоичном коде идёт справа налево, т.е. здесь единица стоит в шестом с правого края бите, что говорит микроконтроллеру о взаимодействии с состоянием шестого бита регистра порта B (PB5). В таблице ниже изображена структура порта D, она аналогична и приведена для примера.

Вы можете задавать значение не в двоичном, а в шестнадцатеричном виде, например, для этого открываем калькулятор windows и в режиме «ВИД», выбираем вариант «Программист».

Вводим желаемое число:

И нажимаем на HEX:

В таком случае переносим это всё в Arduino IDE, но уже вместо приставки «В» будет «0х».

Но при таком вводе возникает проблема. Если у вас к другим пинам подключено что-либо, то внося команду типа B00010000 – вы все выводы кроме 13 (PB5) обнулите. Вы можете вносить данные на каждый пин по отдельности. Это будет выглядеть следующим образом:

Такая запись может показаться непонятной, давайте разберемся.

Это операция логического сложения, |= значит прибавить к содержимому порту что-либо.

А это значит, что нужно сложить слово из 8 бит в регистре с единицей, смещенной на 5 бит – в результате, если было 11000010 получается 11010010. На этом примере видно, что изменился только PB5, остальные биты этого регистра остались без изменений, как и остались неизменными состояния выводов микроконтроллера.

Но при логическом сложении возникает проблема – вы не можете превратить единицу в ноль, потому что:

0+0=1

1+0=1

0+1=1

Нам на помощь придёт логическое умножение и инвертирование:

&= значит умножить содержимое порта на определенное число.



А это число, на которое мы умножает. Знак «~» обозначает инвертирование. В нашем случае проинвертированная единица является нулем. То есть мы умножаем содержимое порта на ноль, сдвинутый на 5 бит. Например, было 10110001, стало 10100001. Остальные биты остались без изменений.

Тоже самое можно сделать с помощью операции инвертирования (^):



Чтение с портов, аналог digitalRead() выполняют с помощью регистра PIN, на практике это выглядит так:

Здесь мы проверяем равно ли выражение в скобках реальному состоянию портов, т.е. аналогично тому, если бы мы написали if (digitalRead(12) == 1).

Пошаговое обучение программированию и созданию устройств на микроконтроллерах AVR: Программирование микроконтроллеров для начинающих

Заключение

Для чего такие сложности с управлением портами, если можно использовать стандартные удобные функции? Всё дело в быстродействии и размерах кода. При использовании второго способа, рассмотренного в статье размер кода, значительно снижается, а быстродействие увеличивается на несколько порядков. Стандартный digitalWrite() выполнялся за 1800 мкс, а запись прямо в порт за 0,2 мкс, а digitalRead() за 1900 мкс, а стал также за 0,2 мкс. Этот способ управления был найден на просторах сети и часто встречается в коде готовых проектов.

Смотрите также:

7 учебных курсов по работе с Ардуино, онлайн обучение проектированию и конструированию электронной аппаратуры

Алексей Бартош


Источник: http://electrik.info