25 KiB
title | date | draft | tags | ||
---|---|---|---|---|---|
📟 Начало работы с Arduino | 2023-02-24T16:46:09+03:00 | true |
|
⚠️ Статья носит информационный характер и находится в процессе написания.
Есть вероятность, что через некоторое время, части текста не будет.
Цифровые и аналоговые пины и их назначение
Все пины можно разделить на несколько видов, различие будет только в количестве данных выводов на различных платах.
- Power Pins — порты питания, режим их работы нельзя запрограммировать или изменить.
Они выдают стабилизированное напряжение 5V или 3,3V,
Vin
выдает напряжение от источника питания,
аGND
— это заземление (общий минус) - PWM Pins — порты с ШИМ модуляцией, которые можно запрограммировать,
как цифровой выход/вход. Данные порты обозначены на плате знаком тильда (˜) - Analog In — порты, принимающие аналоговый сигнал от датчиков, работают на вход.
Данные порты тоже можно запрограммировать, как цифровой вход/выход.
Данные пины не поддерживают ШИМ модуляцию.
Режим пинов назначается в процедуре void setup
с помощью pinMode()
, например:
void setup() {
pinMode(10, OUTPUT); // объявляем пин 10 как выход
pinMode(A2, OUTPUT); // объявляем пин A2 как выход
pinMode(12, INPUT); // объявляем пин 12 как вход
pinMode(A1, INPUT); // объявляем пин A1 как вход
}
Пояснения к коду:
- К выходу
10
иA2
можно подключить светодиод, который будет включаться и выключаться при вызове команды в программе. - Пин
10
может использоваться для ШИМ сигнала, например, чтобы плавно включить светодиод, а пинA2
может выдавать только цифровой сигнал (0
или1
) - К входу
12
иA1
можно подключить цифровой датчик и микроконтроллер будет проверять наличие сигнала на этих пинах (логический нуль или единицу) - К входу
A1
можно подключить аналоговый датчик, тогда микроконтроллер будет получать не только сигнал, но и узнавать характеристику сигнала.
ℹ️ Для справки:
Пины не случайно разделены на пины с ШИМ модуляцией (PWM Pins) и аналоговые.
PWM пины создают аналоговый сигнал, к ним подключают сервопривод,
шаговый двигатель и другие устройства,
где требуется подавать сигнал с разными характеристиками.
Аналоговые пины (Analog In) используются для подключения аналоговых датчиков,
с них входящий сигнал преобразуется в цифровой с помощью встроенного АЦП.
Использование аналоговых пинов как цифровые
При подключении большого количества устройств к плате
пинов общего назначения может не хватить.
Тогда в скетче можно указыть, что необходимо использовать аналоговые пины как цифровые.
Также можно использовать не буквенное, а цифровое обозначение выходов,
т.е. A0
— это 14 пин, A1
— это 15 пин и т.д. (работает только на Uno или Nano).
В следующем примере две строчки имеют одинаковое значение.
void setup() {
pinMode(A2, OUTPUT); // объявляем пин A2, как цифровой выход
pinMode(16, OUTPUT); // объявляем пин 16, как цифровой выход
}
Назначение пинов SDA, SCL
Данные пины используются для приема/передачи информации по протоколу I2C. Например, при подключении жк дисплея с модулем I2C или GPS модуля. С помощью специальной библиотеки микроконтроллер может обмениваться информацией с подключенным периферийным устройством, поддерживающим данный протокол. На Ардуино Mega, в отличии от Uno и Nano, имеется целых три пары пинов SDA и SCL.
Назначение пинов TX, RX
Пины TX/RX также используются для коммуникации,
но уже по протоколу UART.
На платах Uni и Nano пины TX/RX подключены параллельно USB разъему для связи с компьютером.
Поэтому, если подключиться к данным портам устройство, например, Bluetooth модуль,
то загрузить в Ардуино скетч не получится, так как плата автоматически переключится
на чтение данных с устройства, а не с компьютера.
Функции void loop()
и void setup()
Функции void loop()
и void setup()
— это первое с чем сталкивается любой,
кто начинает знакомство с языком программирования микроконтроллеров Ардуино.
Данные циклы должны быть в каждом скетче и вызываться только один раз, даже если один из циклов не используется.
Дело в том, что при запуске микроконтроллера Arduino, начинают работать встроенные микропрограммы, которые первым делом проверяют не началась ли загрузка новой программы с компьютера.
Если пользователь не начал прошивку, то контроллер начинает выполнять ранее загруженный скетч.
Оба цикла вызываются встроенной функцией main()
из файла main.cpp
.
При этом функция void setup()
вызывается один раз,
а и void loop()
вызывается в цикле for
бесконечное количество раз.
Если в скетче будут присутствовать более одной функции
void setup()
или void loop()
, то при компиляции Aduino IDE выдаст ошибку:
redefinition of ‘void setup()‘
или redefinition of ‘void loop()’
соответственно.
Функция void setup()
void setup() {
// код должен располагаться между фигурных скобок
}
Фигурные скобки указывают, где начало и конец цикла, поэтому все команды должны располагаться между ними.
Процедура void setup()
вызывается один раз
и её используют для назначения режима работы пинов или команд,
которые необходимо выполнить только в момент загрузки программы.
void loop()
После выполнения цикла setup
, программа переходит в цикл loop
,
который будет повторяться до тех пор, пока на плату подано питание.
Если цикл содержит одну команду, то она будет выполняться тысячи раз в секунду.
Если написать скетч для мигания светодиодом на Arduino, то необходимо добавлять в код задержку для выполнения программы, иначе мигания светодиода не будет заметно.
Функция loop*()
имеет следующую конструкцию:
void loop() {
// основной код программы располагается здесь
}
Таким образом, если необходимо при запуске программы
включить светодиод один раз (для индикации работы устройства)
на микроконтроллере Arduino Nano,
то команду лучше написать в цикле void setup()
.
Если в программе необходимо выполнять какое-то действие постоянно, например,
получать данные с ультразвукового дальномера HC-SR04,
то команду следует располагать в цикле void loop()
.
Функция pinMode()
Функция pinMode()
устанавливает режим работы заданного пина, как входа или выхода.
Цифровой пин может находиться в двух состояниях. В режиме входа пин считывает напряжение от 0 до 5 Вольт, а в режиме выхода – выдавать на пине такое же напряжение.
Режим работы пина микроконтроллера выбирается при помощи функции pinMode(pin, mode)
,
где pin
это номер пина, а mode
это режим.
pinMode OUTPUT
OUTPUT (порт работает как выход) — пин становится управляемым источником питания с максимальным током 40 мА.
В зависимости от команды digitalWrite()
пин принимает значение единицы или нуля.
Пример: pinMode(10, OUTPUT)
.
Функция digitalWrite()
и analogWrite()
Цифровой пин может генерировать цифровой сигнал с помощью команды digitalWrite()
,
т.е. выдавать напряжение 5 Вольт.
Цифровой сигнал может иметь два значения — 0
или 1
(0 Вольт или 5 Вольт).
Если в программе используется команда analogWrite()
для ШИМ портов платы,
то микроконтроллер может генерировать сигнал PWM на портах —
создавать имитацию аналогового сигнала.
void setup() {
pinMode(10, OUTPUT);
}
void loop() {
digitalWrite(10, HIGH);
delay(250);
digitalWrite(10, LOW);
delay(250);
}
К пинам нельзя подключать устройства, потребляющие ток более 40 мА,
так как основное назначение микроконтроллера —
это управления другими устройствами при помощи логических сигналов.
Если к пину подключить устройство, потребляющее ток больше указанного значения,
то пин может выгореть. Поэтому к выводам микроконтроллера не следует подключать ничего
мощнее светодиода.
pinMode INPUT
INPUT (порт работает как вход) —
пин в этом режиме считывает данные с аналоговых и цифровых датчиков, состояния кнопок.
Порт находится в высокоимпедансном состоянии,
т.е. у пина высокое сопротивление.
Пример: pinMode(10, INPUT)
.
Функция digitalRead()
и analogRead()
Arduino может определить наличие напряжения на пине через функцию digitalRead()
,
которая возвращает 0
(LOW
) или 1
(HIGH
).
Существует разница между цифровым датчиком (который обнаруживает включение/выключение) и аналоговым датчиком, значение которого постоянно изменяется.
Используя функцию analogRead()
, можно прочитать напряжение с аналогового датчика,
функция возвращает число от 0
до 1023
.
void setup() {
pinMode(10, INPUT);
Serial.begin(9600);
}
void loop() {
int data = digitalRead(10);
Serial.println(data);
delay(250);
}
Нельзя подавать на вход микроконтроллера напряжение выше напряжения питания платы.
Кроме того, для аналоговых выводов можно использовать команды
digitalRead()
и digitalWrite()
.
В этом случае аналоговые порты будут считывать (digitalRead
)
или выдавать (digitalWrite
) цифровой, а не аналоговый сигнал.
pinMode INPUT_PULLUP
INPUT_PULLUP (порт работает как вход) но к пину подключается резистор в 20 кОм.
В этом режиме при подключении кнопки к Ардуино можно не использовать
внешние подтягивающие резисторы.
Пример: pinMode(10, INPUT_PULLUP)
.
Директива #define
Директива #define
позволяет задавать имена значениям (константам),
которые делают скетч более понятным.
То есть можно в начале программы один раз определить имя константы или фрагмента кода, а затем использовать в скетче только это название.
Рассмотрим на примерах с описанием правильные варианты использования директивы #define
.
Константы, которые определены через директиву #define
,
не занимают места в памяти микроконтроллера,
так как значение подставляет значения вместо имен при компиляции скетча.
Работу директивы можно сравнить с операцией НАЙТИ
и ЗАМЕНИТЬ
в текстовом редакторе.
При компиляции скетча компилятор находит в программе часть кода <что меняем>
и заменяет ее на кусок кода <на что меняем>
.
Синтаксис директивы:
#define <что меняем> <на что меняем>
При использовании директивы следует избегать использования имени другой переменной, константы или команды ардуино, иначе оно оно будет заменено при компиляции.
ℹ️ Обрати внимание:
В конце строчки не ставится точка с запятой и знак равенства, как это делается при объявлении переменной.
#define RED 11 // присваиваем имя RED для пина 11
#define GRN 12 // присваиваем имя GRN для пина 12
#define BLU 13 // присваиваем имя BLU для пина 13
void setup() {
pinMode(RED, OUTPUT); // используем PIN 11 для вывода
pinMode(GRN, OUTPUT); // используем PIN 12 для вывода
pinMode(BLU, OUTPUT); // используем PIN 13 для вывода
}
При написании кода гораздо удобнее использовать имена вместо номеров, чтобы каждый раз не вспоминать какой цвет к какому пину подключен.
А программа автоматически будет заменять имена RED
, GRN
, BLU
на соответствующие значения при компиляции.
Директивы #ifdef
, #ifndef
и #endif
в скетче
Инструкция #ifdef
проверят, было ли встречено в программе данное определение ранее,
если было, то ставится блок кода с последующей строки и до #endif
.
В примере проверяется был ли ранее в #define
определен признак отладки,
если да, то код (вывод сообщения на монитор порта) будет выполнен,
если признак не определен, то сообщение на мониторе выводиться не будет.
#ifdef DEBUG
Serial.println("Debug message: ...");
#endif
Инструкция #ifndef
проверят, было ли встречено в программе данное определение ранее
и если не было, то вставится блок кода с последующей строки и до #endif
.
В следующем простом примере объявляется новая константа, если только ранее её не объявляли в скетче.
Если дефайн с таким именем уже использовался,
то программа проигнорирует строчки внутри конструкции #ifndef … #endif
.
#ifndef RED
#define RED 11
#endif
Замена функций с помощью #define
Кроме использования дефайн в программе для объявления констант,
можно заменять целые фрагменты кода с помощью директивы #define
.
Это более сложный, но интересный вариант использования #define
,
который позволяет создать много разных упрощающих инструкций в скетче.
Например, можно в первом примере заменить функцию pinMode()
на конструкцию с заданными параметрами.
#define out(pin) pinMode(pin, OUTPUT)
void setup() {
out(11);
out(12);
out(13);
}
В примере функция pinMode()
закодирована одним словом out
.
Теперь, где в скетче встретится слово out
,
компилятор подставит строку pinMode(pin, OUTPUT)
с заданным параметром pin
.
Таким же образом можно заменить команды digitalWrite()
и delay()
.
Используя RGB светодиод или три обычных светодиода с ардуинкой можно проверить работу следующего примера скетча с директивой дефайн.
#define out(pin) pinMode(pin, OUTPUT)
#define on(pin, del) digitalWrite(pin, HIGH); delay(del)
#define off(pin, del) digitalWrite(pin, LOW); delay(del)
void setup() {
out(11);
out(12);
out(13);
}
void loop() {
on(11, 500);
off(11, 500);
on(12, 500);
off(12, 500);
on(13, 500);
off(13, 500);
}
define
или const
, что выбрать?
Иногда бывает не удобно применять директиву #define
для создания констант,
в этом случае используют ключевое слово const
.
В отличие от глобальных переменных, значение const
должно быть определено сразу
при объявлении константы.
Необходимо помнить, что при использовании #define
имена следует делать
максимально уникальными, чтобы не было совпадений с командами из подключаемых библиотек.
const int RED = 11; // присваиваем имя RED для пина 11
const int GRN = 12; // присваиваем имя GRN для пина 12
const int BLU = 13; // присваиваем имя BLU для пина 13
void setup() {
pinMode(RED, OUTPUT); // используем PIN 11 для вывода
pinMode(GRN, OUTPUT); // используем PIN 12 для вывода
pinMode(BLU, OUTPUT); // используем PIN 13 для вывода
}
ℹ️ Для справки:
Использование #define
или const
не дает никаких преимуществ,
с точки зрения экономии объема памяти микроконтроллера.
Задержки, delay()
и millis()
delayMicroseconds()
Функция delayMicroseconds()
останавливает выполнение программы
на заданное количество микросекунд (в 1 секунде 1 000 000 микросекунд).
При необходимости задержки в программе более чем на несколько тысяч микросекунд
рекомендуется использовать delay()
.
delay()
Функция delay()
останавливает выполнение программы на заданное количество миллисекунд
(в 1 секунде 1 000 миллисекунд).
Во время задержки программы с помощью функции delay()
,
не могут быть считаны подключенные к плате датчики или произведены другие операции,
например, запись в EEPROM
данных.
В качестве альтернативы следует использовать функцию millis()
.
millis()
Функция millis()
возвращает количество прошедших миллисекунд
с момента начала выполнения программы.
Счетчик времени сбрасывается на ноль при переполнении значения unsigned long
(приблизительно через 50 дней).
Функция millis()
позволяет сделать многозадачность в ардуинке,
так как выполнение программы не останавливается
и можно выполнять параллельно другие операции в скетче.
unsigned long timer = 0;
bool led_state = HIGH;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (millis() - timer > 200) {
led_state =! led_state;
digitalWrite(LED_BUILTIN, led_state);
timer = millis();
}
// выводим количество миллисекунд прошедших с момента начала программы
Serial.print("Time: ");
Serial.println(timer);
}
tone()
// FIXIT: https://xn--18-6kcdusowgbt1a4b.xn--p1ai/tone-arduino/
map()
// FIXIT: https://xn--18-6kcdusowgbt1a4b.xn--p1ai/map-arduino/
random()
// FIXIT: https://xn--18-6kcdusowgbt1a4b.xn--p1ai/random-arduino/
Serial Monitor
// FIXIT: https://xn--18-6kcdusowgbt1a4b.xn--p1ai/%d0%bc%d0%be%d0%bd%d0%b8%d1%82%d0%be%d1%80-%d0%bf%d0%be%d1%80%d1%82%d0%b0-%d0%b0%d1%80%d0%b4%d1%83%d0%b8%d0%bd%d0%be/
Используемые материалы:
- Уроки по Arduino | робототехника18.рф