Почему операторы Bitshift? - PullRequest
       3

Почему операторы Bitshift?

5 голосов
/ 03 августа 2010

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

SWITCH_DDR &= ~SWITCH_BIT;   
SWITCH_PORT |= SWITCH_BIT;

Зачем использовать эти операторы?

Или ...

void SerialInit(void)
{
   UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
   UBRRL = (XTAL / (8 * 250000)) - 1;
   UCSRA = (1<<U2X);
   UCSRB = (1<<TXEN);
   UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);
}

Что здесь происходит ??? кто-то, пожалуйста, объясните графически с 0 и 1, пожалуйста. Или вот еще один пример:

ulong MesureLC(void)
{
 int i;

 TCCR1B = 0;
 CountHigh = 0;
 TCNT1 = 0;

 for (i=0;i<25000;i++)
 {
  TCCR1B = (1<<CS12) + (1<<CS11) + (1<<CS10);   // WTF ???
  UDR = 0x55;
  while(!(UCSRA & (1<<UDRE)));
 }
 while(!(UCSRA & (1<<TXC)));
 TCCR1B = 0;

 CountLow = TCNT1;
 Count = CountLow + (CountHigh << 16);

 return Count;
}

Мне нужно понять это. Любая помощь приветствуется.

Ответы [ 8 ]

17 голосов
/ 03 августа 2010

На ум приходит множество причин , почему вы бы их использовали.

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

Это требование также может быть обусловлено тем фактом, что другие устройства или программное обеспечение, с которым вы взаимодействуете, имеют такое же минимальное пространство. Они могут использовать суббайтовые типы данных для выполнения работы, например, заголовки TCP / IP, где поля варьируются от одного бита (например, поле DF) до гораздо большего (например, IP-адреса).

Фактически, второй бит кода, который вы дали, SerialInit, устанавливает свойства для чипа последовательной связи (UART = универсальный асинхронный приемник и передатчик). UCSRx обычно означает UART Control / Status Register #x, поэтому вы записываете информацию на довольно низкоуровневое устройство для управления его поведением.

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

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

Светодиод может иметь такую ​​структуру:

+---+---+---+---+---+---+---+---+
| d | e | g | f | a | b | c | p |
+---+---+---+---+---+---+---+---+
  #   #   #   #   #   #   #   #
  #   #   #   #   #   #   #   #===========#
  #   #   #   #   #   #   #               #
  #   #   #   #   #   #   #===========#   #
  #   #   #   #   #   #               #   #
  #   #   #   #   #   #===========#   #   #
  #   #   #   #   #               #   #   #
  #   #   #   #   #=====#         #   #   #
  #   #   #   #         #         #   #   #
  #   #   #   #     +-------+     #   #   #
  #   #   #   #     |aaaaaaa|     #   #   #
  #   #   #   #   +-+-------+-+   #   #   #
  #   #   #   #   |f|       |b|   #   #   #
  #   #   #   #===|f|       |b|===#   #   #
  #   #   #       |f|       |b|       #   #
  #   #   #       +-+-------+-+       #   #
  #   #   #=========|ggggggg|         #   #
  #   #           +-+-------+-+       #   #
  #   #           |e|       |c|       #   #
  #   #===========|e|       |c|=======#   #
  #               |e|       |c|           #
  #               +-+-------+-+ +---+     #
  #=================|ddddddd|   |ppp|=====#
                    +-------+   +---+

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

Например, включение сегмента g включает в себя следующую операцию:

mmap_byte |= 0x20; // binary 00100000

Отключение включает в себя:

mmap_byte &= 0xdf; // binary 11011111

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


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

  • И равно 1, только если оба его входа равны 1.
  • ИЛИ равно 1, если один или несколько его входов равны 1.
  • XOR равно 1, только если один из его входов равен 1.
  • NOT равен 1, только если его ввод равен 0.
  • Сдвиг влево сдвигает биты влево на определенную величину.
  • сдвиг вправо сдвигает биты вправо на определенную величину.

Если не считать пока изменений, их лучше всего описать как таблицы истинности. Возможности входов находятся сверху и слева, результирующий бит - одно из четырех (два в случае НЕ, поскольку он имеет только один вход) значений, показанных на пересечении двух входов.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

