Когда впервые столкнулся с Arduino, меня несколько удивил подход итальянских ребят к работе с портами (выводами) микроконтроллера AVR. Обычно, с каждой линией работают через соответсвующий бит определенного порта, а тут… пронумеровали все ножки микросхемы и сделали их «равноправными»: теперь не надо думать — какой это порт? можно ли его использовать для своих целей? не занят ли он чем-либо «своим»? Очень удобно, с точки зрения ленивого разработчика. Но, с другой стороны, за это надо чем-то платить! очень хотелось посмотреть: чего итальянцы «засунули» в процедуру digitalWrite()?
digitalWrite()
Подает HIGH или LOW значение на цифровой вход/выход (pin).
Оказалось — не один я такой любопытный — есть люди, которые тщательно провели соответствующее расследование. В итоге оказалось, что кроме очевидного «декодирования» номера пина в его «нормальное представление»: ПОРТ/БИТ и выполнения логического ИЛИ (для записи HIGH) или И (для записи LOW) для этого порт-бита, процедура делает еще массу действий, в-основном, защищая программу «от дурака». Цена такой защиты и удобства программирования — большой объем кода и время его выполнения.
Не удержусь, и приведу абстрактный пример, на котором будет видна «цена» за удобство. Допустим надо на 7-сегментный индикатор вывести букву «У». Схема подключения — ниже, обратите внимание — катоды индикатора подключены к линиям одного порта микроконтроллера.
Для отображения «У» надо «поджечь» сегменты B, C, D, F, G, т.е на их катоды подать уровень LOW. Стандартный скетч для этого:
#define s_A 0 #define s_B 1 #define s_C 2 #define s_D 3 #define s_E 4 #define s_F 5 #define s_G 6 #define s_H 7 void setup() { //устанавливаем режим работы "на вывод" pinMode(s_A, OUTPUT); pinMode(s_B, OUTPUT); pinMode(s_C, OUTPUT); pinMode(s_D, OUTPUT); pinMode(s_E, OUTPUT); pinMode(s_F, OUTPUT); pinMode(s_G, OUTPUT); pinMode(s_H, OUTPUT); //включаем нужные и выключаем ненужные сегменты digitalWrite(s_A,HIGH); digitalWrite(s_B,LOW); digitalWrite(s_C,LOW); digitalWrite(s_D,LOW); digitalWrite(s_E,HIGH); digitalWrite(s_F,LOW); digitalWrite(s_G,LOW); digitalWrite(s_H,HIGH); } void loop() { }
Все ясно и понятно, размерчик готового bin-файла 978 байт!
У «не ардуинщика» глаза на лоб полезут от ТАКОГО решения задачи. В «своей» системе разработки он написал бы примерно следующее (в переводе на «язык ардуины»):
void setup() { //устанавливаем режим работы порта PD "на вывод" DDRD = B11111111; //включаем нужные и выключаем ненужные сегменты PORTD = B10010001; } void loop() { }
Довольно сложно для понимания теми, кто «не изучал матчасть». Очень «не гибко» — даже малейшее изменение схемы требует полной смены программы. Но — довольно коротко и быстро в исполнении… Размер 474 байта — ни о чем не говорит, тут больше «служебного мусора».
Скорость выполнения кода можно оценить осциллографом используя скетч с непрерывным циклом, в котором сегменты включаются, а затем гасятся. Для «Ардуиновского варианта» время включения нужного изображения («У») составило 35 мкс (полного гашения всех восьми сегментов — такое же). Второй вариант оценить оказалось довольно сложно. Лишь в цикле где повторяются 40 раз команда включения, затем выключения удалось «плохоньким китайским приборчиком» определить, что включение сегментов происходит примерно за 120 нс. Разница — примерно в 300 раз!
Развернутая статья о прямом использовании портов — здесь.
PS: Разбирая коды исполнения digitalWrite(), отметил для себя печальный факт, что обратиться к pin A7 по его «цифровому» номеру 21 — не получится. Не предусмотрели итальянцы мастерство китайских товарищей, которые в своем клоне Arduino Pro mini вывели на плате контакты PC6 (pin A6) и PC7 (pin A7) микросхемы ATmega328 в 32-х выводном корпусе :(.