МПС 3 курс Лекции Лекция 3 Представление данных Лекция 3 Представление данных и операции - BysonHunter/Tavrich_college GitHub Wiki
🏠 Главная · Микропроцессорные системы · 3 курс · Лекции · Лекция 3 Представление данных
Дисциплина: «Микропроцессорные системы»
Уровень: 4 курс колледжа
Рекомендуемая продолжительность: 2 академических часа
Понять, как микропроцессор представляет числа, символы и логические значения в виде двоичных кодов, а также научиться выполнять основные арифметические и побитовые операции с учётом разрядности и флагов состояния.
После изучения темы студент должен уметь:
- различать бит, байт, машинное слово и разрядность данных;
- переводить целые числа между двоичной, восьмеричной, десятичной и шестнадцатеричной системами;
- определять диапазон беззнакового и знакового целого числа;
- получать дополнительный код отрицательного числа;
- объяснять различие переноса и знакового переполнения;
- использовать логические операции, маски и сдвиги;
- учитывать порядок байтов при обмене многобайтными данными;
- выбирать типы
uint8_t,int16_t,uint32_tи другие типы фиксированной ширины.
Микропроцессор не воспринимает температуру, давление, скорость или звук непосредственно. Датчик преобразует физическую величину в электрический сигнал, а аналого-цифровой преобразователь, АЦП, превращает этот сигнал в целое число. Процессор получает уже готовый двоичный код, размещает его в регистре и выполняет над ним команды.
На рисунке показан общий путь данных от измеряемого процесса до результата обработки.

Рисунок 1 — датчик формирует электрический сигнал, АЦП создаёт двоичный код, а процессор обрабатывает этот код и передаёт результат на выходное устройство.
Важно различать три понятия:
- данные — код, который хранится или передаётся;
- значение — смысл, приписанный этому коду;
- формат — правило интерпретации битов.
Например, комбинация 11111111 может означать:
- число 255 в формате
uint8_t; - число −1 в формате
int8_t; - часть цвета пикселя;
- набор восьми логических флагов;
- фрагмент команды процессора.
Биты сами по себе не сообщают процессору, что именно они обозначают. Смысл определяется командой и программой.
Бит принимает одно из двух значений: 0 или 1. Физически этим значениям могут соответствовать два диапазона напряжения, два состояния триггера или два направления намагниченности.
Байт обычно состоит из 8 бит. Один байт имеет
Машинное слово — количество бит, с которым процессор наиболее естественно выполняет основные операции. У восьмиразрядного AVR основные регистры содержат 8 бит, а ядро ARM Cortex-M4 имеет 32-разрядные регистры общего назначения.
Соотношение этих понятий показано на рисунке.

Рисунок 2 — бит является минимальной единицей, восемь бит образуют байт, а размер машинного слова зависит от архитектуры процессора.
Если беззнаковое число занимает
Для восьми бит диапазон равен
Количество комбинаций и диапазон нельзя путать. Восьмиразрядное поле содержит 256 комбинаций, но максимальное беззнаковое значение равно 255, поскольку отсчёт начинается с нуля.
-
MSB (
Most Significant Bit) — старший, наиболее значимый бит; -
LSB (
Least Significant Bit) — младший, наименее значимый бит.
В числе 10110010₂ левый бит имеет вес

Рисунок 3 — значение двоичного числа образуется сложением весов тех разрядов, в которых записана единица.
В позиционной системе вес цифры зависит от её положения. Для числа с основанием
где
В микропроцессорной технике используют несколько систем.
| Система | Основание | Цифры | Назначение |
|---|---|---|---|
| Двоичная | 2 |
0, 1
|
Работа цифровых схем и регистров |
| Восьмеричная | 8 |
0–7
|
Компактная запись групп по три бита |
| Десятичная | 10 |
0–9
|
Привычное человеку представление |
| Шестнадцатеричная | 16 |
0–9, A–F
|
Адреса, машинные коды, регистры, маски |
В языке C/C++ основания обозначают префиксами:
uint8_t a = 42; // десятичная запись
uint8_t b = 0b00101010; // двоичная запись
uint8_t c = 052; // восьмеричная запись
uint8_t d = 0x2A; // шестнадцатеричная записьВсе четыре переменные содержат одинаковое значение 42. Различается только способ записи исходного текста программы.
Необходимо умножить каждый бит на вес его разряда и сложить результаты:
Для перевода через степени двойки нужно разложить десятичное число в сумму степеней 2. Работу начинают с наибольшей степени, которая не превышает исходное число. Если степень входит в сумму, в соответствующем разряде записывают 1, иначе — 0.
Рассмотрим число 77. Наибольшая подходящая степень равна
Следующая подходящая степень равна
Оставшееся число раскладывается как
Запишем коэффициенты при степенях от
| Степень | |||||||
|---|---|---|---|---|---|---|---|
| Значение степени | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| Входит в сумму | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
Получаем:
Обратный перевод выполняют по тому же набору степеней: складывают веса разрядов, содержащих единицу:
Последовательность действий можно представить следующим алгоритмом.
flowchart TD
A[Исходное десятичное число N] --> B[Выбрать наибольшую степень 2, не превышающую N]
B --> C[Записать 1 в соответствующем разряде]
C --> D[Вычесть выбранную степень из N]
D --> E{Остаток равен нулю?}
E -- Нет --> B
E -- Да --> F[В остальных разрядах записать 0]
F --> G[Получено двоичное число]
На рисунке показан перевод в обоих направлениях с использованием степеней двойки.