Один пример: если вам нужны только младшие 4 бита целого числа, вы И это с 15 (двоичный код 1111) так:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

Другой пример: если у вас есть два 4-битных значения, которые вы хотите упаковать в 8-битное, вы можете использовать все три своих оператора (left-shift, and и or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • Операция & 15 гарантирует, что оба значения имеют только младшие 4 бита.
  • << 4 - это 4-битный сдвиг влево для перемещения val1 в верхние 4 бита 8-битного значения.
  • | просто объединяет эти два.

Если val1 равно 7, а val2 равно 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100
5 голосов
/ 03 августа 2010
  1. Если у вас есть функция, которую можно включить или отключить, вы можете использовать один бит для установки состояния: 1 или 0. Таким образом, за 1 байт вы можете установить 8 функций.

  2. Если вам нужно сохранить значение (скажем, где-то от 0 до 7), вам нужно только 3 бита (000 в двоичном = 0 в декабре, 001 = 1, 010 = 2, ..., 111 = 7) И у вас все еще есть 5 из 8 свободных битов.

Что здесь происходит ??? кто-то, пожалуйста, объясните графически с 0 и 1

1 << 2 = 00000001 << 2 = 00000100 = 4 </p>

4 >> 2 = 00000100 >> 2 = 00000001 = 1

4 голосов
/ 03 августа 2010

Я думаю, что настоящий вопрос не в том, «что, черт возьми, это значит», а в том, «почему, черт возьми, разработчикам, пишущим программы для микроконтроллеров, кажется, нравится этот путь?»

Ну, я не знаю, любят ли они так. Я вообще не разработчик, и мне просто немного интересны микроконтроллеры, но вот что я думаю.

Вы когда-нибудь видели документацию на чип? Вот пример из некоторого Atmel pdf.

Регистр управления и состояния USART - UCSRnB

• Бит 7 - RXCIEn: RX завершен Interrupt Enable n Запись этого бита в один включает прерывание на RXCn Флаг. USART получен завершенным прерывание будет сгенерировано, только если бит RXCIEn записывается в один, Глобальный флаг прерывания в SREG записано в один и бит RXCn в UCSRnA установлен.

• Бит 6 - TXCIEn: передача завершена Interrupt Enable n Запись этого бита в один включает прерывание на TXCn Флаг. Передача USART завершена прерывание будет сгенерировано, только если бит TXCIEn записывается в один, Глобальный флаг прерывания в SREG записано в один и бит TXCn в UCSRnA установлен.

... • Бит 5 - UDRIEn: ... • Бит 4 - RXENn: ... • Бит 3 - TXENn: ... • Бит 2 - UCSZn2: ... • Бит 1 - RXB8n: ... • Бит 0 - TXB8n: ...

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

Теперь предположим, что нужно включить прерывание на флаге RXCn и оставить все остальные настройки без изменений. Это требует установки одного бита в одном конкретном регистре, оставляя другие биты без изменений. Оператор | = это самый простой способ сделать это

Если предположить, что адрес регистра равен 0x3F (я набрал номер, это не имеет значения), можно написать так:

*((unsigned char*)0x3F) |= 0x80;// I want to set bit number 7 (RXCIEn)

Теперь, насколько это читабельно?

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

UCSRnB |= (1 << RXCIEn);

Благодаря документации что-то вроде

UCSRnB = (1<<RXENn)|(1<<TXENn);

считается более читабельным, чем

*((unsigned char*)0x3F) = 0x18; // I want to set bits number 4 and 3 (RXENn and TXENn)

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

3 голосов
/ 03 августа 2010

В первом примере вы дали:

SWITCH_DDR &= ~SWITCH_BIT;   
SWITCH_PORT |= SWITCH_BIT;

Это не битовый сдвиг, а битовая маска.

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

Допустим, SWITCH_DDR и SWITCH_PORT - это специальные значения памяти, которые управляют поведением какого-либо устройства. Каждый из его битов включает / выключает функцию. Если вы хотите управлять данной функцией по отдельности, вы должны иметь возможность немного изменить ее, не мешая другим. Давайте также скажем, что функция, управляемая SWITCH_BIT, является самым левым битом в байте. Таким образом, SWITCH_BIT будет иметь значение 0x80 (10000000 в двоичном виде). Когда вы делаете первое утверждение, вы инвертируете SWITCH_BIT с оператором ~ (получая 01111111 в двоичном виде) и применяете двоичное И к SWITCH_DDR. Это эффективно очищает крайний левый бит и оставляет остальные без изменений. Второе утверждение делает двоичное ИЛИ, поэтому результат противоположен.

Теперь, что касается операций сдвига, у них много приложений (большая часть уже была упомянута в других ответах), я просто объясню конкретное использование в опубликованном вами коде. Итак, у вас есть:

void SerialInit(void)
{
   UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
   UBRRL = (XTAL / (8 * 250000)) - 1;
   UCSRA = (1<<U2X);
   UCSRB = (1<<TXEN);
   UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);
}

