Про digitalWrite() — как это работает

Когда впервые столкнулся с Arduino, меня несколько удивил подход итальянских ребят к работе с портами (выводами) микроконтроллера AVR. Обычно, с каждой линией работают через соответсвующий бит определенного порта, а тут… пронумеровали все ножки микросхемы и сделали их «равноправными»: теперь не надо думать — какой это порт? можно ли его использовать для своих целей? не занят ли он чем-либо «своим»? Очень удобно, с точки зрения ленивого разработчика. Но, с другой стороны, за это надо чем-то платить! очень хотелось посмотреть: чего итальянцы «засунули» в процедуру digitalWrite()?

digitalWrite()

Подает HIGH или LOW значение на цифровой вход/выход (pin).

Оказалось — не один я такой любопытный — есть люди, которые тщательно провели соответствующее расследование. В итоге оказалось, что кроме очевидного «декодирования» номера пина в его «нормальное представление»: ПОРТ/БИТ и выполнения логического ИЛИ (для записи HIGH) или И (для записи LOW) для этого порт-бита, процедура делает еще массу действий, в-основном, защищая программу «от дурака». Цена такой защиты и удобства программирования — большой объем кода и время его выполнения.

Не удержусь, и приведу абстрактный пример, на котором будет видна «цена» за удобство. Допустим надо на 7-сегментный индикатор вывести букву «У». Схема подключения — ниже, обратите внимание — катоды индикатора подключены к линиям одного порта микроконтроллера.s7seg_s1

Для отображения «У» надо «поджечь» сегменты  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-х выводном корпусе :(.

Добавить комментарий

Ваш e-mail не будет опубликован.

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".