0е. Числа для железа: двоичная и шестнадцатеричная запись, биты и байты, знак и как читать 0x40021018
Раздел 0д был про электричество: на проводе либо есть напряжение, либо
нет. Этот раздел — про числа, которые из этого получаются. Он для того,
кто никогда не писал программу для микроконтроллера и видит запись
вроде 0x40021018 впервые.
Здесь нет языка программирования и нет формул. Есть счёт на пальцах,
ряд выключателей и шкаф с ящиками. К концу раздела запись 0x40021018
читается как обычный адрес, а фраза «включить бит 2» понятна без
единой строки кода. Дальше, в маршруте новичка (0а), те же действия
будут записаны на языке C.
Каждое число в разделе посчитано программой-стендом, а адреса и номера битов взяты из заголовочного файла ST для STM32F103. Ответы к упражнениям в 0е.6 проверены тем же стендом.
Опоры: ничего знать заранее не нужно. Полезно прочитать раздел 0д — про напряжение на проводе, — но и без него всё понятно.
Как это называется в документации
Оригинал Перевод bit бит: одна двоичная цифра, 0 или 1 byte байт: восемь бит word, half-word слово (у Cortex-M — 32 бита), полуслово (16 бит) binary, decimal, hexadecimal двоичная, десятичная, шестнадцатеричная запись nibble тетрада: четыре бита, одна шестнадцатеричная цифра LSB, MSB младший бит (справа), старший бит (слева) address адрес: номер ячейки памяти register регистр: ячейка, которая управляет железом mask маска: число, в котором стоят только нужные биты set, clear a bit установить бит (в 1), сбросить бит (в 0) signed, unsigned со знаком, без знака two's complement дополнительный код: как хранят отрицательные числа
0е.1 Выключатель и счёт на пальцах
Люди считают десятками, потому что у них десять пальцев. Каждый палец либо загнут, либо нет. Но мы смотрим не на то, какие пальцы загнуты, а только на то, сколько. Поэтому десятью пальцами можно показать числа от 0 до 10.
Теперь договоримся иначе. Пусть важно, какой именно палец поднят. Правый мизинец значит 1, безымянный — 2, средний — 4, указательный — 8, большой — 16. Каждый следующий палец весит вдвое больше предыдущего. Число — сумма поднятых пальцев. Одной рукой так показываются числа от 0 до 31, а двумя руками — от 0 до 1023, всего 1024 разных числа. Те же десять пальцев, а чисел — больше тысячи.
Это и есть двоичная запись. В ней всего две цифры: 0 (палец загнут) и 1 (палец поднят).
Почему железо считает именно так? Внутри микроконтроллера — миллионы крошечных выключателей. Выключатель знает только два положения: включён или выключен. На проводе либо есть напряжение (у плат справочника это 3,3 вольта), либо его нет (0 вольт). Третьего надёжного состояния нет: «чуть-чуть напряжения» шум легко превратит в «нет» или в «да». Поэтому всё, что хранит и передаёт железо, — длинные ряды нулей и единиц.
Десятичная запись — привычная: десять цифр от 0 до 9, и каждая следующая позиция слева весит в десять раз больше. В числе 352 тройка значит «три сотни», пятёрка — «пять десятков», двойка — «две единицы». В двоичной записи всё так же, только позиция весит не в десять, а в два раза больше соседней справа.
0е.2 Бит, байт, слово
Бит — одна двоичная цифра, один выключатель: 0 или 1. Слово произошло от английского binary digit, «двоичная цифра».
Один бит различает два случая: да или нет. Два бита — уже четыре: 00, 01, 10, 11. Каждый новый бит удваивает число вариантов. Это главное правило всего раздела:
| Сколько бит | Сколько разных чисел | Самое большое число |
|---|---|---|
| 1 | 2 | 1 |
| 4 | 16 | 15 |
| 8 | 256 | 255 |
| 16 | 65 536 | 65 535 |
| 32 | 4 294 967 296 | 4 294 967 295 |
Самое большое число всегда на единицу меньше количества чисел: счёт начинается с нуля. Восемь выключателей дают 256 положений, но если первое из них — «все выключены», то есть 0, последнее — 255.
Байт — восемь бит. Это самая маленькая порция, которую память выдаёт по одному номеру. Один байт хранит число от 0 до 255.
Слово — у микроконтроллеров с ядром Cortex-M, а STM32 как раз такие, это 32 бита, то есть четыре байта. Половина слова, 16 бит, называется полусловом. Каждый регистр, который управляет блоком STM32, занимает место в одно слово: 32 выключателя в ряд, хотя заняты не всегда все.
Биты в ряду нумеруют справа налево, начиная с нуля. Самый правый — бит 0, его называют младшим. У байта самый левый — бит 7, у слова — бит 31; это старший бит. Номер бита совпадает с номером «пальца» из 0е.1: бит 0 весит 1, бит 1 — 2, бит 2 — 4, бит 3 — 8 и так далее, каждый вдвое больше.
Отрицательные числа. В байте 256 комбинаций, и как их понимать —
вопрос договорённости. Можно считать их числами от 0 до 255 — это
числа без знака. А можно отдать половину отрицательным, и тогда
тот же байт хранит числа от −128 до 127 — это числа со знаком.
Отрицательное число получают так: записывают такое же положительное,
переворачивают все биты (0 на 1, 1 на 0) и прибавляют единицу. Для −1:
единица — 0000 0001, перевёрнутая — 1111 1110, плюс один — 1111 1111.
У отрицательных чисел старший бит всегда 1, поэтому его называют знаковым.
Важно одно: сам байт не знает, со знаком он или без. Байт 1100 1000
— это 200, если считать без знака, и −56, если со знаком. Какой смысл
у байта, решает программа. Подробнее — в 3а.3 и 63.14.
0е.3 Перевод туда и обратно
Из двоичной в десятичную. Подписать над каждой цифрой её вес
(справа налево: 1, 2, 4, 8, 16, …) и сложить веса там, где стоит 1.
Для 1101:
| Вес позиции | 8 | 4 | 2 | 1 |
|---|---|---|---|---|
| Цифра | 1 | 1 | 0 | 1 |
| Вклад | 8 | 4 | 0 | 1 |
Сумма: 8 + 4 + 0 + 1 = 13. Значит, 1101 в двоичной — это 13.
Из десятичной в двоичную. Делить число пополам и записывать остаток: 0, если поделилось ровно, 1, если осталась единица. Делить, пока не получится ноль. Потом прочитать остатки снизу вверх. Для 13:
| Число | Половина (целая часть) | Остаток |
|---|---|---|
| 13 | 6 | 1 |
| 6 | 3 | 0 |
| 3 | 1 | 1 |
| 1 | 0 | 1 |
Снизу вверх: 1101. Совпало с тем, с чего начали, — так перевод
проверяют в обе стороны.
Ещё пример. Число 200 в двоичной — 1100 1000: 128 + 64 + 8 = 200.
Вот все числа, которые помещаются в четыре бита, в трёх записях. Эту таблицу полезно один раз переписать от руки — после этого она запоминается сама:
| Десятичная | Двоичная | Шестнадцатеричная |
|---|---|---|
| 0 | 0000 | 0 |
| 1 | 0001 | 1 |
| 2 | 0010 | 2 |
| 3 | 0011 | 3 |
| 4 | 0100 | 4 |
| 5 | 0101 | 5 |
| 6 | 0110 | 6 |
| 7 | 0111 | 7 |
| 8 | 1000 | 8 |
| 9 | 1001 | 9 |
| 10 | 1010 | A |
| 11 | 1011 | B |
| 12 | 1100 | C |
| 13 | 1101 | D |
| 14 | 1110 | E |
| 15 | 1111 | F |
Правый столбец — третья запись, о ней следующий подраздел.
0е.4 Шестнадцатеричная запись: четыре бита — одна цифра
Двоичная запись честная, но длинная. Адрес из названия раздела в двоичной — это 32 цифры:
0100 0000 0000 0010 0001 0000 0001 1000
Прочитать такое без ошибки трудно. В десятичной тот же адрес — 1 073 877 016: коротко, но по этому числу не видно, какие выключатели включены.
Выход — шестнадцатеричная запись. В ней шестнадцать цифр: от 0 до 9 и дальше буквы A, B, C, D, E, F — они значат 10, 11, 12, 13, 14 и 15 (таблица в 0е.3). Почему шестнадцать: ровно столько чисел помещается в четыре бита. Значит, одна шестнадцатеричная цифра — это ровно четыре бита, не больше и не меньше. Четыре бита называют тетрадой.
Перевод получается без всякого счёта, заменой по таблице:
- Двоичное число разбить на четвёрки справа налево.
- Каждую четвёрку заменить одной цифрой из таблицы 0е.3.
0100 0000 0000 0010 0001 0000 0001 1000
4 0 0 2 1 0 1 8 → 0x40021018
Обратно — так же: каждую цифру заменить её четвёркой.
Чтобы шестнадцатеричное число не спутать с десятичным, перед ним пишут
0x. Сами буквы 0x ничего не значат, это только метка: «дальше
шестнадцатеричная запись». Поэтому 10 — это десять, а 0x10 —
шестнадцать. Длинные числа для удобства иногда пишут с пробелом
посередине: 0x4002 1018 — то же самое, что 0x40021018.
Ещё несколько пар для привыкания: 0x2A = 0010 1010 = 42;
0xFF = 1111 1111 = 255 — полный байт, все восемь выключателей
включены; 0xC8 = 1100 1000 = 200 — число из 0е.3.
Слово из 32 бит — ровно восемь шестнадцатеричных цифр, байт — две. Каждая цифра отвечает за свои четыре выключателя (верхний ряд рисунка). Именно поэтому в документации на микроконтроллеры почти всё пишут в шестнадцатеричной записи.
0е.5 Адрес — номер ящика, регистр — ряд выключателей
Адрес. Представьте огромный шкаф с пронумерованными ящиками.
В каждом ящике лежит один байт. Номер ящика — это адрес. У STM32
адрес занимает 32 бита, поэтому ящиков 4 294 967 296: столько разных
чисел помещается в 32 бита (таблица в 0е.2). Самый первый ящик —
номер 0x00000000, самый последний — 0xFFFFFFFF.
Не во всех ящиках лежит память. Шкаф поделён на крупные зоны: в одной —
программа, в другой — оперативная память, в третьей — периферия,
то есть блоки, которые управляют выводами, таймерами, связью. Зона
периферии начинается с ящика 0x40000000. Как поделён весь шкаф,
показано в 1.4.
Регистр. Некоторые ящики — не память, а пульты управления. Если
положить в такой ящик число, где-то в микроконтроллере щёлкнут
выключатели: загорится светодиод, запустится таймер. Такой ящик
называют регистром. Регистр STM32 — одно слово, 32 бита, поэтому он
занимает четыре ящика подряд. Отсюда шаг адресов регистров одного блока —
4: 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18 и так далее.
Как читать 0x40021018. Этот адрес — не случайное число. Он сложен из частей, как почтовый адрес из города, улицы и дома (средний ряд рисунка):
| Часть | Что значит |
|---|---|
0x40000000 |
начало зоны периферии — «город» |
+ 0x20000 |
участок шины AHB — «район» |
+ 0x1000 |
блок тактирования RCC — «дом» |
+ 0x18 |
регистр APB2ENR внутри RCC — «квартира» |
= 0x40021018 |
адрес регистра целиком |
Именно так этот адрес собран в заголовочном файле, который ST поставляет
для STM32F103. Смещение 0x18 — это 24 ящика от начала блока RCC: шесть
регистров по четыре ящика лежат раньше, APB2ENR — седьмой. Следующий
за ним регистр начинается через четыре ящика, с 0x4002101C.
Сложение в шестнадцатеричной записи здесь простое: цифры частей не накладываются друг на друга, каждая стоит на своём месте. Поэтому адрес читается кусками, а его десятичная запись, 1 073 877 016, ничего такого не показывает.
Что делает регистр APB2ENR. Его название — сокращение от «включение тактов шины APB2». Каждый его бит — выключатель такта одного блока. Такт — это частые импульсы, без которых блок микроконтроллера стоит, как часы без батарейки. Пока бит выключен, блок не работает. Бит 2 подаёт такт на порт выводов A, бит 4 — на порт C, бит 0 — на блок AFIO. Это тоже из заголовочного файла ST. Подробно о тактах — раздел 6, а именно этот регистр появится в 6.5.
0е.6 «Включить бит 2» без языка программирования, и упражнения
Пусть в регистре APB2ENR уже включён такт порта C — бит 4. Значит,
в нём записано 0x10. Нижние восемь бит выглядят так: 0001 0000.
Нужно включить ещё и порт A — бит 2. Нижний ряд рисунка показывает
это по шагам.
Шаг 1. Маска. Составить число, в котором включён только нужный бит.
Бит 2 весит 4, поэтому маска — 0000 0100, то есть 0x04.
Шаг 2. Наложить маску по правилу «ИЛИ». Сравнить старое значение и маску бит за битом. Если хотя бы в одном из двух стоит 1, в результат идёт 1. Если в обоих 0 — идёт 0:
было 0001 0000 (0x10)
маска 0000 0100 (0x04)
стало 0001 0100 (0x14)
Бит 2 включился, бит 4 остался как был. Остальные биты не тронуты.
Чего делать нельзя: просто положить в регистр маску 0x04. Тогда
в регистре окажется 0000 0100 — бит 2 включится, а бит 4 выключится,
и порт C перестанет работать. «Записать» число и «дописать» бит —
разные действия.
Выключить бит — наоборот: маску переворачивают (1110 1111 для
бита 4) и накладывают по правилу «И»: 1 в результате только там, где
1 в обоих. Из 0001 0100 получится 0000 0100: порт C выключен,
порт A работает.
Проверить бит — посмотреть на его место. В 0001 0100 бит 2 равен 1,
бит 3 равен 0.
На языке C каждое из этих трёх действий займёт одну короткую строку. Как их пишут, — раздел 3а (3а.1), а почему перед записью нужно прочитать старое значение, — 3а.6.
Упражнения
Решайте на бумаге, с таблицей из 0е.3 перед глазами.
- Переведите
1011из двоичной в десятичную. - Переведите 25 в двоичную делением пополам.
- Сколько это
0x3Cв двоичной и в десятичной? - Запишите
1111 0000в шестнадцатеричной и в десятичной. - Какая маска включает бит 4? Запишите её в шестнадцатеричной и в десятичной.
- В регистре
0x00000014. Каким станет значение, если выключить бит 4? - Сколько разных чисел помещается в 12 бит и какое из них самое большое?
- Байт
0xFEпонимают как число со знаком. Какое это число? - Регистр лежит по адресу
0x40021018. С какого адреса начинается следующий? - Светодиод платы BluePill подключён к выводу 13 порта C. Какая маска у бита 13 в шестнадцатеричной записи?
Ответы:
- 11 (8 + 2 + 1).
11001.0011 1100, 60.0xF0, 240.0x10, 16.0x00000004.- 4096 чисел, самое большое — 4095.
- −2.
0x4002101C.0x2000.
0е.7 Грабли
Считать биты с единицы. Самый правый бит — бит 0, а не бит 1.
«Бит 2» — третий справа, его маска 0x04, а не 0x02. Ошибка на единицу
здесь включает соседний блок вместо нужного.
Читать 0x10 как десять. 0x10 — шестнадцать, 0x100 — 256.
Пока метка 0x не стала привычной, лучше проговаривать: «ноль-икс
один-ноль».
Группировать четвёрки слева. Двоичное число делят на четвёрки
справа налево. 101010 — это 10 1010, то есть 0x2A; если резать
слева, получится 1010 10 и неверный ответ. Слева недостающее
дополняют нулями: 0010 1010.
Положить маску вместо того, чтобы дописать её. Запись 0x04
в регистр, где было 0x10, выключает порт C (0е.6). Регистр, где
каждый бит отвечает за свой блок, меняют через «ИЛИ» и «И», а не
записью одной маски.
Один байт — два числа. 1100 1000 — это 200 без знака и −56
со знаком. Если датчик прислал 200, а на экране −56, ошибка не в датчике:
программа поняла байт как число со знаком (0е.2).
Переводить адрес в десятичную. 1 073 877 016 ничего не говорит.
0x40021018 читается кусками: периферия, шина, блок, регистр (0е.5).
0е.8 Итоги
- Железо считает в двоичной записи: выключатель знает только 0 и 1. Каждый новый бит удваивает число вариантов: 10 бит — 1024 числа, от 0 до 1023.
- Бит — 0 или 1; байт — 8 бит, от 0 до 255; слово у Cortex-M — 32 бита, 4 294 967 296 разных чисел. Биты нумеруют справа, с нуля.
- Одна шестнадцатеричная цифра — ровно четыре бита. Слово — восемь
цифр, байт — две. Метка
0xзначит только «шестнадцатеричная»:0x10= 16. - Адрес — номер ящика с одним байтом. Регистр — ящик-пульт на четыре байта; регистры одного блока идут с шагом 4.
0x40021018=0x40000000+0x20000+0x1000+0x18: периферия, шина AHB, блок RCC, седьмой регистр APB2ENR. В десятичной — 1 073 877 016.- «Включить бит 2» = наложить маску
0x04по «ИЛИ»: из0x10получается0x14. Записать одну маску — значит выключить всё остальное. - Байт со знаком хранит от −128 до 127; −1 — это
1111 1111,0xFF.
Проверь себя
- В регистре
0x10, нужно включить ещё бит 2. Почему нельзя просто записать в регистр0x04? - А)0x04— это маска бита 4, а не бита 2 - Б) В регистр можно писать только десятичные числа - В) Можно: «записать» и «дописать» — одно и то же - Г) Запись маски выключит бит 4, который уже был включён; маску надо наложить по «ИЛИ» - Почему адрес регистра читают в шестнадцатеричной записи, а не в десятичной? - А) Процессор понимает только шестнадцатеричные числа - Б) Одна шестнадцатеричная цифра — ровно четыре бита, и адрес читается кусками: зона, шина, блок, регистр - В) В десятичной записи адрес не помещается в 32 бита - Г) Шестнадцатеричная запись просто короче, другой пользы нет
- Датчик прислал байт, программа показала −56, а ждали 200. Где ошибка? - А) Датчик сломан - Б) Байт не может хранить 200 - В) Программа поняла байт как число со знаком: одни и те же восемь бит — это 200 без знака и −56 со знаком - Г) Биты пронумерованы с единицы, а не с нуля
Проверь себя: ответы
- 1 — Г. Почему: Записать число и дописать бит — разные действия: «ИЛИ» с маской 0x04 даёт 0x14 и не трогает остальные биты (0е.6).
- 2 — Б. Почему: В 0x40021018 видны периферия, шина, блок RCC и смещение регистра, а десятичная запись того же числа ничего такого не показывает (0е.5).
- 3 — В. Почему: Как понимать байт — со знаком или без, — решает программа, а не датчик (0е.7).
Что дальше. Этот раздел — нулевая ступень перед маршрутом новичка 0а «Маршрут новичка: двадцать разделов по порядку». Вот куда в нём ведёт каждая мысль отсюда:
- 1 «Микроконтроллер как таковой» — весь шкаф ящиков сразу: карта памяти (1.4);
- 3 «Язык C для встраиваемых систем» — байт без знака и слово на языке C (3.1), адрес регистра в программе (3.3);
- 3а «Битовые операции» — «ИЛИ», «И» и маски одной строкой (3а.1), почему сначала читают, потом пишут (3а.6), знак при сдвигах (3а.3);
- 6 «Тактирование (RCC)» — тот самый регистр APB2ENR в деле (6.5);
- 8 «Порты ввода-вывода (GPIO)» — регистры порта и светодиод на выводе 13 (8.2, 8.4).
Кому захочется больше про системы счисления — восьмеричная запись, перевод больших чисел и дополнительный код с формулами — раздел 63 «Как текст становится программой; системы счисления» (63.9–63.14).
Дальше: раздел 0ж «Первый вечер: что купить, как поставить среду и помигать светодиодом — пошагово для того, кто ни разу не прошивал» — числа прочитаны, пора к плате: покупки, две программы, четыре провода и первая мигалка за один вечер.