Здесь все аналогично предыдущему примеру (установка определенных битов в значении), но есть несколько отличий из-за ситуации:

a) Хотя в первом примере вы уже создали битовую маску (SWITCH_BIT), здесь константы - это просто ПОЗИЦИЯ бита. Например, U2X содержит положение бита, который должен быть включен / выключен (справа налево), а не его битовую маску. Выполнение (1<<U2X) эффективно создает соответствующую битовую маску. Если бит, который необходимо включить, является самым левым (как я сделал в примере SWITCH_BIT), U2X будет равен 7, а результат сдвига будет таким же, как 10000000 в двоичном виде.

б) В последней строке:

UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);

Это объединение битовых масок, создаваемых каждой константой (значения которой, опять же, являются битовыми позициями справа). Дело в том, что программист решил использовать оператор + вместо двоичного ИЛИ, потому что он / она знал, что, когда у вас нет «сталкивающихся» битов, их результат одинаков. Лично я всегда использовал бы двоичное ИЛИ, чтобы было ясно, что то, что я делаю, не является арифметическим сложением.

в) Наконец, о первой строке

UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;

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

2 голосов
/ 03 августа 2010

Итак, глядя на вашу функцию SerialInit.

Вы оставили здесь много деталей, но все еще достаточно, чтобы пройти мимо. Ясно, что вы инициализируете последовательный порт, а первые две строки - это делитель тактовых импульсов. Поскольку микросхемы могут работать на разных тактовых частотах, а микросхема обычно не пытается угадать, на каких тактовых частотах вы хотите сказать 9600 бод, вам необходимо выполнить математические расчеты для деления, скажем, на эталонные тактовые частоты 12 МГц. Поэтому XTAL / (8 * 250000) пытается увеличить частоту 16 МГц до 250000 с 8-кратной передискретизацией. Я думаю, что это число 0x200, но для примера скажем, что это число 0x234. Регистр UBBRH выглядит как верхняя половина регистра, поэтому ему нужны старшие биты, по-видимому, старшие 8 бит (или меньше), а UBBRL - младшая половина этого делителя, поэтому ему нужны младшие 8 бит.

Старшие 8 битов 0x0234 равны 0x02, чтобы получить 0x02 от 0x0234, вам нужно сдвинуть

0x0234 = 0b0000001000110100 (0b обозначает двоичный файл, где 0x обозначает шестнадцатеричный код)

Это не вращение, это логический сдвиг, язык С не имеет поворота (и не арифметический сдвиг). Таким образом, это означает, что число битов, которые мы сдвигаем вправо, заканчивается в битовой корзине.

, поэтому 0b0000001000110100, сдвинутый вправо 8 раз, становится 0bxxxxxxxx00000010. Xx - это новые биты, сдвинутые в и в C, которые фактически равны нулю. Но это не имеет значения, потому что регистр, в который выполняется запись, равен 8 битам, поэтому учитываются только младшие 8 битов.

Итак, вторая запись нашего 0x234 в UBRRL - это младшие 8 бит. Компилятор C собирается отрубить младшие 8 бит 0x34 и записать это в регистр.

