Работа с регистрами AVR микроконтроллера на Си, битовые операции
Показаны принципы работы с отдельными битами регистра порта в AVR микроконтроллере. Подробно рассмотрены битовые операции и операции сдвига битов в языке Си. Приведены примеры установки и сброса битов в регистре порта, чтение состояния битов и их инверсии.
Для записи и чтения отдельных битов в портах микроконтроллера необходимо научиться выполнять битовые операции, уметь использовать битовые маски и выполнять запись в порт.
Содержание:
- Структура байта
- Порты, байты и биты
- Операции битового сдвига
- Битовые операторы в языке Си
- Установка битов в регистре порта
- Сброс битов в регистре порта
- Проверка разрядов регистра
- Инверсия состояния бита в регистре
- Заключение
Структура байта
Мы знаем что один байт представляет собой 8 бит, а каждый бит - это 1 или 0. Биты в байте считаются справа-налево. Бит 1 является младшим, а бит 8 - старшим.
1 Байт | |||||||
8 (старший бит) | 7 | 6 | 5 | 4 | 3 | 2 | 1 (младший бит) |
Порты, байты и биты
Представьте себе на минуточку, что регистр порта в микроконтроллере - это аппаратная панель, на которой расположены один за другим восемь одинаковых переключателей, каждый из которых может иметь два состояния: включен (1) или выключен (0).
В языке Си для AVR, установка значения 1 для бита в порте - это как перевод нужного переключателя в состояние "включено". На выходе канала для указанного порта, к которому подвязан наш виртуальный выключатель, появится высокий уровень, а это в свою очередь подаст напряжение на какое-то устройство, например на светодиод, который сразу же засветится.
Названия каналов в порте микроконтроллера отсчитываются с нуля (0). Ниже приведен пример битовой структуры порта PORTD:
Регистр порта PORTD, 1 байт | ||||||||
Номер бита в регистре | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
Канал порта | PD7 | PD6 | PD5 | PD4 | PD3 | PD2 | PD1 | PD0 |
Аналогично по структуре выглядят и другие порты - PORTA, PORTB, DDRD, PINA...
Предустановленные значения констант PD0, PD1, PB5, PA4 (и многих других) для каждого типа AVR-микроконтроллера указаны в специальном заголовочном файле библиотеки avr-libc.
Например, чтобы вывести на экран значения всех констант из IO-файла для микроконтроллера ATmega8, содержащих сочетание символов "PD" (ищем константы для порта D, PORTD), достаточно выполнить следующую команду:
cat /usr/lib/avr/include/avr/iom8.h | grep PD
Увидим следующий результат:
#define SPDR _SFR_IO8(0x0F)
#define PD7 7
#define PD6 6
#define PD5 5
#define PD4 4
#define PD3 3
#define PD2 2
#define PD1 1
#define PD0 0
Теперь, при использовании константы PD0 в своей программе на Си, вы знаете что в ней содержится число 0, а в PD3 - 3 и т.д.
Операции битового сдвига
А сейчас, давайте подробно и с примерами разберемся с тем, как работают операторы битового сдвига.
Существует несколько разновидностей операций битового сдвига:
- логический (сдвинутые в направлении биты теряются, а освободившиеся позиции заполняются нулями);
- арифметический (сдвиг влево аналогичен логическому, а при сдвиге вправо - свободные позиции заполняются значениями крайнего левого бита, который еще называют знаковым);
- циклический (потерянные с одной стороны биты перемещаются на освободившиеся позиции с другой, как замкнутое кольцо).
Операторы битового сдвига в языке программирования Си обозначаются как ">>" и "<<" и выполняют логический сдвиг битов в используемой переменной в указанном направлении и на указанное число элементов.
Важный нюанс: при сдвиге вправо (">>") битов в числе с отрицательным знаком (signed) выполняется арифметический сдвиг - освободившиеся позиции слева заполняются единичками (перенос знака). Это важно помнить!
Сокращенные обозначения:
- Bin - от слова Binary, двичная система счисления;
- Dec - от слова Decimal, десятичная система счисления;
- Hex - от слова Hexadecimal, шестнадцатиричная система счисления.
Для примера выполним сдвиги битов в разных числах, предварительно представив их в двоичном виде.
Для числа 1 (Dec, в десятичной системе 1) - 00000001 (Bin, в двоичной системе 0b00000001):
- 1 << 0 = 1 (00000001);
- 1 << 1 = 2 (00000010);
- 1 << 2 = 4 (00000100)
- 1 << 5 = 32 (00100000);
- 1 >> 2 = 0 (00000000).
Сдвиг влево на один разряд выполняет умножение числа на 2, а сдвиг вправо - деление числа на 2.
Для числа 209 (Dec, в десятичной системе 209) - 11010001 (Bin, в двоичной системе 0b11010001):
- 209 = 11010001;
- 209 << 3 = 136 (10001000);
- 209 << 5 = 32 (00100000);
- 209 >> 5 = 6 (00000110)
Для пересчета из одной системы счисления в другую в Linux удобно использовать калькулятор, об этом я уже раньше писал.
Битовые операторы в языке Си
То как двигать биты в байте мы теперь знаем, дальше разберемся с битовыми операторами в Си:
- "&" (логическое И, AND) или умножение - бинарная операция, результат которой равен 1 только в том случае если оба операнда равны 1, в противном случае будем иметь 0;
- "|" (логическое ИЛИ, OR) или сложение - бинарная операция, результат которой равен 1 в том случае если хотя бы один из операндов равен 1;
- "~" (логическое НЕ) или инверсия - унарная операция, результат которой равен 0 если операнд равен 1, и наоборот - результат равен 1, если операнд равен 0;
- "^" (исключающее ИЛИ, XOR) - бинарная операция, результат которой равен 1 в том случае если только один из двух операндов равен 1.
Рассмотрим примеры битовых операций над числами 209, 7 и их битовыми представлениями:
1101 0001 (209) 0000 0111 (7) |
1101 0001 (209) | 0000 0111 (7) ---------------- 1101 0111 (215) |
1101 0001(209) ~ ---------------- 0010 1110(46) |
1101 0001 (209) ^ 0000 0111 (7) ---------------- 1101 0110 (214) |
Как видите, битовые операции позволяют установить или сбросить отдельные биты числа.
Установка битов в регистре порта
А теперь немного практики, давайте сделаем установку 6-го бита в регистре порта PORTB что в свою очередь установит высокий уровень для канала PB5 (6-й бит в регистре).
Допустим что сейчас в регистре PORTB содержится число 136, которое в битовом представлении выглядит как 10001000 (высокий уровень на каналах PB7 и PB3).
Чтобы установить 6-й бит (10001000) мы будем использовать битовую операцию логического ИЛИ в комплексе с битовой маской.
Что такое битовая маска? - по сути это специально подготовленное число, состоящее из требуемой конфигурации битов, которое в сочетании с некоторой операцией над битами другого числа позволяет установить или сбросить биты в последнем.
Для получения битовой маски, при помощи которой позже будет установлен один бит, мы выполним левосторонний сдвиг битов числа 1 (00000001) на 5 разрядов:
0000 0001 (1)
<< 5
----------------
0010 0000 (32)
В результате битовой операции получим число 32 (00100000), это и есть наша битовая маска. Хочу заметить что это число равняется числу 2 в 5-й степени, каждый сдвиг разряда влево умножает результат на 2.
Теперь нам останется выполнить битовую операцию ИЛИ над текущим числом в регистре и получившимся числом-маской:
1000 1000 (136)
|
0010 0000 (32)
----------------
1010 1000 (168)
А теперь сравните состояние регистра перед операцией и после - все состояния битов сохранены и дополнительно установлен 6-й бит.
Для установки 6-го бита и последующей записи числа в регистр порта PORTB (установка высокого уровня для канала PB5) в нашем примере можно использовать любую из следующих конструкций операторов, они все выполняют идентичную задачу:
- PORTB = PORTB | 32;
- PORTB = PORTB | (1 << 5);
- PORTB = PORTB | (1 << PB5);
- PORTB |= (1 << PB5);
Наиболее удобно использовать последнюю краткую запись, где используется комбинирования операция логического ИЛИ и присвоения, в данном случае PB5.
К примеру константа PB5 (канал 5 порта B, 6-й бит регистра) определена в файле /usr/lib/avr/include/avr/iom8.h для микроконтроллера ATmega8 и она равна числу 5.
Как установить несколько бит в регистре? - можно вызвать поочередно две конструкции с операторами, а можно все выполнить одной командой. Допустим нужно установить 2-й и 6-й биты в регистре порта PORTD, что соответствуют каналам PD1 и PD5:
- PORTD |= ( 1 << 1 ) | ( 1 << 5 );
- PORTD |= ( 1 << PD1 ) | ( 1 << PD5 );
Сброс битов в регистре порта
Для сброса разрядов в регистре порта мы будем использовать битовую операцию "&" (логическое "И"), которая применяется к двум битам (бинарная операция) и даёт единицу только в том случае если оба исходных бита имеют единичное значение, также нам пригодится битовая операция "~" (логическое "НЕ", инверсия).
Давайте выполним сброс 5-го бита в регистре порта PORTD, что в свою очередь выполнит установку низкого уровня на канале PD4. Допустим что сейчас в регистре PORTD содержится число 157, которое в битовом представлении выглядит как 10011101.
Для того чтобы сбросить 5-й бит (10011101) в регистре порта PORTD мы подготовим маску (как при установке битов), инвертируем ее биты "~", а потом выполним битовую операцию "&" над текущим значением регистра и полученной инвертированной маской.
Для подготовки маски выполним сдвиг битов на 4 разрядов в числе 1 (00000001).
0000 0001 (1)
<< 4
----------------
0001 0000 (16)
Маска готова, получили число 16 (00010000), 2 в 4-й степени. Выполним инверсию битов:
0001 0000 (16)
~
----------------
1110 1111 (239)
Готово, осталось применить маску к содержимому регистра порта PORTB используя битовую операцию "&":
1001 1101 (157)
&
1110 1111 (239)
----------------
1000 1101 (141)
Теперь в содержимом регистра PORTD значение 5-го бита установлено в 0 с сохранением положений остальных бит. В языке Си данные операции можно выполнить используя любую из приведенных ниже, идентичных по результату команд:
- PORTD = PORTD & ~ 16;
- PORTD = PORTD & 239;
- PORTD = PORTD & ~( 1 << 4 );
- PORTD = PORTD & ~( 1 << PD4 );
- PORTD &= ~( 1 << PD4 );
В данном случае наиболее удобной и информативной формой команды будет последний укороченный вариант. Для одновременного сброса нескольких битов регистра можно использовать вот такие конструкции из операторов:
- PORTD = PORTD & ~( ( 1 << PD4 ) | ( 1 << PD6 ) );
- PORTD &= ~( ( 1 << PD4 ) | ( 1 << PD6 ) );
Проверка разрядов регистра
Теперь разберемся каким образом можно проверить разряды регистра на наличие в них 1 или 0. Это может потребоваться если нужно получить значение битов в регистрах специального назначения (флагов) микропроцессора, а также для чтения состояния различных устройств и модулей, которые передают свое состояние используя битовую структуру.
Как проверить значение установленного бита в регистре на Си? - для этого нужно подобрать специальное выражение с использованием битовых операций, результатом работы которого будет значение: правда (True) или ложь (False). Имея булево (bool) значение выражения мы можем использовать для работы условные операторы языка Си.
Например нам нужно проверить есть ли единица (1) в 3-м бите регистра PORTD, тем самым мы проверим есть ли высокий уровень на канале PD2 порта PORTD. Примем что текущее значение регистра - 10010101 (149).
Для проверки используем выражение, которое состоит из битовой маски с установленным битом для проверки и проверяемого регистра, к которым применен битовый оператор "&" (логическое И).
Готовым маску, в которой только 3-й бит установлен в 1. Для этого выполним сдвиг битов числа 1 на 2 разряда влево:
0000 0001 (1)
<< 2
----------------
0000 0100 (4)
Теперь применим битовую операцию "&" (логическое И) к содержимому регистра PORTD и получившейся маске:
1001 0101 (149)
&
0000 0100 (4)
----------------
0000 0100 (4)
В результате выражения получим число 4 (0000 0100).
В языке Си все числа которые НЕ равны "нулю" (-100, -5, 1, 500) являются логической истиной (True), а 0 - логической ложью (False).
Результат нашего выражения - число 4, которое является логической истиной (True), а это значит что 3-й разряд регистра PORTD содержит единицу.
Вот как будет выглядеть данное выражение из двух логических операций на языке Си:
PORTD & (1 << 2)
Такое выражение можно использовать в условных операторах (if) и операторах циклов (while), например:
while( PORTD & (1 << 2) ) { ... }
if( PORTD & (1 << PD2) ) { ... }
Для проверки состояния бита в регистре на ноль (0) используем такую же конструкцию, только к результату выражения применим логическую операцию инверсии "!" (логическое НЕ).
Логическая операция "!" переворачивает значение с правды (True) на ложь (False), и наоборот. В отличие от битовой операции инверсии, которая переворачивает биты с 1 на 0 и наоборот, логическая операция инверсии оперирует с логическими значениями: правда (True) на ложь (False).
1 = True | 0 = False | 122 = True | (149 & (1 << 2)) = True |
!1 = False | !0 = True | !(5-1) = False | !(149 & (1 << 2)) = False |
Пример выражения на языке Си для проверки на ноль (0) 3-го бита в регистре порта PORTD (канал PD2):
while( !(PORTD & (1 << 2)) ) { ... }
if( !(PORTD & (1 << PD2)) ) { ... }
Здесь выражение "PORTD & (1 << 2)" берется в круглые дужки, что позволяет получить результат этого выражения, к которому потом и будет применен оператор логической инверсии.
Инверсия состояния бита в регистре
Иногда может понадобиться изменить состояние определенного бита в регистре на противоположное - выполнить инверсию состояния бита.
Для подобной операции отлично подходит битовый оператор "^" (исключающее ИЛИ, XOR). Чтобы выполнить инверсию определенного бита в регистре нужно создать маску, в которой этот бит установлен, а потом применить к содержимому регистра и полученной маске бинарный оператор "^", потом останется записать полученный результат в регистр и готово.
Возьмем, к примеру, что нужно погасить светодиод, который подключен к каналу PD5 порта PORTD. Если светодиод светится то это значит что в на канале PD5 присутствует высокий уровень, соответственно это значит что в регистре порта PORTD бит под номером 6 (PD5 = 5, 6-й бит в байте регистра) установлен в 1. Допустим что содержимое регистра порта PORTD сейчас - 10111010 (число 186, 1 байт, 8 разрядов, 6-й разряд = 1).
Подготовим маску, для установки 6-го бита нам необходимо сдвинуть все биты числа 1 на 5 разрядов:
0000 0001 (1)
<< 5
----------------
0010 0000 (32)
Применим маску к содержимому регистра порта PORTD:
1011 1010 (186)
^
0010 0000 (32)
----------------
1001 1010 (154)
Как видите, 6-й бит в байте регистра, который раньше был 1, сейчас установлен в 0 (1001 1010). Теперь осталось записать число в регистр порта и задачу можно считать выполненной.
Примеры использования такой конструкции на языке Си:
- PORTD = PORTD ^ 32;
- PORTD = PORTD ^ (1<< 5);
- PORTD = PORTD ^ (1<< PD5);
- PORTD ^=(1<< PD5);
Как и в предыдущих примерах по установке и сбросу битов, последняя конструкция (я ее выделил) является наиболее краткой и понятной для использования.
Заключение
С первого взгляда выражения наподобие "&", "!", "PD1", ">>" с их комбинациями могут показаться очень сложными. Но если собрать все терпение с вниманием и один раз хорошенько разобраться, попробовав примеры на практике, то в будущем будет уже не сложно понять как работают такие конструкции в коде программы и какой результат от них ожидать.
Логические и битовые операции присутствуют во многих языках программирования, не только в Си. Поэтому данный опыт будет полезен практически любому программисту.
Начало цикла статей: Программирование AVR микроконтроллеров в Linux на языках Asembler и C.
Как установить несколько бит в регистре? - можно вызвать поочередно две конструкции с операторами, а можно все выполнить одной командой. Допустим нужно установить 2-й и 6-й биты в регистре порта PORTD, что соответствуют каналам PD1 и PD5:
PORTB |= ( 1 << 2 ) | ( 1 << 6 );
В таблице раздела Битовые операторы в языке Си в последнем столбце ошибочка, или как?
Дмитрий, спасибо за внимательность. Ошибка исправлена.
Недавно получил такое письмо:
Знак "|" - это логическая операция ИЛИ (OR).
К примеру, записи ниже эквивалентны между собою:
"SREG |= (1<<7);" - то же самое что и "SREG = SREG | (1<<7);"
Допустим что SREG содержит значение 01110001, вот что получится:
1 = 00000001 (в двоичном представлении)
00000001 << 7 = 10000000 (число 128)
01110001 | 10000000 = 11110001
Таким образом, мы выполнили установку восьмого(старшего) бита в регистре SREG (регистр состояния микроконтроллера) чем разрешили использование перерываний в МК.
Теперь рассмотрим вот эту запись:
DDRD = (0 << PD2);
Здесь выполняется логический сдвиг числа 0 влево на 2 позиции (PD2=2, смотрим значение для своего МК).
00000000 << 2 = 00000000
Приведенную выше запись можно использовать для наглядности, хотя по сути она эквивалентна:
DDRD = 0
Следующая конструкция нужна для установки бит ISCxx в регистре MCUCR (настройка прерываний):
MCUCR = (0<<ISC00) | (1<<ISC01);
Она эквивалентна вот этим конструкциям:
MCUCR = 0 | (1<<ISC01);
MCUCR = (1<<ISC01);
В первом варианте выражение "0<<ISC00" используется для наглядности, так более понятно что и как установлено в регистре.
Хочу выразить Вам огромную благодарность!
Как для новичка, то все очень доступно и понятно изложено.
Спасибо за Ваш труд!
«В языке Си все числа что равны или больше 1 являются логической истиной (True), а 0 - логической ложью (False).»
Исправьте , так как в Си, все числа что не равны 0 (-1,-5,1,5,150…) являются логической истиной (True), а 0 — логической ложью (False)
Ivan, благодарю за хорошее замечание! Исправил.
Спасибо,
Переехал с другого языка и никак не мог понять, что же там внутри происходит, что значат такие записи. Истолковано все отлично. Про битовые сдвиги инфы достаточно. Но здесь истолкована сама логика записи. Что важно для запоминания такой нотации. Третий день вникаю. Помогли! Спасибо!
Не очень понял:
"Операторы битового сдвига ">>" и "<<" в языке программирования Си выполняют сдвиг битов в переменной вправо и влево на указанное число элементов. Биты которые были сдвинуты теряются, а с другой стороны появляются нули - выполняется логический сдвиг."
если для вышеприведенного примера
1000 1000 (136)
осуществить сдвиг первого разряда 5 раз, то должны получить
0000 0000 - т.к. двигается ноль, а все, что за ним слева "затирает" в нули.
Если мы 1<<5 сначала записываем первый бит единицу , а потом ее двигаем, то в чем смысл, ведь в первый (PB0) нам надо сначала каким то образом записать единицу? Поэтому как я понимаю работу сдвигового регистра сдвигается значение разряда, а не желаемое значение разряда. Ведь запись 1<
В моем примере при помощи конструкции "1<<5" мы получаем байт с единым установленным битом (остальные биты - нули), потом это число применяется как маска (применяем операцию "|") для установки этого же бита в нужной позиции другого числа (текущего полученного значения регистра МК) без сброса остальных установленных в нем битов.
1000 1000 (136)
1000 1000 << 5 = 1 0001 0000 0000 (4352) - биты сдвинулись влево, справа появились нули, такое число можно записать в 16-биный регистр (2 байта по 8 бит).
Если же это число попробовать записать в 8-ми битный регистр МК то там окажется число 0000 0000 (0). Для демонстрации сделал небольшую анимацию работы программы в симуляторе AVRStudio, где в регистр микроконтроллера помещаем число 1000 1000 сдвинутое на 5 разрядов влево:
000'1' 0000 (16)
Маска готова, получили число 16 (00010000), 2 в 4-й степени. Выполним инверсию битов:
00'1'0 0000 (16)
опечаточка))
d9v4, спасибо! Исправлено.
В разделе "Проверка разрядов регистра" в предложении "Готовым маску, в которой только 3-й бит установлен в 0. Для этого выполним сдвиг числа 1 на 2 разряда:", похоже опечатка, т.к. 3-й бит маски устанавливается не нулем а единицей.
Magnetic, спасибо Вам! Опечатка исправлена.
Сложновато. Я в CVAVR привык к записи типа PORTB.2=1; А все записи имеющие ^,!,>>,&&,| и т д с ума сводят.
Спасибо, все очень понятно, с примерами и без нагромождений. Вопрос: если маску вместо скажем выражения сдвига 1<<5 сразу писать 32 , то при выполнении программы это займет столько же времени(тактов) контроллера, или меньше? А при записи изменения состояния нескольких битов регистра одной строкой, они меняются за один такт или по очереди?
Конструкция "1<<5" при компиляции будет преобразована в число 32, поэтому после прошивки количество требуемых тактов процессора не изменится.
При использовании цепочки из нескольких конструкций для выполнения потребуется дополнительный такт микропроцессора (МП).
Можете проверить это используя пошаговое выполнение в AVR Studio, интересный пример с анимацией я приводил в одном из комментариев к статье Простая программа для AVR микроконтроллера на языке Си.
Не понимаю, куда вводить:
cat /usr/lib/avr/include/avr/iom8.h | grep PD
Flagmans, эта команда из примера выполняется в консоли операционной системы GNU/Linux и выводит на экран дополнительную полезную информацию. Все остальные примеры из статьи относится к языку программирования Си и не привязаны к какой-то конкретной платформе.
Отличная статья! Все очень подробно и самое главное на примере AVR. Я думал буду с этим долго разбираться, но тут прям все так залилось на раз и два. Спасибо!
.
P/S
Всегда думал:"Нафиг нужны эти битовые операторы в Си?" И вот [s]докатился[/s] дожил до их применения
Прикольно так же что 1 это ж литерал, а при помощи битовых операций мы ее изменяем.
Присоединюсь к лайкам. Мне тоже информация легла очень ровно и понятно. Давно ждал именно такого объяснения, чтоб все срослось в одну картину.
Спасибо!
Как и многие делаю первые шаги в изучении микроконтроллеров AVR по книге Белова А.В. Примеры на Си там приведены, написанные в платной CodeVisionAVR. Долго не мог понять, почему они у меня не работают в Atmel Studio. Оказалось, что обращение к отдельным разрядам наподобие PORT5.2, как написано в книге, не работает в Atmel Studio, вот тут и помогла ваша статья.
В общем, ph0en1x, спасибо!
(Сайтик в закладки)
Здравствуйте, есть код для atmega323 и подобных:
void twi_Start(void){
TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //TWSTA - флаг состояния СТАРТ.
while(!(TWCR & (1<<TWINT))){}; //проверяет бит TWINT на ноль, а зачем???
}
Зачем там проверка бита TWINT регистра TWCR на ноль, если его только что в 1 установили?
Здравствуйте. С интерфейсом TWI в AVR пока что не работал, но несколько минут изучения протокола дало ответ на ваш вопрос.
В строчке "TWCR=..." выполняется сброс флага прерывания TWINT и включение модуля TWI (Two Wires Interface).
Строка "while..." содержит цикл ожидания готовности МК к передаче данных (МК выдаст на шину состояние START, флаг TWINT будет аппаратно установлен) после включения шины. Как только МК аппаратно установит флаг TWINT и будет готов к передачи данных, вечный цикл с проверкой бита TWINT завершится и МК перейдет к выполнению других операций.
Изучите работу протокола TWI, посмотрите различные примеры, после этого подобный код не будет вызывать у вас вопросов.
Автору огромный респект! Наконец то я разобрался, что это за маска и для чего она нужна.
И еще, по поводу проверки битов. По мне так проще использовать регистр PINx для проверки разрядов регистра, для единицы (1) это будет ~PINB & (1<<3). Тем более это регистр для чтения, и при ошибке ничего никуда не запишется. Хотя, каждый ..., как говорится, как хочет)
Подскажите пожалуйста, в секции (Установка битов в регистре порта) условие (Допустим нужно установить 2-й и 6-й биты в регистре порта PORTD) в примере:
PORTB |= ( 1 << 1 ) | ( 1 << 5 );
PORTB |= ( 1 << PD1 ) | ( 1 << PD5 );
Правильно я понимаю, что вместо PORTB должно быть PORTD?
Я дико извиняюсь, только только учусь и каждая запись для меня очень важна!
Здравствуйте, Виктор. Верно подметили, в статье была опечатка.
Исправил PORTB на PORTD для последнего примера в разделе "Установка битов в регистре порта".
PORTD - порт под названием D, а PD1 - это один из выходов (Pin) порта D под номером 1. С другими константами портов и пинов - аналогично.
На практике конструкция "PORTB |= ( 1 << PD1 ) | ( 1 << PD5 );" с ошибочной записью все же будет работать, поскольку значения констант в заголовочных (.h) файлах для AVR микроконтроллеров равны по значениям: PD1=PB1, PD2=PB2..PD7=PB7.
Автор =топ_1
Наконец-то я понял что к чему!!
Приветствую. Надо избавиться от digitalRead , заменяю на ~PINx&(1<<xx), но код работает не корректно (энкодер на пинах A2, A3 mega328). Может кто подсказать в чем причина?
*...меняю "digitalRead" на "~PINC&(1<<PCx)" , код перестает корректно работать... энкодер перестает корректно работать.
Здравствуйте. Не понятно зачем вы используете операции инверсии (логического НЕ, ~), ведь перед DigitalRead нет "!".
Попробуйте вот такие записи:
В данной статье описано как работают подобные конструкции, с примерами.