Поле регистра — несколько соседних бит, которые вместе задают одну настройку: режим вывода, делитель частоты, номер канала. Чтобы прочитать или записать поле, не задев соседей, нужны две величины — сдвиг и маска — и константы правильного типа: стандарт C вычисляет 1 << 15 по-разному на 16-битном AVR и 32-битном ARM. Калькулятор считает поле для регистров от 8 до 64 бит, показывает, какой суффикс нужен константам, и пишет готовый код.
Как пользоваться
- Выберите разрядность регистра и компилятор: от компилятора зависит ширина
int, а с ней — что получится из константы без суффикса. При переходе к меньшей разрядности калькулятор оставляет младшие биты значения, а поле, которое уже не помещается, опускает к старшему биту регистра без изменения ширины: поле 21:20 в 8-битном регистре становится полем 7:6. - Введите значение регистра —
0x…,0b…или десятичное. Десятичное число с ведущим нулём калькулятор не примет: в C0777— восьмеричная константа, то есть 511, а не 777. Биты можно переключать мышью, поле выделено рамкой. - Задайте поле номерами старшего и младшего бита, как в документации: 21:20 — это биты 21 и 20, поле шириной 2 бита.
- Введите новое значение поля и номер бита для операций. Кнопки под результатом записывают поле или бит в значение регистра.
- Имена регистра и поля нужны только для кода на C: из них получатся выражения и макросы
NAME_PosиNAME_Msk.
Перевести число между системами счисления или посмотреть его как float удобнее в конвертере систем счисления, а собрать значение регистра AVR по именам битов из даташита — в конструкторе регистров AVR.
Как считается
Поле с битами от hi до lo имеет ширину k = hi − lo + 1 и сдвиг s = lo. Его маска без сдвига и со сдвигом:
Значение поля в регистре x и регистр после записи в поле значения v, где 0 ≤ v ≤ M:
На C это те же действия через сдвиги и побитовые операции: (x >> s) & M и (x & ~M_сдв) | (v << s). Для одного бита n маска — 1 << n: установить бит — x | маска, сбросить — x & ~маска, переключить — x ^ маска, проверить — x & маска.
Тип маски определяет стандарт C11 (черновик N1570). Константа получает первый тип своего списка, в котором помещается значение (6.4.4.1): 1 — это int, 1U — unsigned int, 1UL — unsigned long. Тип результата сдвига — тип левого операнда после целочисленного продвижения (6.3.1.1), которое поднимает uint8_t и uint16_t до int, если int вмещает все их значения; сдвиг на ширину этого типа и больше не определён, а знаковый сдвиг, результат которого не помещается в тип, — тоже (6.5.7). Инверсия ~ работает в типе продвинутого операнда (6.5.3.3). В выражении x & ~маска обычные арифметические преобразования (6.3.1.8) приводят более узкий операнд к типу более широкого: беззнаковое значение при этом сохраняется, то есть дополняется нулями (6.3.1.3), поэтому слишком узкая беззнаковая маска при сбросе обнуляет и старшие биты регистра. Калькулятор проверяет запись маски без суффикса и с суффиксами U, UL и ULL для обоих компиляторов: у avr-gcc int и unsigned int 16-битные, long — 32-битный, у GCC для ARM по AAPCS32 int и long 32-битные, long long у обоих — 64-битный.
Рекомендуемый суффикс калькулятор выбирает по минимальным ширинам, которые стандарт гарантирует любому компилятору (5.2.4.2.1): unsigned int — не меньше 16 бит, unsigned long — 32, unsigned long long — 64. Отсюда U для регистров до 16 бит, UL для 32-битных и ULL для 64-битных — такой код одинаково верен на AVR и ARM.
Пример: вывод PA5 у STM32F103
После сброса регистр GPIOA_CRL равен 0x4444 4444 (RM0008, 9.2.1). Поле MODE5 занимает биты 21:20, поэтому k = 2, s = 20, M = 3 и Mсдв = 0x0030 0000 — ровно GPIO_CRL_MODE5_Msk из stm32f103xb.h. Сейчас в поле 0: 0x4444 4444 >> 20 = 0x444, а 0x444 & 3 = 0. Запись значения 2 даёт
Тетрада бит 23:20 была 0100 и стала 0110: биты CNF5 не изменились, MODE5 стал 10. PA5 станет выходом с открытым стоком на 2 МГц, потому что CNF5 остался 01 (RM0008, таблицы 20 и 21). Для двухтактного выхода запишите значение 0b0010 в поле 23:20 — получится 0x4424 4444, как в примере статьи о битовых операциях.
На ARM маска может быть записана как 0x3U << 20 или как 0x3UL << 20: оба типа 32-битные. Калькулятор пишет UL, как заголовки ST, и тогда его строки #define GPIO_CRL_MODE5_Pos и GPIO_CRL_MODE5_Msk совпадают с stm32f103xb.h знак в знак. Это важно: код с _FLD2VAL подключает этот заголовок, а повторно определить макрос стандарт разрешает только с тем же списком замены (6.10.3). Если вместо (0x3UL << GPIO_CRL_MODE5_Pos) написать (0x3U << GPIO_CRL_MODE5_Pos), препроцессор GCC выдаст предупреждение о повторном определении макроса, а с ключом -Werror, который превращает предупреждения в ошибки, сборка остановится. На AVR такой же 32-битной маске UL необходим: у 0x3U << 20 тип 16-битный, и сдвиг на 20 не определён.
Типичные записи
| Запись | avr-gcc, int — 16 бит | GCC для ARM, int — 32 бита |
|---|---|---|
1 << 15 | неопределённое поведение: 32 768 не помещается в int | int, 0x8000 |
1U << 15 | unsigned int, 0x8000 | unsigned int, 0x8000 |
1 << 31 | неопределённое поведение: сдвиг больше ширины int | неопределённое поведение: 2^31 не помещается в int |
1UL << 31 | unsigned long, 0x8000 0000 | unsigned long, 0x8000 0000 |
1UL << 40 | неопределённое поведение: long 32-битный | неопределённое поведение: long 32-битный |
1ULL << 40 | unsigned long long, 0x100 0000 0000 | unsigned long long, 0x100 0000 0000 |
0x8000 | unsigned int | int |
32768 | long | int |
0x80000000 | unsigned long | unsigned int |
Отсюда суффикс маски по разрядности регистра:
| Регистр | avr-gcc | GCC для ARM |
|---|---|---|
| 8 и 16 бит | U | U |
| 32 бита | UL | U, в заголовках CMSIS и ST — UL |
| 64 бита | ULL | ULL |
Ограничения и частые ошибки
- Поле задают включительно. 21:20 — два бита, 15:4 — двенадцать. Ошибка на единицу в ширине даёт маску, которая задевает соседнее поле.
- Значение шире поля. В C выражение
(v << s) & M_сдвмолча обрежет лишние биты, калькулятор вместо этого сообщает об ошибке. - Узкая маска при сбросе.
x &= ~(1U << 3)на AVR обнуляет биты 16–31 у 32-битной переменной, на ARM — биты 32–63 у 64-битной. Таблица под калькулятором показывает это предупреждением. - Чтение-изменение-запись не атомарна. Прерывание между чтением и записью регистра может потерять его изменения; приёмы для AVR и STM32 разобраны в статье о битовых операциях.
- Макросы
_VAL2FLDи_FLD2VALприводят значение кuint32_t, поэтому для 64-битных значений не годятся; калькулятор предлагает их только для ARM и регистров до 32 бит. - Две модели данных. Калькулятор знает avr-gcc и GCC для ARM. Режим avr-gcc с ключом
-mint8, гдеint8-битный, не учитывается: по GCC Wiki он не соответствует стандарту.
Поля регистров GPIO STM32F1 и F4 подробно разобраны в статье о GPIO STM32, размеры типов на AVR и ARM — в статье «Язык C для микроконтроллеров», а поле PRIGROUP регистра SCB_AIRCR считает калькулятор приоритетов NVIC.
Частые вопросы
Как посчитать маску поля регистра?
Поле из k бит, младший из которых — бит s, имеет маску (2^k − 1), сдвинутую на s позиций. Для поля 21:20 это 0x3 << 20 = 0x0030 0000, для поля 15:4 — 0xFFF << 4 = 0x0000 FFF0. Прочитать поле — (x >> s) & (2^k − 1), записать значение v — (x & ~маска) | (v << s).
Какой суффикс нужен маске: U, UL или ULL?
Такой, при котором тип константы не уже регистра на любом компиляторе. Стандарт C11 (5.2.4.2.1) гарантирует unsigned int не меньше 16 бит, unsigned long — 32, unsigned long long — 64, поэтому для 8- и 16-битных регистров хватает U, для 32-битных нужен UL, для 64-битных — ULL. У avr-gcc ширины ровно такие. У GCC для ARM int и long 32-битные: U тоже сработает с 32-битным регистром, но UL пишут заголовки CMSIS и ST, а 64-битной переменной нужен ULL — UL там всего 32 бита.
Чем опасна маска с суффиксом U для 32-битного регистра на AVR?
Выражение ~(1U << 3) на AVR — 16-битное беззнаковое 0xFFF7. В операции с 32-битным регистром оно переводится в 32-битный беззнаковый тип с сохранением значения, то есть дополняется нулями до 0x0000 FFF7 (C11 6.3.1.8 и 6.3.1.3), и x &= ~(1U << 3) обнуляет заодно биты 16–31. Без суффикса ~(1 << 3) — знаковое −9 в дополнительном коде, который используют avr-gcc и GCC для ARM; при переводе в беззнаковый тип к нему прибавляется 2^32, получается 0xFFFF FFF7, и ошибки не будет, но такой код работает случайно. Надёжная запись — 1UL << 3.
Что означают _Pos и _Msk в заголовках STM32?
Это соглашение CMSIS: NAME_Pos — номер младшего бита поля, NAME_Msk — маска поля, уже сдвинутая на эту позицию. В stm32f103xb.h GPIO_CRL_MODE5_Pos равно 20, а GPIO_CRL_MODE5_Msk — (0x3UL << 20), то есть 0x0030 0000. Макросы _VAL2FLD(NAME, v) и _FLD2VAL(NAME, x) из заголовков ядра CMSIS сдвигают значение в поле и достают его обратно.
Может ли шестнадцатеричная константа без суффикса быть беззнаковой?
Да. По C11 (6.4.4.1) шестнадцатеричная константа получает первый подходящий тип из списка int, unsigned int, long, unsigned long, long long, unsigned long long. На AVR 0x7FFF — int, а 0x8000 уже unsigned int; на ARM 0x8000 0000 — unsigned int. Десятичная константа без суффикса U беззнаковой не бывает: 32768 на AVR имеет тип long.
Источники
- ISO/IEC 9899:201x (C11), Committee Draft N1570, April 2011 — 6.4.4.1, п. 3–4 — константа с ведущим 0 восьмеричная; п. 5 и таблица — тип целой константы: первый тип списка, в котором помещается значение; у десятичной без суффикса — int, long, long long, у шестнадцатеричной — ещё и беззнаковые, с суффиксом U — только беззнаковые; п. 6 — константа без подходящего типа; 5.2.4.2.1 — минимальные UINT_MAX = 65 535, ULONG_MAX = 4 294 967 295, ULLONG_MAX = 2^64 − 1; 6.3.1.1, п. 2 — целочисленное продвижение; 6.3.1.3 — перевод в беззнаковый тип: значение сохраняется, если помещается (п. 1), иначе берётся по модулю (п. 2); 6.3.1.8 — обычные арифметические преобразования; 6.5.7, п. 3 — тип результата сдвига — тип продвинутого левого операнда, сдвиг на отрицательное число или на ширину типа и больше не определён; п. 4 — беззнаковый сдвиг по модулю 2^N, знаковый с переполнением не определён; 6.5.3.3, п. 4 — ~ работает в типе продвинутого операнда, над беззнаковым даёт максимум типа минус операнд; 6.10.3, п. 1–2 — повторное определение макроса допустимо только с тем же списком замены
- GCC Manual: Integers implementation — GCC поддерживает только дополнительный код для знаковых типов (C11 6.2.6.2), поэтому у avr-gcc и GCC для ARM ~8 = −9
- The C Preprocessor (GCC): 3.8 Undefining and Redefining Macros — повторное определение макроса должно совпадать с прежним по лексемам и расстановке пробелов, комментарии считаются пробелом; при несовпадении препроцессор выдаёт предупреждение и берёт новое определение
- GCC Wiki: avr-gcc — ABI and implementation defined behaviour — Type Layout: short и int — 2 байта, long — 4, long long — 8; с ключом -mint8 int 8-битный, что не соответствует стандарту
- Arm. Procedure Call Standard for the Arm Architecture (AAPCS32), release 2025Q4 — Arithmetic Types, таблица Mapping of C & C++ built-in data types: int и long — слово, long long — двойное слово; таблица Byte size and byte alignment of fundamental data types: слово — 4 байта
- Arm. CMSIS-Core (Cortex-M) 5.6.0: Peripheral Access — имена <поле>_Pos для позиции и <поле>_Msk для сдвинутой маски, пример SCB_CPUID_IMPLEMENTER_Pos 24U и (0xFFUL << …_Pos); макросы _VAL2FLD(field, value) и _FLD2VAL(field, value)
- Arm. CMSIS-Core (Cortex-M) 5.6.0: Device Header File <device.h> — шаблон заголовка устройства: маска поля таймера записана как (1UL << …_TMR_CONTROL_SIZE_Pos) — суффикс UL у 32-битных регистров
- Arm. CMSIS 5, core_cm3.h V5.2.0 — определения _VAL2FLD и _FLD2VAL с приведением к uint32_t; SCB_AIRCR_VECTKEY_Pos 16U, SCB_AIRCR_PRIGROUP_Pos 8U и маска (7UL << 8) — эталоны тестов
- STMicroelectronics. cmsis-device-f1: stm32f103xb.h — GPIO_CRL_MODE5_Pos (20U) и GPIO_CRL_MODE5_Msk (0x3UL << GPIO_CRL_MODE5_Pos) = 0x00300000, GPIO_CRL_CNF5 — биты 23:22, RCC_CFGR_PLLSRC — бит 16, RCC_CFGR_PLLMULL — 21:18, USART_BRR_DIV_Mantissa — 15:4 (эталоны тестов; сгенерированные #define сверены с этими строками знак в знак); строка 131 — #include core_cm3.h
- STMicroelectronics. STM32F1 HAL, драйвер stm32f1xx_hal_gpio.c — HAL_GPIO_Init: смещение поля вывода n — 4 · n в CRL (выводы 0–7) и 4 · (n − 8) в CRH (registeroffset = position << 2u или (position − 8u) << 2u), запись через MODIFY_REG — вторая проверка позиций полей GPIO
- STMicroelectronics. RM0008 Rev 21. Reference manual: STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx — 9.2.1: значение GPIOx_CRL после сброса — 0x4444 4444 (значение по умолчанию в калькуляторе); таблицы 20 и 21: CNF = 01 при MODE ≠ 00 — выход с открытым стоком, CNF = 00 — двухтактный, MODE = 10 — выход до 2 МГц
Встроить калькулятор на свой сайт
Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах
<iframe src="https://gaw.ru/embed/bitmask/" title="Калькулятор битовых масок и полей регистра" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/bitmask/">Калькулятор битовых масок и полей регистра</a> — GAW.ru</p>Автоподстройка высоты рамки
Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.
<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>Обновлено .
Нашли ошибку в расчёте?