Embedded HandbookПриложения
Все разделы › 0. О справочнике
STM32F103 · уровень базовый · 15 мин чтения
Открыть в интерактивном справочнике

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). Почему шестнадцать: ровно столько чисел помещается в четыре бита. Значит, одна шестнадцатеричная цифра — это ровно четыре бита, не больше и не меньше. Четыре бита называют тетрадой.

Перевод получается без всякого счёта, заменой по таблице:

  1. Двоичное число разбить на четвёрки справа налево.
  2. Каждую четвёрку заменить одной цифрой из таблицы 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.

Адрес 0x40021018: восемь шестнадцатеричных цифр, под каждой её четыре бита, всего тридцать два выключателя; адрес сложен из начала периферии 0x4000 0000, шины AHB 0x2 0000, блока тактирования RCC 0x1000 и смещения 0x18 седьмого регистра APB2ENR; ниже нижние восемь бит этого регистра — было 0x10, после включения бита 2 стало 0x14, а запись одной маски 0x04 выключила бы порт C

Слово из 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 перед глазами.

  1. Переведите 1011 из двоичной в десятичную.
  2. Переведите 25 в двоичную делением пополам.
  3. Сколько это 0x3C в двоичной и в десятичной?
  4. Запишите 1111 0000 в шестнадцатеричной и в десятичной.
  5. Какая маска включает бит 4? Запишите её в шестнадцатеричной и в десятичной.
  6. В регистре 0x00000014. Каким станет значение, если выключить бит 4?
  7. Сколько разных чисел помещается в 12 бит и какое из них самое большое?
  8. Байт 0xFE понимают как число со знаком. Какое это число?
  9. Регистр лежит по адресу 0x40021018. С какого адреса начинается следующий?
  10. Светодиод платы BluePill подключён к выводу 13 порта C. Какая маска у бита 13 в шестнадцатеричной записи?

Ответы:

  1. 11 (8 + 2 + 1).
  2. 11001.
  3. 0011 1100, 60.
  4. 0xF0, 240.
  5. 0x10, 16.
  6. 0x00000004.
  7. 4096 чисел, самое большое — 4095.
  8. −2.
  9. 0x4002101C.
  10. 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 Итоги

Проверь себя

  1. В регистре 0x10, нужно включить ещё бит 2. Почему нельзя просто записать в регистр 0x04? - А) 0x04 — это маска бита 4, а не бита 2 - Б) В регистр можно писать только десятичные числа - В) Можно: «записать» и «дописать» — одно и то же - Г) Запись маски выключит бит 4, который уже был включён; маску надо наложить по «ИЛИ»
  2. Почему адрес регистра читают в шестнадцатеричной записи, а не в десятичной? - А) Процессор понимает только шестнадцатеричные числа - Б) Одна шестнадцатеричная цифра — ровно четыре бита, и адрес читается кусками: зона, шина, блок, регистр - В) В десятичной записи адрес не помещается в 32 бита - Г) Шестнадцатеричная запись просто короче, другой пользы нет
  3. Датчик прислал байт, программа показала −56, а ждали 200. Где ошибка? - А) Датчик сломан - Б) Байт не может хранить 200 - В) Программа поняла байт как число со знаком: одни и те же восемь бит — это 200 без знака и −56 со знаком - Г) Биты пронумерованы с единицы, а не с нуля

Проверь себя: ответы - 1 — Г. Почему: Записать число и дописать бит — разные действия: «ИЛИ» с маской 0x04 даёт 0x14 и не трогает остальные биты (0е.6). - 2 — Б. Почему: В 0x40021018 видны периферия, шина, блок RCC и смещение регистра, а десятичная запись того же числа ничего такого не показывает (0е.5). - 3 — В. Почему: Как понимать байт — со знаком или без, — решает программа, а не датчик (0е.7).

Что дальше. Этот раздел — нулевая ступень перед маршрутом новичка 0а «Маршрут новичка: двадцать разделов по порядку». Вот куда в нём ведёт каждая мысль отсюда:

Кому захочется больше про системы счисления — восьмеричная запись, перевод больших чисел и дополнительный код с формулами — раздел 63 «Как текст становится программой; системы счисления» (63.9–63.14).


Дальше: раздел 0ж «Первый вечер: что купить, как поставить среду и помигать светодиодом — пошагово для того, кто ни разу не прошивал» — числа прочитаны, пора к плате: покупки, две программы, четыре провода и первая мигалка за один вечер.

Открыть в интерактивном справочнике