Рисунок 4 — число 77 раскладывается в сумму $2^6+2^3+2^2+2^0$, а двоичный код образуется коэффициентами при степенях двойки.
Поскольку
| Десятичное | Двоичное | Шестнадцатеричное |
|---|---|---|
| 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 |
Для перевода двоичного числа группировку начинают справа. При необходимости слева дописывают нули. Пример показан на рисунке.

Рисунок 5 — одно двоичное число преобразуется в восьмеричную запись группировкой по три бита, а в шестнадцатеричную — по четыре.
В беззнаковом формате все биты задают величину. Диапазон
Беззнаковые типы удобны для счётчиков, адресов, битовых масок и показаний АЦП, которые не могут быть отрицательными.
Современные процессоры обычно представляют отрицательные целые числа в дополнительном коде. Для получения кода числа
- записать двоичный код положительного числа
$X$ ; - инвертировать все биты;
- прибавить единицу.
Для
На рисунке показано получение кода числа −18 и диапазон типа int8_t.

Рисунок 6 — число −18 получается инверсией кода +18 и добавлением единицы; восьмиразрядный знаковый диапазон несимметричен и равен от −128 до +127.
Проверим код сложением:
00010010 +18
+ 11101110 −18
-----------
1 00000000
Девятый бит отбрасывается, а восьмиразрядный результат равен нулю.
В программах для микроконтроллеров предпочтительны типы из <stdint.h> или <cstdint>:
| Тип | Разрядность | Диапазон |
|---|---|---|
uint8_t |
8 | 0…255 |
int8_t |
8 | −128…127 |
uint16_t |
16 | 0…65 535 |
int16_t |
16 | −32 768…32 767 |
uint32_t |
32 | 0…4 294 967 295 |
int32_t |
32 | −2 147 483 648…2 147 483 647 |
Запись uint16_t сразу сообщает читателю программы, что переменная беззнаковая и занимает ровно 16 бит. Обычный тип int может иметь разную ширину на разных платформах.
Логическое значение имеет два состояния: истина и ложь. В C++ ему соответствует тип bool. На уровне команд процессор обычно проверяет отдельные биты или флаги регистра состояния.
Текст хранится как последовательность кодов. ASCII определяет коды латинских букв, цифр и управляющих символов в диапазоне 0…127. Например, A имеет код 65, или 0x41.
Unicode задаёт кодовые позиции для символов различных письменностей. UTF-8 кодирует одну кодовую позицию последовательностью от одного до четырёх байтов. Первые 128 кодов UTF-8 совпадают с ASCII. Поэтому утверждение «один символ всегда занимает один байт» верно только для ограниченного набора однобайтовых кодировок и неверно для UTF-8 в общем случае.
В микроконтроллерных системах применяют два подхода:
- фиксированная точка: целое число хранит величину с заранее выбранным масштабом, например 253 означает 25,3 °C;
- плавающая точка: число хранится через знак, порядок и значащую часть.
Фиксированная точка обеспечивает предсказуемую точность и часто требует меньше ресурсов. Плавающая точка удобнее при большом диапазоне значений. На Cortex-M4F операции с float может ускорять аппаратный блок FPU, если он присутствует и включён в конкретном микроконтроллере.
Основные правила:
| Операция | Результат | Перенос |
|---|---|---|
0 + 0 |
0 |
0 |
0 + 1 |
1 |
0 |
1 + 0 |
1 |
0 |
1 + 1 |
0 |
1 |
1 + 1 + 1 |
1 |
1 |
Сложение выполняют справа налево, начиная с младшего бита. Перенос из текущего разряда добавляют к следующему.
Рассмотрим сложение двух однобайтовых чисел:
Поразрядная запись имеет вид:
переносы: 1 1 1 1 1 1
0 0 1 0 1 1 0 1 45
+ 0 0 0 1 1 0 1 1 27
-------------------
0 1 0 0 1 0 0 0 72
7 6 5 4 3 2 1 0 номера битов
Первые действия выполняются так:
- Бит 0:
$1+1=10_2$ . В результат записывается 0, формируется перенос 1. - Бит 1:
$0+1+1=10_2$ . В результат записывается 0, перенос сохраняется. - Бит 2:
$1+0+1=10_2$ . В результат записывается 0, перенос сохраняется. - Бит 3:
$1+1+1=11_2$ . В результат записывается 1, перенос сохраняется. - Операция продолжается до старшего разряда.
Итог:
Подробная схема сложения приведена на рисунке.

