GPIO #
У любого микроконтроллера есть аппаратный блок GPIO - порт ввода-вывода общего назначения (General Purpose Input/Output). GPIO позволяет подавать напряжение на ножки МК или наоборот, считывать с них внешнее напряжение. При помощи GPIO микроконтроллер может общаться с внешними устройствами, подавать сигналы на реле и моторы, опрашивать сигналы с кнопок и датчиков так далее - это базовая возможность МК.
GPIO работает с цифровыми сигналами, то есть только напряжение 0V и VCC (напряжение питания МК). GPIO не может измерить или выдать промежуточное напряжение между этими границами
GPIO вкупе с возможностью работать по заданной программе даёт микроконтроллеру безграничные возможности по созданию различного рода электронных проектов, в которых один программируемый МК может заменить собой огромную и очень сложную схему, а любые доработки и исправления просто вносятся в текст программы.
Распиновка #
На отладочной плате не все пины являются GPIO. Чтобы понять, что и куда можно подключать, существует распиновка (pinout) - "карта" пинов с указанными функциями каждого. Как обсуждалось ранее, МК имеет множество аппаратных блоков, некоторые из них имеют "физические" выводы на пины. Так как количество пинов ограничено, то на некоторых из них объединено сразу несколько функций, что нужно учитывать при выборе МК под конкретную задачу - при использовании одной функции другая скорее всего станет недоступна.
Рассмотрим распиновку Arduino Nano и найдём на ней GPIO, они отмечены прямоугольниками с серым фоном:
В МК AVR пины объединены в пОрты и имеют не очень удобную нумерацию (прямоугольники с белым фоном на распиновке), поэтому на плате Arduino для удобства предусмотрена другая нумерация: от 0 по порядку
Режим работы #
Пины GPIO могут работать в разных режимах - как минимум вход (чтение напряжения) и выход (выдача напряжения), в некоторых случаях это может быть только вход или только выход - см. документацию на конкретную плату, в Arduino Nano таких пинов нет.
В Arduino режим работы пина настраивается при помощи функции pinMode(пин, режим)
(режим пина):
пин
- цифра, номер пина GPIO, соответствует номеру на распиновке. На некоторых неофициальных платах подписи на плате не соответствуют номерам GPIO, в этом случае нужно смотреть распиновку или документацию на платурежим
- режим работы пина, константа:OUTPUT
- выходINPUT
- вход (по умолчанию)INPUT_PULLUP
- вход, подтянутый к питанию- Могут быть и другие нестандартные режимы, см. документацию на плату
По умолчанию все пины установлены в режим INPUT
OUTPUT #
Пин в режиме выхода является источником напряжения, по сути в этом режиме к пину подключается транзистор внутри МК, который может подключить пин к GND или VCC питания микроконтроллера. Так как МК - логическое, цифровое устройство - GPIO может отдавать только сигналы, а не являться источником питания. Например для МК AVR (Arduino Nano, UNO, Mega) максимальный ток пина - около 40mA, у других МК может быть больше или меньше, см. документацию на конкретную модель. Некоторые МК имеют силовые пины с усиленными транзисторами (lgt8f328p).
Это означает, что нельзя подключать пины напрямую к мощной нагрузке, такой как моторы, светодиодные ленты, нагреватели - её нужно подключать через дополнительный драйвер/реле/транзистор
Также нельзя подключать пин в режиме выхода к GND или VCC питания - будет короткое замыкание
В то же время 40 мА это достаточно большой ток, которым можно питать цифровые модули и микросхемы, а также светодиоды. На отладочных платах к одному из пинов обычно подключен "отладочный" светодиод, которым можно мигать и тем самым проверять работу программы без подключения дополнительного оборудования. Давайте этим и займёмся! Найдите на распиновке Arduino Nano выше пин, на котором есть LED - это пин 13
.
Для большинства Arduino-совместимых плат в программе доступна стандартная константа LED_BUILTIN
(встроенный светодиод) - она соответствует номеру пина, к которому подключен светодиод. Это удобно - можно мигать светодиодом на разных платах без изменения кода и обращения к распиновке
Для подачи сигнала на пин используется функция digitalWrite(пин, сигнал)
(цифровая запись):
пин
- номер GPIO по распиновкесигнал
- цифровой сигнал - константа или численное значение:LOW
или0
- низкий сигнал, подключить к GND питанияHIGH
или1
- высокий сигнал, подключить к VCC питания
По умолчанию переведённый в OUTPUT
пин имеет состояние LOW
void setup() {
pinMode(10, OUTPUT); // D10 как выход
pinMode(A3, OUTPUT); // A3 как выход
pinMode(19, OUTPUT); // A5 как выход (Nano/UNO)
digitalWrite(10, HIGH); // высокий сигнал на D10
digitalWrite(A3, 1); // высокий сигнал на A3
digitalWrite(19, LOW); // низкий сигнал на A5
}
void loop() {}
Примеры #
Blink #
Напишем классический пример Blink (мигание) - мигание светодиодом раз в секунду:
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // сделать пин выходом
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // подать высокий сигнал
delay(500); // ждать 500 мс
digitalWrite(LED_BUILTIN, LOW); // подать низкий сигнал
delay(500); // ждать 500 мс
}
Загрузите данную программу и найдите на плате мигающий светодиод. Измените значения задержек, чтобы светодиод мигал с другой скоростью. Например 100/100 или 500/50.
RGB #
Один светодиод - скучно, давайте подключим три. Удобнее делать это c RGB модулем - минус к GND, а пины цветов - на GPIO:
Вручную повключаем разные цвета:
// пины светодиодов
#define LED_R 2
#define LED_G 3
#define LED_B 4
void setup() {
// настройка пинов как выходы
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
}
void loop() {
// мигнуть красным
digitalWrite(LED_R, 1);
delay(300);
digitalWrite(LED_R, 0);
delay(300);
// мигнуть зелёным
digitalWrite(LED_G, 1);
delay(300);
digitalWrite(LED_G, 0);
delay(300);
// мигнуть синим
digitalWrite(LED_B, 1);
delay(300);
digitalWrite(LED_B, 0);
delay(300);
}
Светодиод будет мигать по очереди каждым цветом с паузой между переключениями.
Несколько светодиодов #
Подключим несколько светодиодов к отдельным пинам:
Для удобства создадим массив пинов, чтобы было проще перебирать пины светодиодов в цикле.
Первый эффект - светодиоды зажигаются по очереди и гаснут в том же порядке:
// пины светодиодов
const uint8_t pins[] = {2, 3, 4, 5, 6};
void setup() {
// настроить как выходы
for (uint8_t p : pins) pinMode(p, OUTPUT);
}
void loop() {
// включить все
for (uint8_t p : pins) {
digitalWrite(p, HIGH);
delay(200);
}
// выключить все
for (uint8_t p : pins) {
digitalWrite(p, LOW);
delay(200);
}
}
Второй эффект - светодиоды зажигаются по очереди и гаснут в обратном порядке:
const uint8_t pins[] = {2, 3, 4, 5, 6};
void setup() {
for (uint8_t p : pins) pinMode(p, OUTPUT);
}
void loop() {
for (uint8_t p : pins) {
digitalWrite(p, HIGH);
delay(200);
}
for (int i = 0; i < 5; i++) {
digitalWrite(pins[4 - i], LOW); // в обратном порядке
delay(200);
}
}
Третий эффект - светодиоды зажигаются и гаснут по одному:
const uint8_t pins[] = {2, 3, 4, 5, 6};
void setup() {
for (uint8_t p : pins) pinMode(p, OUTPUT);
}
void loop() {
for (uint8_t p : pins) {
digitalWrite(p, HIGH);
delay(200);
digitalWrite(p, LOW);
delay(200);
}
}