Меню Рубрики

Установка бита в ноль

Как установить, сбросить, проверить нужный бит или битовые операции

Для тех кому надо освежить знания оставлю тут памятку, более подробно эти операции будут рассмотрены в статье.

Независимо от того какие микроконтроллеры Вы собираетесь программировать, первое что придётся освоить — это битовые операции.
Битовых операций в языке Си всего 6.

Начнем с того, что выводы микроконтроллера условно разделены на порты, у Atmega16 порт состоит из 8 выводов, у STM32f103 из 16 выводов.

Установить в 1 нулевой бит порта B можно следующим образом.

Таким образом, мы установили нулевой бит в 1, а все остальные в 0, то есть мы переопределили все биты порта. А что если мы хотим установить в 1 только нулевой бит и не задеть остальные? В таком случае нужно воспользоваться побитовым ИЛИ.

В результате мы изменили только нулевой бит порта.
Надо отметить, что в микроконтроллерах счёт начинается с нуля, то есть первый бит будет иметь нулевой порядковый номер, а порядковый номер восьмого бита будет 7.

Битовая операция НЕ — изменяет значение бита на противоположное.

Эта операция совместно с битовым НЕ может использоваться для сброса конкретного бита в ноль.

При такой записи, мы выставляем в единицу бит, который хотим обнулить, затем инвертируем получившееся число

В итоге мы выставили в 0 только первый бит.

Также эту операцию можно использовать для проверки чему равен бит. Например, нам надо проверить чему равен нулевой бит порта B, это можно сделать с помощью следующей конструкции.

Если бит равен единице, выражение в скобках будет правда, иначе — ложь.

Побитовое исключающее ИЛИ — если сумма соответствующих битов число чётное, результирующий бит 0, иначе 1.

С помощью этой операции можно инвертировать состояние выбранного бита. Например, к нулевому выводу порта подключен светодиод и при выполнении одного и того же фрагмента кода, мы хотим чтобы он погас если горит и наоборот, зажёгся если не горит.

Также с помощью этой операции можно определить равенство регистров. Например, мы хотим сравнить в одинаковом ли состоянии находятся порты B и D.

Если результат равен нулю, то содержимое регистров равно.

Логический сдвиг влево — все разряды при этом сдвигаются на одну позицию влево, самый левый бит теряется, а в самый правый бит записывается 0.

Операция логического сдвига влево эквивалентна умножению на 2.
0b0000 1011 = 11
0b0001 0110 = 22

Логический сдвиг вправо — все разряды при этом сдвигаются на одну позицию вправо, самый правый бит теряется, а в самый левый бит записывается 0.

источник

Побитовые операции | Программирование микроконтроллеров AVR на C

Побитовые операции основаны на логических операциях, которые мы уже рассмотрели ранее. Они играют ключевую роль при программировании микроконтроллеров AVR и других типов. Практически ни одна программа не обходится без применения побитовых операций. До этого мы намеренно избегали их, чтобы облегчить процесс изучения программирования МК.

Во всех предыдущих статьях мы программировали только порты ввода-вывода а и не задействовали дополнительные встроенные узлы, например, такие как таймеры, аналогово-цифровые преобразователи, прерывания и другие внутренние устройства без которых МК теряет всю свою мощь.

Прежде, чем перейти к освоению встроенных устройств МК, необходимо научится управлять или проверять отдельные биты регистров МК AVR. Ранее же мы выполняли проверку или устанавливали разряды сразу всего регистра. Давайте разберемся, в чем состоит отличие, а затем продолжим далее.

Побитовые операции

Чаще всего при программировании микроконтроллеров AVR мы пользовались двоичной системой счисления, поскольку она имеет большую наглядность по сравнению с шестнадцатеричной и хорошо понятна для начинающих программистов МК. Например, нам нужно установить только 3-й бит порта D. Для этого, как мы уже знаем, можно воспользуемся следующим двоичным кодом:

