Руководство 03: Жонглируем битами

Сейчас когда мы рассмотрели регистры, которые как выяснилось щёлкают переключателями(при изменение битов). Мы попробуем пощёлкать ими, для этого потребуется: G2001 со светодиодом от P1.4 к земле. Если на P1.4 нет напряжения (Vss), то ясное дело что ничего не случится, но если же на P1.4 есть напряжение (Vcc), тогда светодиод загорится. Так как мы подключились к P1.4, следовательно мы можем его контролировать измененяя 4 бит в регистрах P1. (Не забывайте что в программировании счёт начинается с 0, следовательно это будет не четвёртый бит а пятый, но кто их считает..?)

Прежде чем смотреть значения битов, мы должны рассмотреть способы представления информации в 8-битных регистрах. Обычно мы используем числа в десятичной системе, но к сожалению она не кратна 8-битным цифрам в следствии чего будет происходить путаница (десятичная система по основанию равна 10).

Проще всего представить значения регистров в двоичной системе(по основанию 2). Таким образом регистр будет имеет 8 цифр, каждая из которых 0 или 1, соответствующая значению определённого бита. Что бы отличать двоичную от десятичной обычно указывают числа с префиксом 0b, например 0b10.

Другая распространенная система — шестнадцатеричная(по основанию 16). Такая запись удобна тем что представляет любое 8-битное значение двумя цифрами, в ней используются значения от 0 до 9 и буквы от a до f. Префикс шестнадцатеричной 0x, так число 2 в шестнадцатеричной системе будет выглядеть как 0x02. Число 12 представляется как 0x0c, так как латинские буквы от a до f соответствуют числам от 10 до 15. 16-битные числа представляются четырьмя знаками, например 0x14da. Большинство значений в MSP430 16-битные, что и делает удобным использование шестнадцатеричной системы. (Прошу прощение за небольшую поправку, в документации TI используется обозначение 14DAh для шестнадцатеричных значений, но здесь мы будем использовать 0x14da. Префикс 0x используется в программировании, и я буду использовать это обозначение когда не ссылаюсь на документацию от TI где используется обозначение h.)

Ещё мы будем использовать восьмеричную систему(по основанию 8), с префиксом 0o. Но на практике я её не встречал.

Разумеется что в двоичной системе видно какие биты мы используем и что они изменяют, к сожалению, для написания в двоичной системе потребуется больше символов чем в любой другой. Когда мы пишем программу на C для MSP430, мы можем подключить заголовочный файл с краткой спецификацией используемого микроконтроллера (например
#include<msp430g2001.h>;
). Этот заголовочный файл содержит ярлыки (краткие обозначения) для двоичных значений где каждая цифра, кроме одной, равна 0. Заголовочный файл позволяет нам использовать BIT2 вместо 0b00000100. Существуют ярлыки от BIT0 до BIT7 что и позволяет нам меньше печатать. Но далее, я всё же попробую использовать полное восьмизначное значение для упрощения понимания происходящего. Пока, это не важно!

Для примера с нашими светодиодами, нам понадобится сначала сделать P1.4 на выход, что бы мы могли контролировать напряжение на светодиодах. Для этого нужно установить в 1 четвёртый бит в регистре P1DIR.

1.Явное присваивание

Если мы знаем какое двоичное (или шестнадцатеричное, или даже десятичное) значение для соответствующей конфигурации нам нужно установить, тогда мы можем сделать это прямо: P1DIR = 0b00010000, для установки всех битов P1DIR в 0 кроме бита 4. Этот метод хорошо работает при инициализации процессора, но позже в программе могут появиться проблемы, когда мы не знаем в каком состоянии находятся соседние биты и не хотим их изменять. Для выключения светодиода потребуется только установить значение бита 4 в 0: P1DIR = 0b00000000, в данном случае можно просто написать P1DIR = 0. (Используя BITx, мы можем написать P1DIR = BIT4.)

2.Сложение и Вычитание.

Если мы не знаем какое значение необходимо для конфигурации, или же нам нужно добавить значение только одного бита в P1DIR, тогда можно использовать двоеточие(:) для обозначения неизвестных цифр: 0b::: 0:::: + 0b::: 1:::: = 0b::: 1::::, так что мы можем переключить бит 4 написав P1DIR += 0b00010000. *(Смущает +=? Смотрите сноску ниже!) Проблемы с этим методом начнутся когда мы случайно попытаемся дважды включить: 0b::: 1:::: + 0b00010000 это вызовет перенос на следующий бит, что повлечёт изменение направления ввода/вывода где нам не нужно! Этот метод следует применять с осторожностью. Обратите внимание что при выключении следует отнимание значения. (С использованием BITx: P1DIR += BIT4 или P1DIR -= BIT4.)