Таким образом, между этими двумя строками кода мы вычислили делитель для последовательных часов 0x234 и записали 0x02 в верхний регистр делителя и 0x34 в нижний.

Следующая строка UCSRA = (1 << U2X); </p>

Очевидно, есть регистр, который хочет установить один бит. Я не знаю, что такое U2X, но допустим, что это 5 1 << 5 означает взять 0x01 или 0b00000001. сдвиг влево на пять означает сдвиг, сдвиньте биты, которые находятся там, влево, а в C введите 5 нулей справа. Биты в верхней части переменной, самые левые 5 битов выпадают в область битов. </p>

So 
0b00000001
add five zeros to visualize
0b0000000100000
then chop five off the left
0b00100000
and we end up with 0x20.

Строка UCSRB работает так же.

Строка UCSRC такая же, но задаются три бита

UCSRC = (1 << URSEL) + (1 << UCSZ1) + (1 << UCSZ0); </p>

Для примера позвольте мне составить некоторые цифры, чтобы заполнить те, которые не определены в вашем примере.

UCSRC = (1 << 5) + (1 << 2) + (1 << 4); </p>

Как мы делали с UCSRA и UCSRB, визуализируйте эти числа

one with five zeros shifted in
0b100000
Pad that on the left to make it a full 8 bits
0b00100000
one with 2 zeros and one with 4 zeros
0b100 padded is 0b00000100
0b10000 padded is 0b00010000

Итак, три составляющих:

0b00100000
0b00000100
0b00010000

и при сложении становятся

0b00110100 = 0x34

И это значение записывается в регистр.

Теперь вы должны быть осторожны, используя add вместо or. Если вы не будете осторожны с вашими определениями или не потрудитесь посмотреть, вы можете обнаружить, что один и тот же бит определен с двумя именами, и вы можете почувствовать, что вам нужны обе функции, не зная, что это один и тот же бит, добавление испортит это, или привычка. Например, если URSEL и UCSZ1 оказались одним и тем же битом

UCSRC = (1 << URSEL) + (1 << UCSZ1) + (1 << UCSZ0); </p>

UCSRC = (1 << 5) + (1 << 5) + (1 << 4); </p>

you would get
0b00100000
0b00100000
0b00010000
which adds to
0b01010000
when you probably wanted to or them and get
0b00110000

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

Обычно ПОЧЕМУ вы выполняете эту форму сдвига битов, в частности, с микро- и драйверами, в том, что регистр в устройстве может определять более чем одну вещь. Последовательный порт - отличный пример, скажем, у вас был управляющий регистр, который выглядел так

0b0SPPLLLL

где SS - стоповые биты 1 = 2 стоповых бита 0 = 1 стоповый бит PP равен четности 0b00 = нет четности 0b01 = четный 0b10 = нечетный LLLL имеет длину 0b1000 = 8, 0b0111 = 7 бит и т. Д.

Очень часто вы найдете код для такого регистра, который делает что-то вроде:

SCONTROL = (0 << 7) | (2 << 4) | (8 << 0); </p>

За исключением того, что жестко закодированные числа заменяются на определения:

SCONTROL = (ONESTOPBIT << STOPBITS) | (NOPARITY << PARITYBITS) | (DATABITS8 << DATABITS); </p>

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

И с не является простым способом не иметь дело с переменной длиной

SWITCH_DDR & = ~ SWITCH_BIT;

SWITCH_PORT | = SWITCH_BIT;

Так что, если вы хотите что-то прочитать-изменить-записать и сказать, что младшие 3 бита - это то, что вы хотите изменить, и не хотите связываться с другими битами в регистре, вы можете сделать что-то вроде этого:

ra = SOMEREGISTER;
ra&=~7;
ra|=newnumber&7;
SOMEREGISTER = ra;

Ра & = ~ 7 означает

start with 
0x0000....0000111
take the ones complement, which is make the zeros ones and ones zeros
0x1111....1111000

и то, что с ra было в нем до и с 1, означает, что вы сохраняете свое значение, а с нулем превращаете его в ноль, поэтому младшие три бита были принудительно установлены в 0, остальные биты не заботятся о том, сколько неизменных , затем вы или новый номер, чтобы установить младшие три бита на то, что вы хотите изменить, и затем записать обратно в регистр измененное значение.

