Электротехнический интернет-журнал Electrik.info

"Электрик Инфо" - онлайн журнал про электричество. Теория и практика. Обучающие статьи, примеры, технические решения, схемы, обзоры интересных электротехнических новинок. Уроки, книги, видео. Профессиональное обучение и развитие. Сайт для электриков и домашних мастеров, а также для всех, кто интересуется электротехникой, электроникой и автоматикой.
 


Услуги электрика | Схемы подключения | Принципиальные схемы | Электроснабжение Розетки и выключатели | Автоматы защиты | Кабель и провод | Монтаж электропроводки Ремонт электротехники | Молодому электрику | Электротехническая продукция

Электрик Инфо » Схемы на микроконтроллерах » Способы чтения и управления портами ввода-вывода Arduino
17 декабря 2018
Количество просмотров: 91671
Комментарии к статье: 9


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

Для взаимодействия с окружающим миром нужно настроить выводы микроконтроллера на приём или передачу сигнала. В результате каждый пин будет работать в режиме входа и выхода. На всеми любимой плате 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, так, как если бы вы работали с отдельным микроконтроллером. Но обо всем по порядку. Чтобы управлять портами Ардуино таким образом вам нужно сначала внимательно рассмотреть следующую иллюстрацию.

Порты микроконтроллера Atmega168

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

Порты микроконтроллера Atmega328

Здесь вы видите соответствие выводов Ардуино и названий портов атмеги. Итак, у нас есть 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, она аналогична и приведена для примера.

Структура порта D

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

Калькулятор Windows

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

Режим калькулятора программист

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

Перевод чисел на калькуляторе

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

Перенос числа в в Arduino IDE

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

Внесение данных в каждый пин

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

Разбор записи

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

Операция логического сложения

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

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

0+0=1

1+0=1

0+1=1

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

Логическое умножение и инвертирование

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

 

Умножение содержимого порта на число

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

Умножаем содержимое порта на ноль, сдвинутый на 5 бит

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

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

Чтение с портов

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

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

Заключение

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

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

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

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





Поделитесь этой статьей с друзьями:


Другие статьи с сайта Электрик Инфо:

  • Подключение и программирование Ардуино для начинающих
  • Как подключить инкрементальный энкодер к Ардуино
  • Микроконтроллеры PIC для начинающих
  • Дистанционное управление микроконтроллером: ИК-пульт, Arduino, ESP8266, 433 ...
  • Подключение аналоговых датчиков к Ардуино, считывание показаний датчиков
  • Измерение температуры и влажности на Arduino – подборка способов
  • Виды и устройство микроконтроллеров AVR
  • Ардуино и шаговый двигатель: основы, схемы, подключение и управление
  • Какую плату Arduino выбрать
  • Микроконтроллеры программируемые на JavaScript: какой выбрать, характеристи ...
  • Категория: Схемы на микроконтроллерах

    Цифровая электроника, Программирование Ардуино, Arduino для начинающих

      Комментарии:

    #1 написал: Kipovets | [цитировать]

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

    0+0=1 " (c)

    Небольшая оплошность: 0+0=0.

      Комментарии:

    #2 написал: chugou | [цитировать]

    Kipovets, он, наверно, хотел сказать:

    1 + 1 = 1

      Комментарии:

    #3 написал: Алексей Бартош | [цитировать]

    Kipovets,
    Банальная опечатка! Видите как хорошо, что на нашем портале сидят специалисты! Приходится делать только годный контент!

      Комментарии:

    #4 написал: Serg | [цитировать]

    В заключительной части написано PORTB |= 1 << 5 ... if (digitalRead(12) == 1). Но 5 пин порта B, это 13 пин ардуино. Или я ошибаюсь?!

      Комментарии:

    #5 написал: p-a-h-a | [цитировать]

    If (PINB==B00010000){} не аналогично тому, если бы мы написали if (digitalRead(12) == 1)
    скорее аналог 
    (digitalRead(12) == 1)&&(digitalRead(13) == 1)&&(digitalRead(14) == 1)&&(digitalRead(15) == 1)&&(digitalRead(11) == 1)... и так 8 пинов порта

    Тут нужно либо так:
    if (
    !(~PORTB&(1 << PB4))){}// возвращает 0 или 1
    либо так:
    if (PORTB&(1 << PB4)){} // возвращает сдвинутую единицу = 16, DEC
    либо так:
    if (
    bit_is_set(PORTB,4)){}// возвращает сдвинутую единицу = 16, DEC

      Комментарии:

    #6 написал: Electronik83 | [цитировать]

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

    loop {
      PORTB.5 = !PINB.5;
      delay(500);
    }

    А? Тут фишка такая, что читать из PORTB мы не можем, поэтому юзаем PINB.

    Это аналогично ардуиновскому:

    digitalWrtie(pin, !digitalRead(pin)) ;

      Комментарии:

    #7 написал: Shagrat | [цитировать]

    Electronik83,
    Если ты так сделаешь, то нифига не прочитаешь из pinb.5, потому как ddrb.5 =0. Тут надо именно из portb.5, читать. Кто сказал, из port читать нельзя? :)
      Комментарии:

    #8 написал: Виктор | [цитировать]

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

      Комментарии:

    #9 написал: Pravdafon | [цитировать]

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

    ПОХОЖЕ НА ОПЕЧАТКУ...

    Команда PORTB B00100000 переводит PB5 в состояние логической единицы ДВА РАЗА!!!

    Добавление комментария
    Имя:*
    Комментарий:

    Присоединяйтесь к нам в социальных сетях:

    ВКонтакте | Facebook | Одноклассники | Электрик Инфо на Яндекс Дзен

     


    Популярные разделы сайта:

    Электрика дома  Электрообзоры  Энергосбережение
    Секреты электрика Источники света Делимся опытом
    Домашняя автоматика Электрика для начинающих
    Практическая электроника Электротехнические новинки
    Андрей Повный - все статьи автора



    Copyright © 2009-2022 Электрик Инфо - Electrik.info, Андрей Повный
    Вся информация на сайте предоставлена в ознакомительных и познавательных целях.
    За применение этой информации администрация сайта ответственности не несет.
    Перепечатка материалов сайта запрещена.