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

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


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

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


Способы чтения и управления портами ввода-вывода 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 мкс. Этот способ управления был найден на просторах сети и часто встречается в коде готовых проектов.

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

Популярные публикации:

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

Подписывайтесь на канал в Telegram про электронику для профессионалов и любителей: Практическая электроника на каждый день



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


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

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

    Цифровая электроника, Программирование Ардуино, 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 в состояние логической единицы ДВА РАЗА!!!

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

    #10 написал: Михаил |

    Пины на Arduino могут быть назначены как входы, так и выходы. Для назначения пина на вход или выход, используется функция pinMode(). Например, чтобы назначить пин 2 на выход, используется следующий код: pinMode(2, OUTPUT);

    А чтобы назначить пин 3 на вход, используется следующий код: pinMode(3, INPUT);

    После того как пин был назначен на вход или выход, можно считывать или записывать значения на этот пин. Для записи значения на пин используется функция digitalWrite(), а для чтения значения с пина используется функция digitalRead().

    Например, чтобы записать значение HIGH на пин 2, используется следующий код: digitalWrite(2, HIGH);

    А чтобы считать значение с пина 3, используется следующий код: int value = digitalRead(3);

    Здесь value будет равен либо HIGH (если на пине 3 подано напряжение), либо LOW (если на пине 3 нет напряжения). Важно знать, что на одном пине можно одновременно использовать как вход, так и выход, но не одновременно. То есть, если пин назначен на вход, то нельзя записывать значения на этот пин, и наоборот, если пин назначен на выход, то нельзя считывать значения с этого пина.

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

    #11 написал: Алексей |

    Очень интересно. Спасибо!

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

    #12 написал: Гость |

    Цифровые входы/выходы (D0-D13 на Arduino Uno) - используются для управления внешними устройствами, такими как светодиоды или моторы, и могут принимать значения только 0 (низкий уровень) или 1 (высокий уровень). Аналоговые входы (A0-A5 на Arduino Uno) - используются для измерения напряжения от датчиков и могут принимать любое значение от 0 до 5 В. ШИМ-выходы (контакты 3, 5, 6, 9, 10, 11 на Arduino Uno) - могут выдавать сигнал с широтно-импульсной модуляцией (ШИМ), который используется для управления яркостью светодиодов или скоростью мотора.

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

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

     

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

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



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