Использование битовых масок в ESP32

Битовые маски используются, для того, чтобы можно было получить/передать множество параметров в одной переменной. 
Общий принцип следующий: в целочисленной переменной, сохраняется значение параметров, которые могут принимать два значения, 0 или 1. Т.е. бит, отсюда и название. 
Для сохранения и чтения параметров используют значения разряда в числе (еще его называют "словом). 
Например 1001 - тут мы видим, что в первом (считается справа налево) и четвертом разряде хранится единица, а во втором и третьем - ноль.
В данном примере 1001 в двоичном счислении это цифра 9 в десятично счислении. Но хранится число в шестнадцатиричном коде. 
1001(двоичный код) = 9 (десятичный код) = 0x9 (шестнадцатеричный код, где 0х -указывает, что число хранится в шестнадцатеричном коде).
Для работы с битами в C++ используются побитовые операции: 
  • И - and (&) - побитовое умножение
  • ИЛИ - or (|) - побитовое сложение 
  • ИСКЛЮЧАЮЩЕЕ ИЛИ - xor (^) - логическое сложение по модулю
  • ИНВЕРСИЯ - NOT(~) - побитовое отрицание или инверсия
  • Сдвиг вправо - (>>)
  • Сдвиг влево - (<<)
Приведем примеры, что делает каждая из этих операций. Все они работают в двоичном представлении над каждым битом числа.
И - and (&)
0 & 0 = 0
1 & 0 = 0
0 & 1 = 0
1 & 1 = 1
ИЛИ - or (|)
0 | 0 = 0
1 | 0 = 1
0 | 1 = 1
1 | 1 = 1
ИСКЛЮЧАЮЩЕЕ ИЛИ - xor (^)
0 ^ 0 = 0
1 ^ 0 = 1
0 ^ 1 = 1
1 ^ 1 = 0
ИНВЕРСИЯ - NOT(~)
~1 = 0
~0 = 1

Сдвиг вправо просто сдвигает вправо всё биты на заданное количество разрядов.

1001 >> 2 = 0010 (все в двоичном счислении, бывшие справа 01, просто отбрасываются, слева замещаются 0).

Сдвиг влево сдвигает влево на заданное количество разрядов.

1001 << 2 = 0100 (бывшие слева разряды 10 сдвигаются и теряются навсегда, справа замещаются нулями)

Вышеописанные операции применяют далее для работы с битовыми масками или параметрами переданными битовым словом. 

Примеры

Определение значения единичного бита
Для определения значения бита в определенном разряде применяются операции сдвига вправо на количество, чтобы нужный бит стал в первый разряд и логически умножаем полученное число на 1.
Например мы желаем узнать значение 4 разряда в слове: 1001001 
1001001 >> 3 & 1 итог = 1. Как это получилось? Разберем пошагово:
1 шаг - побитовый сдвиг вправо.
1001001 >> 3, итог 0001001 - все биты сдвинулись на три вправо. 
2 шаг - умножение на 1. Распишем в столбик:
0001001
&
0000001
Итог:
0000001 Это и есть единица. 
Если бы значение нужного бита было бы 0, на выходе был бы 0.

Работа с битовой маской
Битовые маски используют, чтобы сравнивать значение одного или нескольких бит. 
И в этом деле есть свой нюанс.
Дело в том, что для того, чтобы задать битовую маску в основном используют шестнадцатеричное представление числа. 
Вы будете встречать в коде программ числа вида 0x3000. Где "0х" означает, что далее идет шестнадцатеричное число. 
Это вносит определенные сложности.
Разберем на примере работы с выводами контроллера. Из примера "мигающего светодиода".
К примеру мы желаем определять значения на 12 и 13 выводах котроллера EMS32 и если на обеих ногах 0, то устанавливается высокая скорость моргания светодиода, иначе низкая.
Битовая маска для задания контрольного значения будет иметь еще один нюанс. Дело в том, что первый бит будет контролировать 0 вывод контроллера. Значит, для того, чтобы определять 12 и 13 вывод, нам нужно будет использовать 13 и 14 разряды в полученном от контроллера слове, содержащим состояния выводов. 

Итак, как задать маску.

Берем в двоичном коде число, содержащее в 13 и 14 разряде единицы:
11000000000000 - вот что получилось.
Сколько разрядов слева роли для нас не играет. Они заполнятся нолями. 
Воспользуемся онлайн конвертером и конвертируем данное двоичное число в шестнадцатеричное. 
Получим следующее:
11000000000000 = 0х3000 - это и будет значение для нашей маски. 
А код будет выглядеть примерно так (инициализация и прочее пропущено):

#define BUTTON_MASK_12_13 0x3000 //бит маска для 12 и 13 выхода контроллера (13 и 14 разряда в слове)

while(1) {
input_GPIO = gpio_input_get(); // Считываем значения входов с 0 по 31

gpio_output_set(0, LED_GPIO_SET, 0, 0); // светодиод погашен

printf("Светодиод погас\n");

if((input_GPIO & BUTTON_MASK_12_13) == 1) vTaskDelay(250 / portTICK_PERIOD_MS); //задержка 0,25 сек
else vTaskDelay(500 / portTICK_PERIOD_MS); //задержка 0,5 сек

gpio_output_set(LED_GPIO_SET, 0, 0, 0);// светодиод горит

printf("Светодиод горит\n");

input_GPIO = gpio_input_get();

if((input_GPIO & BUTTON_MASK_12_13) == 1) vTaskDelay(250 / portTICK_PERIOD_MS); //задержка 0,25 сек
else vTaskDelay(500 / portTICK_PERIOD_MS); //задержка 0,5 сек
}

input_GPIO & BUTTON_MASK_12_13 - вот эта операция сравнивает нашу маску со значением 13 и 14 бита в переданном слове, содержащем состояние выводов. 
И если на обеих выводах 0, результатом операции будет 0, иначе значение, отличное от 0.

Почему может использоваться работа с переданным словом, а не опрос каждого вывода контроллера? Во первых это быстрее работает, а во вторых может понадобится считывать состояние шины данных в момент времени. 

На этом вводную статью о работе с битами и битовыми масками завершим. 
Если будут вопросы, пишите в комментариях. Постараемся разобрать и ответить. 


Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.