Существуют стандартные методы языка для настройки выводов микроконтроллера на вход или выход.Ардуино программируется на C++ с некоторой адаптацией и упрощениями для новичков. Он называется Wiring. Изначально все порты ардуино определяются как входы, и нет нужды задавать это в коде. Порты обычно прописываются в функции инициализации переменных
void setup ()
{
// код
}
Для этого используется команда pinMode, у неё достаточно простой синтаксис, сначала указывается номер порта, затем его роль через запятую.
pinMode (nomer_porta, naznachenie)
С помощью этой команды внутренняя схема микроконтроллера конфигурируется определенным образом.
Есть три режима в которых может работать порт: INPUT – вход, в этом режиме происходит считывание данных с датчиков, состояния кнопок, аналогового и цифрового сигнала. Порт находится в высокоимпедансном состоянии, простыми словами – у входа высокое сопротивление. Устанавливается это значение, на примере 13 пина платы, при необходимости так:
pinMode (13, INPUT);
OUTPUT – выход, в зависимости от команды прописанной в коде порт принимает значение единицы или нуля. Выход становится своего рода управляемым источником питания и выдаёт максимальный ток (в нашем случае 20 мА и 40 мА в пике) в нагрузку к нему подключенную. Чтобы назначить порт как выход на Arduino нужно ввести:
pinMode (13, OUTPUT);
INPUT_PULLUP – порт работает как вход, но к нему подключается подтягивающий резистор в 20 кОм.
Данные принимаются с портов и передают на них командами:
digitalWrite(пин, значение) – переводит выходной пин в логическую 1 или 0, соответственно на выходе появляется или исчезает напряжение 5В, например digitalWrite (13, HIGH) – подаёт 5 вольт (логическую единицу) на 13 пин, а digitalWrite (13, low) – переводит 13 пин в состояние логического ноля (0 вольт);
digitalRead(пин) – считывает значение со входа, пример digitalRead (10), считывает сигнал с 10 пина;
analogRead(пин) – считывает аналоговый сигнал с аналогового порта, вы получаете значение в диапазоне от 0 до 1023 (в пределах 10-битного АЦП), пример analogRead (3).
На большинстве плат Arduino (на базе микроконтроллера ATmega168 или ATmega328) ШИМ поддерживают порты 3, 5, 6, 9, 10 и 11, на плате Arduino Mega порты с 2 по 13. На более ранних версиях плат Arduino analogWrite() работал только на портах 9, 10 и 11.
Для вызова analogWrite() нет необходимости устанавливать тип вход/выхода функцией pinMode(). Функция analogWrite никак не связана с аналоговыми входами и с функцией analogRead.
В данном примере регистр DDR обращается к порту B, лог. 1 в 5 байте переводит вывод в режим выхода, регистр PORT так же обращается к порту B, лог. 1 в 5 байте переводит вывод в HIGH. После загрузки скетча должен засветится светодиод который расположен на плате Arduino.
Показанный выше пример позволяет всего двумя строчками кода сконфигурировать все выходы порта и установить необходимые логические состояния выводов.
Если необходимо установить для примера на выходе D13 лог. 1 и не менять состояние других выходов, то можно воспользоваться следующим примером:
void setup(){ DDRB = 0B00100000; PORTB |= (1 << 5); // см. пример http://rcl-radio.ru/?p=80784 } void loop(){}
или установить лог. ноль:
void setup(){ DDRB = 0B00100000; PORTB &= ~(1 << 5); // см. пример http://rcl-radio.ru/?p=80784 } void loop(){}
Для чтения состояния порта используется регистр PIN:
void setup(){ Serial.begin(9600); DDRB = 0b00111111; // назначить выводы PB0...PB5 как выходы (D8...D13) PORTB = 0b00111011; // PB0...PB1 и PB3...PB5 = HIGH, PB2 = LOW } void loop(){ Serial.println(PINB,BIN); // чтение порта и вывод в монитор порта delay(1000); }
Информация о состоянии порты выводится в монитор порта:
Для чтения одного вывода порта используйте следующий пример:
void setup(){ Serial.begin(9600); DDRB = 0b00111111; PORTB = 0b00111011; } void loop(){ Serial.println(((PINB >> 5) & 1),BIN); // чтение состояния PB5 delay(1000); }
Далее переведем все выводы порта в режим чтения:
void setup(){ Serial.begin(9600); DDRB = 0b00000000; // все выводы порта в режиме чтения } void loop(){ Serial.println(((PINB >> 0) & 1),BIN); // чтение состояния PB0 (D8) delay(1000); }
На вход D8 подавать уровни LOW или HIGH, в мониторе порта Вы увидите 0 или 1.
Так же в цифровом входе можно использовать подтягивающий резистор, при этом на входе будет HIGH. Этот режим удобно использовать для подключения кнопки (коммутация кнопки на GND).
void setup(){ Serial.begin(9600); DDRB = 0b00000000;// все выводы порта в режиме чтения PORTB = 0b00000001;// подключить подтягивающий резистор к выводу PB0 (D8) } void loop(){ Serial.println(((PINB >> 0) & 1),BIN); delay(1000); }
У Вас может возникнуть вопрос: — «Зачем все это? Ведь существуют такие функции как pinMode(), digitalWrite() и digitalRead(). Эти функции понятны и просты в применении.»
Как говорилось в начале статьи, что использование регистров портов позволяют выполнять низкоуровневые высокоскоростные манипуляции с выводами микроконтроллера. Помимо увеличения скорости чтения и переключения цифровых выводов, использование памяти контроллера значительно уменьшается. Если у Вас простой скетч не требовательный к размеру памяти и ресурсам микроконтроллера, то проще использовать стандартные функции. Но если скетч большой и требовательный к скорости переключения цифровых выводов, то использование регистров будет самым оптимальным для Вас решением.
Далее приведу несколько примеров использования регистров портов, в которых будет сравнение с использованием стандартных для Arduino функций.
Мигание светодиодом D13 (Blink)
void setup() { pinMode(13,OUTPUT); } void loop() { digitalWrite(13,HIGH); delay(1000); digitalWrite(13,LOW); delay(1000); }
Скетч использует 924 байт (2%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
void setup(){ DDRB = 0B00100000; } void loop(){ PORTB |= (1 << 5); delay(1000); PORTB &= ~(1 << 5); delay(1000); }
Скетч использует 642 байт (1%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Оба скетча выполняют одно и тоже действие, второй скетч занимает меньше памяти. А если Вам не важна точность задержки функции delay(), то можно применить _delay_ms() и использование памяти микроконтроллера станет еще меньше:
void setup(){ DDRB = 0b00100000; } void loop(){ PORTB |= (1 << 5); _delay_ms(1000); PORTB &= ~(1 << 5); _delay_ms(1000); }
Скетч использует 488 байт (1%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Скорость переключения выхода D13
void setup() { Serial.begin(9600); pinMode(13,OUTPUT); } void loop() { unsigned long times = micros(); for(int i=0;i<100;i++){ digitalWrite(13,HIGH); digitalWrite(13,LOW); } Serial.println(micros()-times); delay(1000); }
Скетч использует 2104 байт (6%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.
void setup(){ Serial.begin(9600); DDRB = 0B00100000; } void loop(){ unsigned long times = micros(); for(int i=0;i<100;i++){ PORTB |= (1 << 5); PORTB &= ~(1 << 5); } Serial.println(micros()-times); delay(1000); }
Скетч использует 1820 байт (5%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.
Как видно на скриншотах, переключение цифрового выхода D13 100 раз занимает в среднем 680 мкс, тот же процесс во втором скетче занимает в среднем 54 мкс.
Аналогичная ситуация и при чтении выхода:
void setup() { Serial.begin(9600); pinMode(13, OUTPUT); digitalWrite(13, HIGH); } void loop() { unsigned long times = micros(); int x = 0; for (int i = 0; i < 1000; i++) { if (digitalRead(13) == 1) { x++; }; } Serial.println(micros() - times); Serial.println(x); delay(1000); }
Скетч использует 2242 байт (6%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.1000 — кол-во измерений, 2576 — затраченное время в мкс
void setup() { Serial.begin(9600); DDRB = 0B00111111; PORTB |= (1 << 5); } void loop() { unsigned long times = micros(); int x = 0; for (int i = 0; i < 1000; i++) { if (((PINB >> 5) & 1) == 1) { x++; } } Serial.println(micros() - times); Serial.println(x); delay(1000); }
Скетч использует 1906 байт (5%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.1000 — кол-во измерений, 444 — затраченное время в мкс
Отправить комментарий
Отправить комментарий