Рисунок 7 — при сложении чисел 45 и 27 перенос последовательно передаётся в старшие разряды, а результат 01001000₂ равен 72.
При ручном вычитании можно занимать единицу из старшего разряда. Однако АЛУ обычно заменяет вычитание сложением с дополнительным кодом второго операнда:
Выполним
+29: 00011101
инверсия: 11100010
добавить 1:11100011 это код −29
Теперь складываем 83 и −29:
01010011 +83
+ 11100011 −29
----------
1 00110110
Перенос за пределы восьмиразрядного результата отбрасывается:
Следовательно,

Рисунок 8 — вычитаемое 29 заменяется его дополнительным кодом, после чего процессор выполняет обычное двоичное сложение.
Двоичное умножение строится на частичных произведениях. Каждый единичный бит множителя добавляет к результату множимое, сдвинутое влево на номер этого бита.
Рассмотрим пример:
Множитель 00001011₂ содержит единицы в разрядах 0, 1 и 3. Поэтому формируются три ненулевых частичных произведения:
| Бит множителя | Действие | Частичное произведение |
|---|---|---|
| 0 = 1 | 00001101 << 0 |
00001101 = 13 |
| 1 = 1 | 00001101 << 1 |
00011010 = 26 |
| 2 = 0 | не добавляется | 00000000 |
| 3 = 1 | 00001101 << 3 |
01101000 = 104 |
Сложим частичные произведения:
00001101 13
00011010 26
00000000 0
+ 01101000 104
----------
10001111 143
Таким образом:
Алгоритм умножения сдвигом и сложением показан ниже.
flowchart TD
A[Результат равен 0] --> B{Младший бит множителя равен 1?}
B -- Да --> C[Прибавить множимое к результату]
B -- Нет --> D[Сложение пропустить]
C --> E[Сдвинуть множимое влево]
D --> E
E --> F[Сдвинуть множитель вправо]
F --> G{Множитель равен 0?}
G -- Нет --> B
G -- Да --> H[Результат готов]
Подробный числовой пример приведён на рисунке.

Рисунок 9 — единичные биты множителя 11 выбирают сдвинутые значения 13, 26 и 104, сумма которых равна 143.
Для двух восьмиразрядных сомножителей результат в общем случае может потребовать 16 бит. Например,
Деление можно выполнить последовательной обработкой битов делимого. На каждом шаге очередной бит присоединяется справа к текущему остатку. Затем остаток сравнивается с делителем:
- если остаток меньше делителя, в частное записывается 0;
- если остаток не меньше делителя, делитель вычитается, а в частное записывается 1.
Рассмотрим деление
| Шаг | Следующий бит делимого | Остаток после присоединения бита | Действие | Новый бит частного |
|---|---|---|---|---|
| 1 | 1 | 1 |
|
0 |
| 2 | 0 | 2 |
|
0 |
| 3 | 0 | 4 |
|
0 |
| 4 | 1 | 9 |
|
0 |
| 5 | 1 | 19 | 1 | |
| 6 | 1 | 13 | 1 | |
| 7 | 0 | 0 |
|
0 |
| 8 | 0 | 0 |
|
0 |
Полученные биты частного образуют 00001100₂, то есть 12. Остаток равен нулю:
Алгоритм двоичного деления имеет следующий вид.
flowchart TD
A[Остаток R и частное Q равны 0] --> B[Присоединить к R следующий бит делимого]
B --> C{R не меньше делителя?}
C -- Да --> D[Вычесть делитель из R и записать 1 в Q]
C -- Нет --> E[Записать 0 в Q]
D --> F{Все биты обработаны?}
E --> F
F -- Нет --> B
F -- Да --> G[Q содержит частное, R содержит остаток]