3.Логические операции & и |

Во избежании проблем связанных со сложением и вычитанием, мы можем использовать логические операции. Допустим мы имеем бит x. (x может быть 0 или 1, мы смотрим на него в общем.) Операторы и(&) и или(|) могут использоваться для изменения значения будь оно 1 или 0. x & 0 = 0 не зависимо от значения x, точно также, как: x | 1 = 1. Кроме того, x & 1 = x и x | 0 = x, в этом случае значение x не изменяется. Так какой результат будет в операции P1DIR | 0b00010000? Биты от 0 до 3 и от 5 до 7 не изменятся (помните, | 0 не изменит значение), а бит 4 будет установлен в 1. (Возможно он был 1 и раньше, но уж точно после операции!) В другом случае: P1DIR &= 0b11101111 — бит 4 установится в 0, оставив неизменным все остальные. Запись 0b11101111 является итогом применения оператора не(~) к 0b00010000, это можно было записать как P1DIR & = ~ 0b00010000. (Используя BITx будет: P1DIR |= BIT4 включит бит 4, и P1DIR &= ~BIT4 выключит его.)

Иногда вы будете встречать записи: a+b и a|b — запомните они равны по значению, таким образом вы можете использовать запись любого вида.**

4.Исключающее или ^.

Этот вариант изменения схож с третьим, но он эффективнее когда нам нужно переключить значение, не устанавливая его специально в 0 или 1. Этот оператор (^) воздействуя на x^0 = 1 при x равном 1, и 0 при x = 1. x^1 = 1 если x = 0, и 0 при x = 1(0 получается при совпадении значений, а 1 при отличии). Таким образом использование x^=0 оставит x без изменений, а x^=1 изменит значение x в противоположную сторону. P1DIR ^= 0b00010000 переключит бит 4 (в 1 если он 0 или в 0 если он 1) точно также как переключения переключатель. (Используя BITx будет: P1DIR ^= BIT4.)

Сейчас, в нашей программе нам нужно назначить контакт на выход:
P1DIR = BIT4;

(Эквивалентом будет запись с логическим оператором: P1DIR |= BIT4;)
Значение P1OUT может быть 0 или 1, если мы проконтролировать момент зажигания светодиода, тогда нужно записать следующее:
P1OUT = 0;

(Лучше инициализировать состоянии вывода до установки состояние конкретного контакта.)
Сейчас мы имеем вывод установленный в 0, следовательно светодиод выключен. Для включения его мы должны дать команду:
P1OUT |= BIT4;

Для выключения, пишем:
P1OUT &= ~BIT4;

Для переключения состояния(выключить если включён, включить если выключен):
P1OUT ^= BIT4;


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

Сначала, давайте инициализируем два контакта на выход изначально выключенными:
P1OUT = 0;
P1DIR = BIT4 + BIT6;

(добавляем оба значения вместе, проще понять в 0b01010000).
Мы можем по отдельности выключать и включать каждый контакт:
P1OUT |= BIT4;		// P1.4 включить
P1OUT &= ~BIT4;		// P1.4 выключить
P1OUT |= BIT6;		// P1.6 включить
P1OUT &= ~BIT6;		// P1.6 выключить

Или же переключать их вместе:
P1OUT |= BIT4 + BIT6;		//  оба включить
P1OUT &= ~(BIT4 + BIT6);	// оба выключить

И наконец, переключить их вместе:
P1OUT ^= BIT4 + BIT6;		// переключить вместе


Упражнение читателю: Напишите требуемый код для использования светодиодов на контактах 3 и 7 первого порта, где контакт 3 изначально выключен, а 7 включен. Далее переключите светодиоды так что бы 3 включён, а 7 выключен.

* += сокращённые операторы в C. Если говорится x += 1, следует понимать что эта команда прибавляет 1 к x и присваивает полученное значение x. Другими словами, это значит то же самое что: x = x + 1. Хотя в алгебре такое не имело бы значение, но в программировании такая запись очень распространена. Не думайте что = означает равенство обоих сторон, читается это как: x сейчас равен значению в переменной x плюс 1.

