Макрос C для установки нескольких бит - PullRequest
0 голосов
/ 25 января 2019

Я работаю над микроконтроллером в C. Частично это связано с изменением битовых значений в регистрах. Я придумал несколько макросов, чтобы упростить задачу:

#define BIT_SET(addr, shift) (addr | (1 << shift))
#define BIT_RESET(addr, shift) (addr & ~(1 << shift))
#define BIT_GET(addr, shift) (addr & (1 << shift))
...
reg1 = BIT_SET(reg1, 3); //set bit 3
...

Однако теперь я хочу сделать макрос, который будет менять несколько битов за раз. Например, может потребоваться изменить первые 3 бита 10101010 на 110, в результате чего получится число 11001010.

Могу ли я сделать это в C? Или я подхожу к этому неправильно?

Ответы [ 3 ]

0 голосов
/ 25 января 2019

Вы можете ввести маску полной ширины, которая определяет биты, которые вы хотите установить.Например, для установки первых 3 битов используйте 0xe0, который находится в двоичном формате 11100000.

Наряду с этой маской укажите значение для записи также во всю ширину.
Например, 0xc0 для требуемого двоичного файла 110 в первомтри бита.

Затем нужно немного магии-операции, чтобы записать только нужные позиции битов.

#define MULTIBIT_SET(addr, mask, value) (((addr)&((0xff)^(mask)))|((value)&(mask)))

Здесь ((0xff)^(mask)) инвертирует маску, так что при этом удаляетсясуществующие биты из значения.
((value)&(mask)) - это значение, но только с установленными битами в желаемых позициях, то есть это предотвращает нежелательную установку битов в другом месте.

Всего, или две части, дает вамжелаемый результат.

Здесь есть выбор дизайна, о котором я хочу упомянуть:
Назовите это паранойей.Если кто-то говорит мне: «Я хочу установить только первые три бита, на это значение, которое имеет биты в другом месте», тогда я предпочитаю принять это безопасным образом, то есть удалить «биты в другом месте».
Да, я предполагаючто установка битов без намерения является большим риском, чем их установка, что является делом вкуса.

0 голосов
/ 25 января 2019

Вы всегда можете сделать это в C.: -)

Если вы используете компилятор, совместимый с C99, и, учитывая, что это 2019, я предполагаю, что вы можете использовать макросы с переменным числом, чтобы сделать эторабота, а более конкретно, P99 .

У меня был пример, когда я извлекал соответствующие компоненты P99, но он становился грязным, потому что это серьезное использование препроцессора Cкак полный по Тьюрингу язык.

Это также нарушает скобки;в частности, EXTRACT_VALUE x расширяется до EXTRACT_VALUE (a, b), поскольку мы передаем список кортежей.

#include "p99.h"

#define EXTRACT_SHIFT(shift, value) (shift)
#define EXTRACT_VALUE(shift, value) (value)
#define MERGE(NAME, I, A, B) (A), (B)

#define SET_OR_CLEAR(target, x, i) \
    ((EXTRACT_VALUE x) ? \
        ((target) |= (1 << EXTRACT_SHIFT x)) : \
        ((target) &= ~(1 << EXTRACT_SHIFT x)))
#define SET_AND_CLEAR(target, pairs...) \
    P99_FOR(target, P99_NARG(pairs), MERGE, SET_OR_CLEAR, pairs)

// ...
reg1 = BIT_SET(reg3, 3); //set bit 3                                         
// ...
SET_AND_CLEAR(reg3, (7, 1), (6, 1), (5, 0)); // set bits 6 and 7, clear bit 5

Возможно, вам придется скомпилировать с -std=c99.Обратите внимание, что я использовал переменное слово pairs вместо __VA_ARGS__, чтобы попытаться прояснить, что мы хотим использовать 2-кортежа в качестве аргументов.

Обратите внимание, что это не маскирует входной регистр, которыйне устанавливается или не очищается.

0 голосов
/ 25 января 2019

Мой опыт работы с битовыми масками для нескольких бит намного проще. Таким образом, отдельные биты идентифицируются определенным определением, имя которого указывает функцию бита, как в

#define ENABLE_T1  0x0001
#define ENABLE_T2  0x0002

, а затем они используются с побитовыми операторами для включения и выключения битов.

unsigned short usReg1 = GetRegister(REG1);

// disable T1 and enable T2

usReg1 &= ~ ENABLE_T1;
usReg1 |= ENABLE_T2;

или

// enable both T1 and T2
usReg1 |= (ENABLE_T1 | ENABLE_T2);

или

// disable both T1 and T2
usReg1 &= ~(ENABLE_T1 | ENABLE_T2);

или для проверки битов

if (usReg1 & ENABLE_T1) {  // is T1 turned on

if (usReg1 & (ENABLE_T1 | ENABLE_T2)) {  // is either T1 or T2 turned on

if ((usReg1 & (ENABLE_T1 | ENABLE_T2)) == ENABLE_T1) {  // is T1 turned on and T2 turned off

if ((usReg1 & (ENABLE_T1 | ENABLE_T2)) && ((usReg1 & (ENABLE_T1 | ENABLE_T2)) != (ENABLE_T1 | ENABLE_T2))) {  // is either T1 or T2 turned on but not both

Примечание по макросам

Кстати, вам нужно убедиться, что вы используете круглые скобки в определениях макросов, чтобы максимально изолировать аргументы в целях безопасности. Так

#define BIT_SET(addr, shift) (addr | (1 << shift))

действительно должно быть записано как

#define BIT_SET(addr, shift) ((addr) | (1 << (shift)))
...