Однако этой командой мы устанавливаем 3-й разряд в единицу, а все остальные (0, 1, 2, 4, 5, 6 и 7-й) мы сбрасываем в ноль. А теперь давайте представим ситуацию, что 6-й и 7-й разряды задействованы как входы АЦП и в это время на соответствующие выводы МК поступает сигнал от какого-либо устройства, а мы, применяемой выше командой, обнуляем эти сигналы. В результате чего микроконтроллер их не видит и считает, что сигналы не приходили. Поэтому вместо такой команды нам следует применить другую, которая бы установила только 3-й бит в единицу, при этом не влияя на остальные биты. Для это обычно применяется следующая побитовая операция:

PORTD |= (1 Установка отдельного бита

Для установки отдельного бита, например порта D, применяется побитовая операция ИЛИ. Именно ее мы применяли в начале статьи.

PORTD = 0b00011100; // начальное значение

PORTD = PORTD | (1 Сброс (обнуление) отдельных битов

Для сброса отдельного бита применяются сразу три ранее рассмотренные команды: Переключение бита

Читайте также:  Установка распределительного щита в гараже

Кроме установки и сброса также применяется полезная команда, которая переключает отдельный бит на противоположное состояние: единицу в ноль и наоборот. Данная логическая операция находит широкое применение при построении различных световых эффектов, например, таких как новогодняя гирлянда. Рассмотрим на примере PORTA

PORTA ^= (1 Проверка разряда на наличие логического нуля (сброса) с if

if (0==(PIND & (1 Проверка разряда на наличие логической единицы (установки) с if

if (0 != (PIND & (1 Ожидание сброса бита с while

while (PIND & (1 Ожидание установки бита с while

Здесь синтаксис языка С позволяет записать код двумя наиболее распространёнными способами. На практике применяются оба типа записи.

источник

_______________ сдвиг влево unsigned char tmp = 3; //0b00000011
tmp = tmp //теперь в переменной tmp число 6 или 0b00000110

tmp = tmp //теперь в переменной tmp число 48 или 0b00110000

Выражения, в которых над переменной производится какая-либо операция, а потом результат операции присваивается этой же переменной, можно записывать короче, используя составные операторы.

tmp = 7; //0b00000111
tmp //сокращенный вариант записи
//теперь в переменной tmp число 28 или 0b00011100

Операция сдвига влево на n разрядов эквивалентна умножению переменной на 2 n .

_______________ сдвиг вправо >> _______________

Пример для беззнаковой переменной

unsigned char tmp = 255; //0b11111111
tmp = tmp >> 1;
//теперь в переменной tmp число 127 или 0b01111111

tmp >>= 3; //сокращенный вариант записи
//теперь в переменной tmp число 15 или 0b00001111

Пример для переменной знакового типа

int tmp = 3400; //0b0000110101001000
tmp >>= 2;
//теперь в переменной число 850 или 0b0000001101010010

tmp = -1200; //0b1111101101010000
tmp >>= 2;
//теперь в tmp число -300 или 0b1111111011010100
//видите — два старших разряда заполнились единицами

Операция сдвига вправо на n разрядов эквивалентна делению на 2 n . При этом есть некоторые нюансы. Если потерянные младшие разряды содержали единицы, то результат подобного “деления” получается грубоватым.

Например 9/4 = 2,5 а 9>>2 (1001>>2) равно 2
11/4 = 2,75 а 11>>2 (1011>>2) равно 2
28/4 = 7 а 28>>2 (11100>>2) равно 7

_______________поразрядная инверсия

tmp;
//теперь в переменной tmp число 161 или 0b10100001

tmp;
//теперь в tmp снова число 94 или 0b01011110

_______________ поразрядное ИЛИ | ______________

Оператор | обычно используют для установки заданных битов переменной в единицу.

tmp = 155
tmp = tmp | 4; //устанавливаем в единицу второй бит переменной tmp

155 0b10011 0 11
|
4 0b00000 1 00
159 0b10011 1 11

Использовать десятичные числа для установки битов довольно неудобно. Гораздо удобнее это делать с помощью операции сдвига влево //устанавливаем в единицу четвертый бит переменной tmp

Установить несколько битов в единицу можно так

tmp = tmp | (1 //устанавливаем в единицу седьмой, пятый и нулевой биты переменной tmp

С помощью составного оператора присваивания |= можно сделать запись компактней.

tmp |= (1 //обнуляем третий бит переменной tmp

155 0b1001 1 011
&
247 0b1111 0 111
147 0b1001 0 011

Видите, третий бит стал равен 0, а остальные биты не изменились.

Обнулять биты, используя десятичные цифры, неудобно. Но можно облегчить себе жизнь, воспользовавшись операторами //обнуляем третий бит

(1 1 011 & 0b1111 0 111
результат 0b1001 0 011

Обнулить несколько битов можно так

((1 //обнуляем третий, пятый и шестой биты

Используя составной оператор присваивания &= ,можно записать выражение более компактно

((1 if ((tmp & (1 // блок будет выполняться, только если установлен
// второй бит переменной tmp
>

if ((tmp & (1 // блок будет выполняться, только если не установлен
// второй бит переменной tmp
>

_______________побитовое исключающее ИЛИ ^ _______________

Оператор ^ осуществляет операцию логического исключающего ИЛИ между соответствующими битами двух операндов. Результатом операции логического исключающего ИЛИ будет 0 в случае равенства битов. Во всех остальных случаях результат будет 1. Это проиллюстрировано в табице истинности.

tmp = 155;
tmp = tmp ^ 8; // инвертируем четвертый бит переменой tmp

155 0b1001 1 011
^
8 0b0000 1 000
147 0b1001 0 011

Четвертый бит изменил свое значение на противоположное, а остальные биты остались без изменений.

tmp = tmp ^ 8; // опять инвертируем четвертый бит переменой tmp

147 0b1001 0 011
^
8 0b000 0 1 000
155 0b1001 1 011

Видите, четвертый бит снова изменил свое значение на противоположное.

Так записывать выражение намного удобнее

tmp = tmp ^ (1 / инвертируем третий бит переменой tmp

А так и удобно и компактно

tmp ^= (1 //инвертируем четверый бит

Можно инвертировать несколько битов одновременно

tmp ^= ((1 //инвертируем 4,2 и 1 биты

tmp = var1;
var1 = var2;
var2 = tmp;

Но используя оператор ^ переставить значения можно так:

var1 ^= var 2;
var 2 ^= var 1;
var 1 ^= var 2;

Чистая магия, хотя, честно говоря, я ни разу не пользовался таким приемом.

Читайте также:  Установка андроида 8 oreo самому

________________Директива #define__________________

//порт, к которому подключены кнопки
#define PORT_BUTTON PORTA
#define PIN_BUTTON PINA
#define DDRX_BUTTON DDRA

//выводы, к которым подключены кнопки
#define DOWN 3
#define CANCEL 4
#define UP 5
#define ENTER 6

int main()
<
//конфигурируем порт на вход,
//и включаем подтягивающие резисторы
DDRX_BUTTON = 0;
PORT_BUTTON = 0xff;

При задании символического имени можно использовать и выражения

#define MASK_BUTTONS ((1 #define не жалейте скобок чтобы четко задать последовательность вычисления выражений!

Некоторые выражения можно замаскировать под «функции».

Можно использовать многострочные определения, используя в конце каждой строки символ \

#define INIT_Timer() TIMSK = (1 #define – это задание макроопределений (или просто макросов). Вот как с помощью #define можно задать макросы для рассмотренных ранее операций с битами

#define SetBit(reg, bit) reg |= (1 #define ClearBit(reg, bit) reg &= (

(1 #define InvBit(reg, bit) reg ^= (1 #define BitIsSet(reg, bit) ((reg & (1 #define BitIsClear(reg, bit) ((reg & (1

пример использования:

SetBit(PORTB, 0); //установить нулевой бит порта B
InvBit(tmp,6); //инвертировать шестой бит переменной tmp

Определим макрос, вычисляющий квадрат числа:

выражение
tmp = SQUARE(my_var);
даст корректный результат.

А что будет если в качестве аргумента макроопределения использовать выражение my_var+1

Препроцессор заменит эту строчку на

а это вовсе не тот результат, который мы ожидаем.

Чтобы избежать таких ошибок не скупитесь на скобки при объявлении макросов!

выражение
tmp = SQUARE(my_var +1);
даст корректный результат, потому что препроцессор заменит эту строчку на
tmp = ((my_var + 1) * (my_var +1));

источник

Битовые операции

Введение

Я зык Си иногда называют макроассемблером за его тягу к железу. Если не использовать оптимизацию, можно даже примерно оценить, в какие конструкции на ассемблере преобразуется код программы. Простота и минимализм языка (простоту языка не путать с простотой программирования на языке) привели к тому, что на многих платформах си остаётся единственным высокоуровневым языком программирования. Без обзора побитовых операций, конечно, изучения языка было бы неполным.

Побитовые операции, как понятно из названия, позволяют оперировать непосредственно с битами. Большое количество примеров использования побитовых операций можно найти, например, в книге Генри Уоррена «Алгоритмические трюки для программистов». Здесь мы рассмотрим только сами операции и примитивные алгоритмы.

Побитовые И, ИЛИ, НЕ, исключающее ИЛИ

ЗАМЕЧАНИЕ: здесь и далее в примерах используются 8-битные числа для упрощения записи. Всё это верно и для любых других чисел.

Н апомню для начала, что логические операции И, ИЛИ, исключающее ИЛИ и НЕ могут быть описаны с помощью таблиц истинности

В побитовых (bit-wise) операциях значение бита, равное 1, рассматривается как логическая истина, а 0 как ложь. Побитовое И (оператор &) берёт два числа и логически умножает соответствующие биты. Например, если логически умножить 3 на 8, то получим 0

Так как в двоичном виде 3 в виде однобайтного целого представляет собой

Первый бит переменной c равен логическому произведению первого бита числа a и первого бита числа b. И так для каждого бита.

00000011
00001000 ↓↓↓↓↓↓↓↓
00000000

Соответственно, побитовое произведение чисел 31 и 17 даст 17, так как 31 это 00011111 , а 17 это 00010001

00011111
00010001 ↓↓↓↓↓↓↓↓
00010001

Побитовое произведение чисел 35 и 15 равно 3.

00100011
00001111 ↓↓↓↓↓↓↓↓
00000011

Аналогично работает операция побитового ИЛИ (оператор |), за исключением того, что она логически суммирует соответствующие биты чисел без переноса.

выведет 15, так как 15 это 00001111 , а 11 это 00001011

00001111
00001011 ↓↓↓↓↓↓↓↓
00001111

Побитовое ИЛИ для чисел 33 и 11 вернёт 43, так как 33 это 00100001 , а 11 это 00001011

00100001
00001011 ↓↓↓↓↓↓↓↓
00101011

Побитовое отрицание (оператор

) работает не для отдельного бита, а для всего числа целиком. Оператор инверсии меняет ложь на истину, а истину на ложь, для каждого бита. Например,

Выведет -66, так как 65 это 01000001 , а инверсия даст 10111110

что равно -66. Кстати, вот алгоритм для того, чтобы сделать число отрицательным: для нахождение дополнительного кода числа его надо инвертировать и прибавить к нему единицу.

Исключающее ИЛИ (оператор ^) применяет побитово операцию XOR. Например, для чисел

будет выведено 89, так как a равно 00001100 , а b равно 01010101 . В итоге получим 01011001

Иногда логические операторы && и || путают с операторами & и |. Такие ошибки могут существовать в коде достаточно долго, потому что такой код в ряде случаев будет работать. Например, для чисел 1 и 0. Но так как в си истиной является любое ненулевое значение, то побитовое умножение чисел 3 и 4 вернёт 0, хотя логическое умножение должно вернуть истину.

Операции побитового сдвига

О пераций сдвига две – битовый сдвиг влево (оператор >). Битовый сдвиг вправо сдвигает биты числа вправо, дописывая слева нули. Битовый сдвиг влево делает противоположное: сдвигает биты влево, дописывая справа нули. Вышедшие за пределы числа биты отбрасываются.

Читайте также:  Установки высокого давления из китая

Например, сдвиг числа 5 влево на 2 позиции

Сдвиг числа 19 вправо на 3 позиции

Независимо от архитектуры (big-endian, или little-endian, или middle-endian) числа в двоичном виде представляются слева направо, от более значащего бита к менее значащему. Побитовый сдвиг принимает два операнда – число, над которым необходимо произвести сдвиг, и число бит, на которое необходимо произвести сдвиг.

Так как сдвиг вправо (>>) дописывает слева нули, то для целых чисел операция равносильна целочисленному делению пополам, а сдвиг влево умножению на 2. Произвести битовый сдвиг для числа с плавающей точкой без явного приведения типа нельзя. Это вызвано тем, что для си не определено представление числа с плавающей точкой. Однако можно переместить число типа float в int, затем сдвинуть и вернуть обратно

Но мы, конечно же, получим не 5.0f, а совершенно другое число.

Особенностью операторов сдвига является то, что они могут по-разному вести себя с числами со знаком и без знака, в зависимости от компилятора. Действительно, отрицательное число обычно содержит один бит знака. Когда мы будем производить сдвиг влево, он может пропасть, число станет положительным. Однако, компилятор может сделать так, что сдвиг останется знакопостоянным и будет проходить по другим правилам. То же самое и для сдвига вправо.

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

Побитовые операторы и операторы сдвига не изменяют значения числа, возвращая новое. Они также как и арифметические операторы, могут входить в состав сложного присваивания

Примеры

1. Напишем функции, которые позволяют определять и изменять определённый бит числа

Для того, чтобы узнать, какой бит (1 или 0) стоит на позиции n, воспользуемся логическим умножением.

Нужно узнать, выставлен ли бит на позиции 3 (начиная с нуля). Для этого умножим его на число, у которого все биты равны нулю, кроме третьего:

00001001 & 00001000 = 00001000

Теперь узнаем значение бита в позиции 6

00001001 & 01000000 = 00000000

Таким образом, если мы получаем ответ, равный нулю, то на искомой позиции находится ноль, иначе единица. Чтобы получить число, состоящее из нулей с одним битом на нужной позиции, сдвинем 1 на нужное число бит влево.

Заметьте, что в функции условие записано так

Потому что без скобок сначала будет вычислено равенство нулю и только потом выполнено умножение.

Функция, которая выставляет бит на n-й позиции в единицу.

Известно, что логическое сложение любого бита с 1 будет равно 1. Так что для установки n-го бита нужно логически сложить число с таким, у которого все биты, кроме нужного, равны нулю. Как получить такое число, уже рассмотрено.

Функция, которая устанавливает бит на n-й позиции в ноль.

Для этого нужно, чтобы все биты числа, кроме n-го, не изменились. Умножим число на такое, у которого все биты равны единице, кроме бита под номером n. Например

0001011 & 1110111 = 0000011

Чтобы получить такую маску, сначала создадим число с нулями и одной единицей, а потом инвертируем его.

Функция, изменющая значение n-го бита на противоположное.

Для этого воспользуемся функцией исключающего или: применим операцию XOR к числу, которое состоит из одних нулей и одной единицы на месте нужного бита.

Битовые флаги

Расммотрим синтетический пример. Пусть у нас есть три логические переменные, и нам нужно вывести определённое значение в зависимости от всех этих переменных сразу. Очевидно, что может быть 2 3 возможных вариантов. Запишем это условие в виде ветвления:

Мы получили 8 ветвей. Пусть теперь нам понадобилось добавить ещё одно условие. Тогда число ветвей удвоится, и программа станет ещё сложней для понимания и отладки. Перепишем пример.

Если каждое из наших логичесих значений сдвинуть на своё число бит влево и логически сложить, то мы получим свою уникальную комбинацию бит в зависимоти от значений a, b и c:

Используем этот подход к нашей задаче и заменим ветвеление на switch:

Этот метод очень часто используется для назначения опций функций в разных языках программирования. Каждый флаг принимает своё уникальное название, а их совместное значение как логическая сумма всех используемых флагов. Например, библиотека fcntl:

00000000000000000000001000000000

и O_APPEND

источник

Добавить комментарий