Тогда и с перевернутой маской все может быть проще, чем делать математику самостоятельно, ~ 7 вместо 0xFFFF ... FF8. Как это помогает, вам может понадобиться только одно определение:

#define SOMEMASK 0x7

and then use 
ra&=~SOMEMASK;
ra|=newvalue&SOMEMASK.

Вы можете стать еще умнее и сказать

#define SOMEMASKBITS 3 #define SOMEMASK ((1

И вам даже не нужно думать, что 0x7 - это три. Вы просто положили 3.

1 голос
/ 03 августа 2010

Битовые операторы работают (как следует из их названия в битах).В первом фрагменте кода, который вы дали, эти операторы используются для установки и сброса определенного бита.Для простоты предположим, что SWITCH_DDR является 8-битным целым числом и что SWITCH_BIT также является 8-битным целым числом, которое имеет постоянное значение 2:

SWITCH_DDR = 00000000;  // initial value of SWITCH_DDR is 0
SWITCH_BIT = 00000010;

Затем вы можете использовать побитовое ИЛИ для установки определенного бита SWITCH_DDR в 1:

SWITCH_DDR |= SWITCH_BIT; // SWITCH_DDR is 00000010 now

Чтобы проверить, установлен ли бит SWITCH_BIT, вы используете оператор AND:

TEMP = 10101010 & SWITCH_BIT; // TEMP is 00000010 now (1 in TEMP is set only if there's 1 in both operands)
if (TEMP == SWITCH_BIT) // The condition is true
{ /* Do something */ }
TEMP = SWITCH_DDR & SWITCH_BIT;  // TEMP is again 00000010 because we set it to 00000010 before and the AND operator doesn't therefore change anything
if (TEMP == SWITCH_BIT)  // The condition is also true
{ /* Do something */ }

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

TEMP = ~SWITCH_BIT;  // TEMP is now 11111101
SWITCH_DDR &= TEMP;  // This preserves everything (because 1 & 1 = 1 and 1 & 0 = 0) but the SWITCH_BIT bit which will be always set to 0 (anything & 0 = 0)

Операторы сдвига просто сдвигают биты влево или вправо:

RESULT = 10010010 << 1;  // RESULT is 00100100
RESULT <<= 1;  // RESULT is 01001000
RESULT <<= 2;  // RESULT is 00100000
RESULT >>= 1;  // RESULT is 00010000

Существует одна особенность операторов сдвига - вы можете использовать их для быстрого деления / умножения со степенями 2:

RESULT = 3 << 1;  // Result is 6 (3 * 2)
RESULT = 5 << 2;  // Result is 20 (5 * 4)
RESULT = 1 << 7;  // Result is 128 (1 * 128)
RESULT = 36 >> 1; // Result is 18 (36 / 2)
RESULT = 35 >> 1; // Result is 17 (35 / 2)
1 голос
/ 03 августа 2010

Оператор сдвига битов представлен в двух основных вариантах (и я не говорю о направлении): сдвиг и поворот.

Кроме того, оба имеют два направления, поэтому обычно у вас четыре:

  • сдвиг влево
  • сдвиг вправо
  • поворот влево
  • поворот вправо

Первые две сменыбиты количество бит в одном направлении.Любые биты, которые «падают» в конце, исчезают.Любые биты, которые «появляются» на другом конце, равны нулю.

Как правило, вы также указываете, сколько битов для сдвига значения.

Итак:

1000 shl 1 = 0000 (the 1 fell off the end, and a 0 appeared on the other end)
1000 shr 1 = 0100 (a zero fell off the right end)

Вращениене теряет выпадающие биты, вместо этого они поворачиваются обратно на другую сторону.

1000 rol 1 = 0001 (the 1 was rotated back in on the other side)

Вы можете думать о двух операциях как:

  • Для сдвига,число содержит бесконечное число нулей на обоих концах, которое следует за значением при его сдвиге
  • Для вращения число повторяется бесконечным числом в обоих направлениях, которое следует за значением при его сдвиге

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

