Итак, глядя на вашу функцию 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.