Как можно различными способами задать (обозначить, запрограммировать) порты ввода и вывода в AVR (Atmega, Атмега) и PIC – в двоичной, шестнадцатеричной и других системах.
1. Регистр выбора направления передачи данных DDR
Выводы (порты) микроконтроллера могут работать как входы и как выходы. Поэтому предварительно нужно настроить вывод МК на соответствующий режим посредством специального регистра, который называется DDR.
У каждого порта есть свой DDR регистр. Например, у Atmega8 три порта: B, C и D, а соответствующие им регистры называются: DDRB, DDRC и DDRD.
В том случае, если нам необходимо сконфигурировать вывод МК как вход, то в соответствующий разряд DDR регистра записывается ноль, если – как выход, то единица.
Одним из распространённых способов настройки DDR регистров является запись в виде двоичного кода с количеством разрядов, совпадающим с разрядностью порта. К примеру, чтобы настроить вывод PC0 как выход, мы должны написать следующую команду:
DDRC = 0b0000001;
Здесь префикс 0b идентифицирует следующее за ним число как двоичное, а порядковый номер бита отвечает номеру бита внутри порта. То есть последний (крайний правый) разряд этого числа соответствует PC0, предпоследний – PC1 и т. д. Как итог: PC0 настроен как выход, все остальные разряды (PC1…PC6) – как высокоимпедансные входы.
Поскольку порт С в Atmega8 содержит не 8 разрядов, а 7, то и количество битов в двоичном коде равно 7.
Ещё один пример с 8-разрядным портом D:
DDRD = 0b10001000;
Здесь третий и седьмой биты порта D сконфигурированы как выход, а остальные биты – как вход.
Если подставить двоичное число (то, что находится после 0b) в калькулятор и конвертировать его в шестнадцатеричный код, то можно полученное число также использовать в настройке бита, только при этом – 0b заменить на 0x.
Перепишем наши примеры с шестнадцатеричными числами:
DDRC = 0x01; DDRD = 0x88;
Часто функциональное назначение отдельно взятого вывода удобнее задавать побитовой настройкой. Для того, чтобы нам настроить PC0 как выход, в этом случае следует записать следующую команду:
DDRC |= ( 1 << 0 );
Эта запись означает, что для нулевого разряда порта С (номер разряда указывает последняя цифра), т. е. для вывода PC0 мы прописали в DDR единицу и тем самым настроили его как выход.
А вот, если бы нам туда понадобилось прописать ноль, т. е. настроить PC0 как вход, то команда выглядела бы так:
DDRC &= ~( 1 << 0 );
Запишем в побитовой форме и второй пример – настроим третий и седьмой биты порта D как выходы:
DDRD |= ( 1 << 3 ); DDRD |= ( 1 << 7 );
Эти две команды можно заменить одной:
DDRD |= ( 1 << 3 )|( 1 << 7 );
2. Регистр выходных данных порта PORT
Служит для управления состоянием вывода.
Если вывод (контакт) сконфигурирован как выход, то единица в соответствующем бите регистра PORTx формирует на выводе сигнал высокого уровня, а ноль – формирует сигнал низкого уровня.
Если вывод (контакт) сконфигурирован как вход, то единица в бите регистра PORTx подключает к выводу внутренний подтягивающий pull-up резистор, который обеспечивает высокий уровень на входе при отсутствии внешнего сигнала.
Воспользуемся предыдущими примером, в котором мы установили вывод PC0 как выход, т. е. использовали команду:
DDRC = 0b0000001;
Тогда, чтобы установить на этом выходе высокий логический уровень, нам надо прописать:
PORTС = 0b0000001; – в двоичном коде, либо PORTС = 0x01; – в 16-ричном, либо PORTC |= (1 << PC0); – в побитовом
А для того, чтобы установить низкий уровень –
PORTС = 0b0000000; – в двоичном коде, либо PORTС = 0x00; – в 16-ричном, либо PORTC &= ~(1 << PC0); – в побитовом
Если мы введём:
PORTС = 0b0000101; – в двоичном коде, либо PORTС = 0x05; – в 16-ричном, либо PORTC |= (1 << PC0)|( 1 << PC2 ); – в побитовом,
то это будет означать, что на выходе PC0 присутствует высокий логический уровень, а ко входу PC2 подключён внутренний подтягивающий к питанию резистор.
Все остальные разряды порта С настроены как обычные высокоимпедансные входы.
Для некоторых приложений может оказаться полезной команда, которая переключает отдельный бит в противоположное состояние, т. е. единицу в ноль и наоборот. К примеру, для вывода PD3 данная логическая операция выглядит следующим образом:
PORTD ^= (1 << 3);
3. Регистр считывания состояния вывода PIN
Данный регистр непрерывно отражает текущее состояние выводов порта, причём независимо от того, используется вывод как вход или как выход. То есть, обратившись к нему, мы можем узнать, какое напряжение подано на входной вывод, либо установлено в ходе работы программы на выходном. Из всего этого следует, что PIN – это регистр, из которого можно только читать.
Давайте посмотрим, как можно считывать информацию из регистра PIN на примере оператора if.
Как правило, у разработчика возникает необходимость проверять состояние не одновременно всех битов регистра, а какого-то конкретного, отдельно взятого бита, а потому и применять в этом случае следует побитовую операцию.
К примеру, команда:
if (PINB & (1 << PINB1)) {какой-либо код}
ожидает появления на выводе PB1 высокого уровня (единицы).
А вот команда:
if (!(PINB & (1<<PINB1))) {какой-либо код}
ожидает появления на выводе PB1 низкого уровня (нуля).