Шпаргалка начинающего программиста AVR МК
Делаем первые шаги в программировании микроконтроллера на языке Си. Порты ввода-вывода AVR – простые, но необходимые знания
На этой странице мы будем поэтапно собирать информацию, которая в том или ином виде проскальзывала в наших, пока не сильно
многочисленных статьях,
посвящённых программированию МК. Причём, как мне кажется, это должно быть удобно не только начинающему программисту, так как вся
базовая информация
будет сосредоточена в одном месте, но и, собственно, мне, по причине отсутствия необходимости часто и нудно повторяться.
Напоминаю, что в качестве примера у нас выбран популярный AVR микроконтроллер Atmega8, а в качестве основного языка программирования
– Си.
Однако приступим к делу:
И начнём мы с программирования портов ввода-вывода микроконтроллера.
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 низкого уровня (нуля).
|