Рисунок 10 — при делении 156 на 13 частное формируется по одному биту, а промежуточный остаток уменьшается вычитанием делителя.
После арифметической или логической команды процессор может обновить флаги регистра состояния. В ARM Cortex-M используются флаги N, Z, C, V. В AVR аналогичные признаки находятся в регистре SREG.
| Флаг | Значение |
|---|---|
N |
результат имеет установленный знаковый бит |
Z |
результат равен нулю |
C |
возник перенос из старшего разряда или заём, согласно правилам команды |
V |
возникло переполнение знакового результата |
Флаги C и V нельзя считать взаимозаменяемыми. Их различие показано на рисунке.

Рисунок 11 — перенос C обнаруживает выход беззнакового результата за диапазон, а V показывает невозможный для выбранной разрядности знаковый результат.
Знаковое переполнение при сложении возникает, если складываются числа одинакового знака, а результат получает противоположный знак. Например, в восьми разрядах 127 + 1 превращается в код 10000000, который интерпретируется как −128.
В языках C и C++ беззнаковая арифметика выполняется по модулю
Логические команды обрабатывают каждый разряд независимо.
A |
B |
A AND B |
A OR B |
A XOR B |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
Операция NOT инвертирует каждый бит.
-
ANDоставляет единицы только в разрешённых маской позициях; -
ORустанавливает выбранные биты; -
XORпереключает выбранные биты; -
NOTсоздаёт инвертированную маску.
Маска — двоичное значение, в котором единицы отмечают обрабатываемые разряды. Маски особенно важны при работе с регистрами периферии: один регистр часто управляет сразу несколькими выводами или режимами.
Основные операции показаны на рисунке.

Рисунок 12 — операции OR и AND позволяют установить, сбросить или проверить отдельный бит, сохранив остальные разряды регистра.
Пример на C/C++:
uint8_t reg = 0b10100101;
reg |= (1u << 3); // установить бит 3
reg &= ~(1u << 2); // сбросить бит 2
reg ^= (1u << 0); // переключить бит 0
bool bit5 = (reg & (1u << 5)) != 0; // проверить бит 5Нумерация битов начинается с нуля: младший бит имеет номер 0.
Следует различать:
- побитовые операторы
&,|,^,~; - логические операторы
&&,||,!.
Выражение a & b обрабатывает отдельные биты чисел, а a && b проверяет истинность двух условий.
При логическом сдвиге освободившиеся позиции заполняются нулями. Для беззнакового числа сдвиг влево на один разряд соответствует умножению на 2, если значащий бит не был потерян. Сдвиг вправо соответствует целочисленному делению на 2.
Арифметический сдвиг вправо копирует знаковый бит в освободившиеся старшие позиции, благодаря чему отрицательное число сохраняет знак.
Различие показано на рисунке.

Рисунок 13 — логический сдвиг заполняет позиции нулями, а арифметический сдвиг вправо распространяет знаковый бит.
При циклическом сдвиге вытесненный бит возвращается с противоположной стороны. Процессоры также могут выполнять сдвиг через флаг переноса. Такие операции применяют в алгоритмах контроля, кодирования и многобайтной арифметике.
Практическое правило: перед сдвигом следует учитывать тип и ширину операнда. Для формирования 32-разрядной маски безопаснее использовать 1UL << n, а не 1 << n, если ширина обычного int на платформе неизвестна.
Многобайтное число занимает несколько соседних адресов. Архитектура определяет, какой байт хранится по младшему адресу.
- little-endian: сначала младший байт;
- big-endian: сначала старший байт.
На рисунке показано размещение числа 0x12345678.

