Частота, кГц
Исходная версия
04_05
72,26
Объявление с типом byte вместо int
04_06
77,17
Использование константы с номером контакта вместо переменной
04_07
77,92
Перенос содержимого loop в setup
04_08
86,39
Байты и биты
Прежде чем переходить к непосредственному управлению портами ввода/вывода, нужно сначала разобраться с двоичным представлением, битами, байтами и целыми числами.
На рис. 4.2 показано, как связаны биты и байты.

Рис. 4.2.Биты и байты
Бит (в английском языке bit , происходит от binary digit — двоичная цифра) может иметь одно из двух значений — 0 или 1. Байт — это коллекция из 8 битов. Так как каждый из битов в байте может иметь значение 1 или 0, всего возможно 256 разных комбинаций битов в байте. Байт можно использовать для представления любых чисел в диапазоне от 0 до 255.
Каждый бит можно использовать также для обозначения состояния «включено» или «выключено». То есть, чтобы включить или выключить подачу напряжения на какой-то контакт, нужно установить или сбросить некоторый бит.
Порты в ATmega328
На рис. 4.3 изображены порты в микроконтроллере ATmega328 и то, как они связаны с контактами на плате Arduino Uno.

Рис. 4.3.Порты в ATmega328
Каждый порт не случайно имеет по 8 бит (байт), хотя в портах B и C используется только по 6 бит. Каждый порт управляется тремя регистрами . Регистр можно считать специальной переменной, позволяющей присваивать ей значения и читать значение из нее. На рис. 4.4 изображены регистры для порта D.

Рис. 4.4.Регистры для порта D
Регистр DDRD (Data Direction Register D — регистр D направления передачи данных) имеет 8 бит, каждый из которых определяет режим работы соответствующего контакта — вход или выход. Если бит установлен в значение 1, контакт работает как выход, в противном случае — как вход. Этим регистром управляет функция pinMode. Регистр PORTD используется для установки выходного напряжения на выходе, то есть digitalWrite устанавливает соответствующий бит, 1 или 0, чтобы установить на указанном контакте уровень напряжения HIGH или LOW.
Последний регистр называется PIND (Port Input D — вход порта D). Читая содержимое этого регистра, можно определить, на какие контакты подано напряжение HIGH, а на какие — LOW.
Каждый из трех портов имеет свои три регистра, для порта B они называются DDRB, PORTB и PINB, а для порта C — DDRC, PORTC и PINC.
Очень быстрый вывод цифровых сигналов
Следующий скетч обращается к портам напрямую, без применения pinMode и digitalWrite:
// sketch_04_09_square_ports
byte state = 0;
void setup()
{
DDRB = B00000100;
while (true)
{
PORTB = B00000100;
PORTB = B00000000;
}
}
void loop()
{
}
Скетч должен переключать контакт D10, который связан с портом B, поэтому вначале контакт настраивается на работу в режиме выхода, для чего третий бит справа в регистре DDRB устанавливается в 1. Обратите внимание на то, что B00000100 — это двоичная константа. В главном цикле мы сначала устанавливаем тот же бит в 1, а затем сбрасываем его в 0. Установка бита производится как простое присваивание значения регистру PORTB, как если бы это была обычная переменная.
Этот скетч способен генерировать сигнал с частотой 3,97 МГц (рис. 4.5) — почти 4 млн импульсов в секунду, что почти в 46 раз быстрее, чем с использованием digitalWrite.

Рис. 4.5.Сигнал с частотой 4 МГц, сгенерированный платой Arduino
Сигнал далек от прямоугольной формы из-за переходных процессов, которые вполне ожидаемы на такой частоте.
Еще одно преимущество непосредственного использования регистров порта — возможность вывода сигналов сразу на восемь контактов, что может пригодиться для вывода данных в параллельную шину данных.
Быстрый ввод цифровых сигналов
Тот же прием непосредственного доступа к регистрам можно использовать для увеличения скорости ввода цифровых сигналов. Хотя, если вы предполагаете таким способом определять моменты появления очень коротких импульсов, подумайте о возможности использования прерываний, они являются лучшим решением этой задачи (см. главу 3).
Читать дальше