** Если a и b не содержат установленных бит на совпадающих местах, то операции действительно эквивалентны. Чуть хуже, если попытаться таки собразом установить бит, который уже установлен. Поэтому для битовых масок рекомендуется использовать исключительно или.

Перевод Tutorial 03: Flipping Bits
  • -1
  • 11 октября 2011, 00:02
  • Akay

Комментарии (17)

RSS свернуть / развернуть
Ты все запутанно расписал.
Есть wasm.ru где операции над битами расписаны на пальцах.
Также есть книга «Код» Петцольд. Там тоже все на пальцах только еще и обьяснение откуда биты взялись.
0
  • avatar
  • a9d
  • 11 октября 2011, 00:18
Не думайте что = означает равенство обоих сторон, читается это как: x сейчас равен значению в переменной x плюс 1.
Это называется присвоение. Математики правда его обозначают стрелочкой. Вероятно поэтому в паскале знак "=" используется как логический оператор «равно», а для присвоения выделен отдельный знак (хотя и не стрелочка, которой на клаве тогда не было).

Алсо перевод по прежнему китайский. Такое впечатление, что ты просто сам не понимаешь, что переводишь. Чтобы понять, что в статье написано — нужно самому эту тему знать в объеме большем, чем в ней же и рассматривается. А это делает статью малополезной.
0
  • avatar
  • Vga
  • 11 октября 2011, 00:30
Забыли упомянуть, что ^ — исключающее или (exclusive or).
На мой взгляд, такие таблички нагляднее (понимаю, что это перевод):
x y  z
0 0  0
0 1  1
1 0  1
1 1  0
0
  • avatar
  • John
  • 11 октября 2011, 00:32
Дело в том, что подразумевается, что если ты сюда сунулся — то С (немного) понимаешь. А тутор рассматривает только типичную технику работы с битами в нем.
Алсо в оригинале оператор таки назван — «сделать это можно оператором исключающее или (^)».

Хотя со сноской про х, которое теперь равно х, но на единицу больше, это не очень вяжется.
0
Простите, а что за IDE вы используете для MSP430? Насколько я знаю, в IAR нет возможности использовать запись вроде 0bxxxxxxx — компилятор ругается.
0
CCS тоже ругается, я как понял автор хотел нагляднее показать изменения в битах
0
0
… там можно записывать бинарные константы, если поставить галочку в настройках
0
Ви таки путаете IDE и компилятоr. Вообще надо смотреть у первоисточника, но из компилеров есть еще например MSPGCC.
0
Каюсь, грешен. Не судите строго, матчасть знаю не очень хорошо. Но факт остается фактом: в IAR не получается использовать запись вида 0b. Хотя при помощи BIT0 — BIT7 наверное даже нагляднее.
0
«Да ты шо! Ты бач, яки пидлюки...» :)

В асме вот так B'01010101. Смотрим внимательно — второго апострофа нет ;)

В Си — увы, но можно использовать дефайны, если не в лом рисовать хотя бы 256 штук комбинаций. Можно комбинацией битовых операций, вроде ((1<<7)|(1<<2)).
0
Иногда вы будете встречать записи: a+b и a|b — запомните они равны по значению, таким образом вы можете использовать запись любого вида.
Не путайте людей! В языке С (как и С++, С# и т. д.) a+b это арифметическое сложение, a|b – побитовое логическое «или»
Почувствуйте разницу:
1 + 1 = 2
1 | 1 = 1
0
  • avatar
  • e_mc2
  • 11 октября 2011, 22:50
Если Вы подразумеваете терминологию Булевой алгебры — то там допустимо обозначать «или» как «логическую сумму» А + В = А V В. Но тогда лучше явно об этом написать в статье.
0
Если a и b не содержат установленных бит на совпадающих местах, то операции действительно эквивалентны. Чуть хуже, если попытаться таки собразом установить бит, который уже установлен. Поэтому для битовых масок рекомендуется использовать исключительно or.
0
Ну вот так вот думаю понятнее
0
«Чуть хуже...» :)
0
сижу вот курю МСП… с выходом грейс вообще лафа но… то инит конфиги… я его юзаю когда недопонимаю немного какие биты куда рассовать чтоб так как нужно мне запустить таймер :)

другой вопрос… в АВРах я привык к записям типа
PORTC.1=a;
где а это переменная… у МСП есть в системе команд установить и слить бит… а вот как в кодкомпозер студио это провернуть? или например в иаре?
был бы очень признателен ибо никогда не понимал где эти особенности компиллера искать в хелпах
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.