Рисунок 14 — при little-endian по адресу 0x1000 находится байт 0x78, а при big-endian — 0x12.
Порядок байтов особенно важен при:
- обмене по сети и последовательным интерфейсам;
- чтении двоичных файлов;
- передаче структур между разными процессорами;
- сборке 16- и 32-разрядного значения из отдельных байтов.
Пример явной сборки 16-разрядного числа из двух байтов:
uint8_t highByte = 0x12;
uint8_t lowByte = 0x34;
uint16_t value = (static_cast<uint16_t>(highByte) << 8) | lowByte;
// value == 0x1234Явная сборка делает порядок байтов понятным и не зависит от размещения объекта в памяти.
Предположим, один байт содержит состояние системы:
| Бит | Назначение |
|---|---|
| 0 | двигатель включён |
| 1 | авария |
| 2 | автоматический режим |
| 3 | датчик готов |
| 4–7 | резерв |
Программа может управлять полями независимо:
#include <stdint.h>
constexpr uint8_t MOTOR = 1u << 0;
constexpr uint8_t ALARM = 1u << 1;
constexpr uint8_t AUTO = 1u << 2;
constexpr uint8_t READY = 1u << 3;
uint8_t status = 0;
status |= MOTOR; // включить двигатель
status |= AUTO; // включить автоматический режим
status &= ~MOTOR; // выключить двигатель
if ((status & ALARM) != 0) {
// обработать аварийное состояние
}Такой способ компактен: восемь независимых логических признаков помещаются в одном байте. Однако назначение битов следует описывать именованными константами, иначе программа быстро становится непонятной.
| Ошибка | Почему возникает | Правильный подход |
|---|---|---|
| Считать, что байт может хранить числа от 0 до 256 | Путают число комбинаций и максимальное значение | Диапазон uint8_t: 0…255 |
| Забывать разрядность результата | Старшие биты отбрасываются | Проверять диапазон до операции |
Использовать C для знаковых чисел |
Перенос не равен знаковому переполнению | Для знаковой арифметики учитывать V
|
| Считать старший бит отдельным знаком | В дополнительном коде он имеет отрицательный вес | Использовать правила дополнительного кода |
Путать & и &&
|
Операторы имеют разное назначение |
& для битов, && для условий |
Сдвигать литерал 1 на большое число позиций |
Тип int может оказаться недостаточно широким |
Использовать 1u, 1UL или нужный тип |
| Предполагать один байт на любой символ | UTF-8 имеет переменную длину | Различать символ, кодовую позицию и байты |
| Игнорировать порядок байтов | Устройства по-разному формируют многобайтные поля | Явно задавать формат протокола |
- Микропроцессор работает с битовыми комбинациями, а формат определяет их смысл.
- Разрядность задаёт число возможных комбинаций и диапазон значений.
- Двоичная система удобна для цифровых схем, а шестнадцатеричная — для компактной записи кодов и адресов.
- Отрицательные целые числа обычно хранятся в дополнительном коде.
- Перенос
Cи переполнениеVотносятся к разным способам интерпретации результата. - Логические операции и маски позволяют изменять отдельные биты регистров.
- Сдвиги перемещают разряды и могут использоваться в арифметике, упаковке данных и работе с периферией.
- При обмене многобайтными данными нужно учитывать порядок байтов.
- Почему одна и та же комбинация битов может обозначать разные данные?
- Сколько комбинаций содержит 12-разрядное поле?
- Каков диапазон
uint16_tиint16_t? - Чему равно
10110110₂в десятичной системе? - Почему одна шестнадцатеричная цифра соответствует четырём битам?
- Как получить дополнительный код отрицательного числа?
- Чем флаг переноса отличается от флага переполнения?
- Как установить бит 6, не изменив остальные биты регистра?
- Как проверить состояние бита 2?
- Чем логический сдвиг вправо отличается от арифметического?
- В чём различие
&и&&в C/C++? - Как число
0x1234размещается в памяти little-endian? - Почему кириллический символ в UTF-8 обычно занимает больше одного байта?
- Когда фиксированная точка может быть удобнее
float? - Как перевести десятичное число в двоичное с помощью степеней двойки?
- Почему при умножении сдвигом складывают только те частичные произведения, которым соответствуют единичные биты множителя?
- Как формируется очередной бит частного при двоичном делении?
Переведите число 173₁₀ в двоичную, восьмеричную и шестнадцатеричную системы. Выполните обратную проверку.
Представьте числа −37 и −100 в восьмиразрядном дополнительном коде. Сложением с положительным числом проверьте первый результат.
Для каждой восьмиразрядной операции укажите результат и значения флагов C, Z, N, V:
-
0xFF + 0x01; -
0x7F + 0x01; -
0x80 + 0x80; -
0x01 − 0x01.
Разработайте байт состояния, содержащий не менее пяти признаков устройства. Для каждого признака запишите выражения установки, сброса, переключения и проверки.
Выполните вычисления подробно, показывая переносы, дополнительный код и промежуточные результаты:
- сложите однобайтовые числа 58 и 37;
- вычислите
$91-46$ через сложение с дополнительным кодом; - умножьте 12 на 10 сдвигом и сложением;
- разделите 180 на 15 сдвигом, сравнением и вычитанием;
- проверьте все ответы обратными операциями в десятичной системе.
- Arm. Cortex-M4 Devices Generic User Guide.
- Microchip. AVR Instruction Set Manual.
- Microchip. AVR Status Register.
- Arduino. Bit Math.
- Unicode Consortium. The Unicode Standard.
- Unicode Consortium. UTF-8, UTF-16, UTF-32 and BOM.
- Intel. Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 1.
- Fixed-width integer types in C++.