Битовые маски (Bit Masks) в Arduino
Битовые маски позволяют обращаться к отдельным битам в байте данных — например, для побитовой передачи информации через один вывод.
Битовые маски — это способ получить доступ к конкретным битам внутри байта. Особенно часто они нужны при последовательной передаче данных через один пин: состояние пина меняется для каждого бита передаваемого байта по очереди. Для этого используются побитовые операции (bitwise operations) и сама битовая маска.
Побитовые операции
Побитовые операции работают прямо на уровне отдельных битов. В Arduino (и в языке C/C++ вообще) доступны четыре основных оператора:
- AND (
&) - OR (
|) - Left Shift (
<<) - Right Shift (
>>)
AND (&)
Оператор AND даёт 1 в тех позициях, где оба входных значения равны 1. Во всех остальных случаях — 0.
Например:
x: 10001101
y: 01010111
x & y: 00000101
Это удобно для «вырезания» нужного бита: если наложить маску с единицей в нужной позиции, все остальные биты обнулятся.
OR (|)
Оператор OR (также называемый «включающим ИЛИ») даёт 1 там, где хотя бы одно из входных значений равно 1.
Например:
x: 10001101
y: 01010111
x | y: 11011111
OR используют, когда нужно принудительно выставить определённые биты в 1, не трогая остальные.
Left Shift (<<)
Оператор сдвига влево смещает все биты на указанное число позиций в сторону старшего разряда.
Например:
y = 1010
x = y << 1
yields: x = 10100
Все биты байта сдвигаются на одну позицию влево. Бит, вышедший за пределы байта слева, просто отбрасывается, а справа появляется 0.
Right Shift (>>)
Оператор сдвига вправо работает симметрично: биты смещаются к младшему разряду.
Например:
y = 1010
x = y >> 1
yields: x = 0101
Бит, вышедший за правый край, теряется, а слева добавляется 0.
Практический пример: передача байта через один пин
Возьмём значение 170, которое в двоичном виде выглядит как 10101010. Нужно «выдать» этот байт побитово через пин 7. Код выглядит так:
byte transmit = 7; //определяем пин передачи
byte data = 170; //значение для передачи, двоичное 10101010
byte mask = 1; //наша битовая маска
byte bitDelay = 100;
void setup()
{
pinMode(transmit,OUTPUT);
}
void loop()
{
for (mask = 00000001; mask>0; mask <<= 1) { //перебираем биты маски
if (data & mask){ // если побитовое AND истинно
digitalWrite(transmit,HIGH); // отправляем 1
}
else{ //если побитовое AND ложно
digitalWrite(transmit,LOW); // отправляем 0
}
delayMicroseconds(bitDelay); //задержка
}
}
Здесь цикл FOR перебирает все восемь бит с помощью переменной-маски mask. На каждой итерации маска сдвигается на одну позицию влево — так «единица» последовательно проходит через каждую битовую позицию байта.
Как это работает шаг за шагом
Используется оператор <<=, который является сокращённой формой записи << — он просто компактнее.
Первая итерация: mask = 00000001
00000001
& 10101010
________
00000000
Результат операции AND равен 0, значит пин устанавливается в LOW.
Вторая итерация: mask = 00000010
00000010
& 10101010
________
00000010
Результат равен 1 (не ноль), значит пин устанавливается в HIGH.
Цикл продолжает работу, пока единица в маске не выйдет за пределы восьми бит — тогда mask становится равна 0, условие цикла нарушается, и все восемь бит переданы.
Типичные ошибки
- Перепутать
&и&&: одиночный&— это побитовый AND, двойной&&— логический AND для условий. Это разные операции. - Забыть начальное значение маски: маска должна начинаться с
1(то есть00000001), иначе ни один бит не будет выделен корректно. - Неверное направление сдвига: если данные нужно передавать начиная со старшего бита (MSB), маска должна стартовать с
10000000и сдвигаться вправо (>>).
Где это пригодится
Битовые маски активно применяются при:
- программной реализации протоколов передачи данных (SPI, 1-Wire, собственные протоколы);
- работе с регистрами микроконтроллера напрямую;
- компактном хранении набора флагов (8 булевых значений в одном байте);
- обработке пикселей в графических приложениях.