Если флаг переноса начинается с 0, вот что происходит:

1000 rcl 1 = 0000 (rcl = rotate through carry to left)
0000 rcl 1 = 0001 (now the 1 came back, it was temporarily stored in carry flag)

Последний в машинном коде может использоваться для перемещения отдельных битов из одного регистра в другой:

rcl ax, 1    ; rotate AX-register, 16-bit, left 1 bit, through carry
rcr bx, 1    ; rotate BX-register, 16-bit, right 1 bit, through carry

Здесь мы временно берем самый левый бит из AXповерните его в флаг переноса изатем поверните его обратно в крайний левый бит BX.

Теперь вы обычно можете комбинировать сдвиг с другими побитовыми операторами.Например, чтобы установить бит N значения (где N равен 0, а бит 0 - самый правый), вы можете сделать это:

value = value OR (1 shl N)

Здесь мы сначала сместим значение 1 N раз влевый.Если N равно 0, это вообще не сдвигает бит.

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

Так что для сдвига:

1 shl 0 = 00000001 shl 0 = 00000001
1 shl 1 = 00000001 shl 0 = 00000010
1 shl 2 = 00000001 shl 0 = 00000100
1 shl 3 = 00000001 shl 0 = 00001000
1 shl 4 = 00000001 shl 0 = 00010000
1 shl 5 = 00000001 shl 0 = 00100000
1 shl 6 = 00000001 shl 0 = 01000000
1 shl 7 = 00000001 shl 0 = 10000000

Тогда ИЛИ:

???????? OR 00100000 = ??1?????, where ? means whatever it was before

Позвольте мне взять пару строк кода, которые вы разместили:

UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
UBRRL = (XTAL / (8 * 250000)) - 1;

Первый выполняет вычисления, (XTAL / (8 * 250000)) - 1 что я не знаю, цель позади.Однако это нормальная математика, поэтому она что-то вычисляет.Давайте назовем это частотой (судя по комментарию.)

Это значение рассчитывается дважды, поэтому давайте перепишем приведенные выше утверждения:

UBRRH = value >>8;   // 250kbps at 16Mhz
UBRRL = value;

Здесь я должен угадать, но япредполагая, что UBRRH и UBRRL объявлены как имеющие тип «BYTE», это означает, что они могут хранить максимум 8 битов значения каждый.Это означает, что код на самом деле выглядит следующим образом:

  1. UBRRH берет старшие 8 битов «значения», сдвигает их в младшие 8 бит и сохраняет их.Так как он хранит только байт, он отсекает остальные, что означает, что он захватывает биты 8-15
  2. UBRRL берет младшие 8 битов и отбирает остальные, что означает, что он захватывает биты 0-7

Поскольку имена двух заканчиваются на L и H, они соответствуют предположению.

0 голосов
/ 03 августа 2010

Стоит отметить, что на многих небольших процессорах есть специальные инструкции для установки и очистки отдельных битов порта ввода / вывода.Я бы посоветовал вам узнать кое-что о любом процессоре, который вы будете использовать, потому что оптимальные стили кодирования варьируются от одного к другому.Например, рассмотрим группы операторов:

  some_port |= 8;  /* Statement #1a -- Note value is a power of 2 */
  some_port |= 2;  /* Statement #1b -- Note value is a power of 2 */

  some_port |= 9;  /* Statement #2 -- Note value is not a power of 2 */

На некоторых процессорах (например, ARM или Z80), если some_port объявлен как volatile (что и должно быть), каждый оператор будет представлять собой три инструкции, поэтому первыйпоследовательность займет в два раза больше времени и места первого.В некоторых других (например, PIC) первые два оператора будут представлять собой одну инструкцию, а вторая - две инструкции, поэтому обе последовательности будут занимать одинаковое время и пространство.Третьи предложат преимущество в пространстве для одного и преимущество во времени для другого.

Time/space for 1st and 2nd methods of setting bits
(time in cycles unless noted)
1st  2nd
2/4  2/3  8051 (standard)
4/4  3/3  8051 (accelerated clone)
2/2  2/2  PIC
10/4 8/6  6805
...