Немного о портах и битовых операциях
Многие из вас видели и наверно даже понимают, что такое
DDRB |= (1 << 5);
PORTB |= (1 << 5);
но возможно не всем до конца ясна логика битовых операторов и их предназначения при установке значений в порты.
Основная опасность и в то же время преимущество управления портами напрямую без прослоек типа digitalWrite() - это то, что вы можете управлять сразу 8 портами всего за 1 строчку кода, которая в свою очередь выполняется достаточно быстро, намного быстрее стандартной функции.
Но если необходимо подергать частью портов так, чтобы не сбить какие-либо связи на других портах - надо изолировать эти обращения. Тут на помощь и придет битовая логика.
Например надо настроить на вывод 11, 12, 13 порт уны, а 8, 9, 10 не трогать. Для этого нужен оператор "ИЛИ", который обозначается '|':
DDRB |= B00111000;
или
DDRB |= (B111 << 3);
Оба варианта делают абсолютно одно и то же. Сдвиг влево (<<) задается количеством бит, на которое сдвигается значение. Сдвигая B111 на три бита, получаем B111000. Можно сказать, что мы добавляем определенное количество значащих нулей.
Теперь разберем, почему оператор | так важен. Для этого надо понять, что делает этот оператор:
0101 (decl 5)
0011 (deс 3)
OR=
0111 (dec 7)
Если по-простому - при установке единицы меняет 0 на 1, а при установке 0 в бит, где есть единица - не меняется ничего, так же и при установке ноля в 0. То есть мы установили на вывод три бита, не затронув остальные, которые так же могли быть уже назначены выводами.
Но если надо выставить 0 так, чтобы не прибить остальные биты в 0, нужен оператор "И", обозначающийся &. Так же к нему понадобится оператор "НЕ" - обозначается ~.
Итак, выключаем светодиод на 13 пине:
PORTB &= ~(1 << 5);
или
PORTB &= ~B00100000;
Я лично предпочитаю левый сдвиг тогда, когда мне надо выставить определенный порт, так не запутаюсь в количестве нолей, и записывать битовые значения, когда мне надо дернуть несколько. Так нагляднее, а насчет быстродействия сказать не могу - в ассемблере и то, и другое выполняется в одну команду.
Итак, ~ переворачивает все с ног на голову:
~B00100000 = B11011111
А оператор & как раз нолем меняет любую единицу на ноль, а единицей не меняет ничего. В итоге - 1 выключенный бит без беспокойства остальных. "И" в двоичной математике - это умножение: умножаем 1 на 0 - и получим 0, а 1*1=1.
Так же есть исключающее "ИЛИ" или XOR в английском варианте - ^. С помощью него можно поменять значение определенного бита. Например:
B11011111 ^= B00110000;
будет иметь в итоге
B11101111.
Вкратце: с помощью единицы меняем бит на противоположный, ноль ничего не делает.
Теперь можно поговорить о цифровом чтении портов. Перед чтением необходимо установить 0 в нужные биты:
DDRB &= ~B00000001;
На чтение выставлен только 8 порт уно с помощью этой конструкции, остальные не затронуты и могут оставаться как вводами, так и выводами.
И благодаря тому, что мы оперируем сразу 8 портами одновременно, можно не городить конструкции типа
if(digitalRead(8) && digitalRead(9) && digitalRead(11) && digitalRead(13)){....}
А сделать небольшую маску сравнения:
if(PINB & B00101011){....}
Работает точно так же, но экономит место в памяти контроллера, плюс выигрыш в быстродействии. Главное от наводок защититься подтягивающими резисторами, иначе даже digitalRead не спасет.
Напоследок расскажу один чит, который подглядел в оригинальных библиотеках для shiftOut, и не сразу смог понять, что же он делает:
!!(val & (1 << i))
Разгадка оказалась на поверхности, но о ней по порядку: '!' - это НЕ в логических операндах, работает так:
!1=0
!255=0
!0=1В данном случае двойной восклицательный знак позволяет привести 8, 16, 32... битное число с помощью маски в один лишь бит, без всяких нолей, значащих и нет:
0. !!(val & (1 << i))
1. !!(B1110 & (1<<2))
2. !!(B0100)
Поскольку B0100 равно или больше единицы, однократное НЕ приведет к нолю, а двухкратное к единице, переход через 0 позволяет оставить лишь суть:
3. !(0)
4. (1)
Таким нехитрым образом можно разобрать любое число на отдельные биты.
Просьба разжевать по конструкции if(PINB & B00101011){....}
Как я ее понял- в условии висит побитовое И между состоянием порта и маска. Что получится после побитового сложения? И каков критерий выполнения "if"? Насколько я помню проверяется истина-ложь относительно выражения в скобках, но истиной оно будет во всем диапазоне от 1 до 255, или я чего-то не понимаю в побитовых операциях...
Благодарю.
а можно с начала?
что делает DDRB, что делает команда DDRB |= (1 << 5);
PORTB |= (1 << 5);
разжевать для тех, кто в танке.
интересно конечно, но далеко не для начинающих
а у вас есть свой модуль для созданию многоуровневого меню? можете поделиться?