Во встраиваемых системах часто для экономии памяти приходится использовать битовые операции, например для реализации флага какого-либо события. Далее рассмотрен удобный способ работы с битами на макросах.
Для начала создадим макрос для объявления битовых массивов:
typedef uint8_t BARR_ELTYPE;
#define BARR_ELBITS 8
#define BARR_ARRAYSIZE(N) ((N + BARR_ELBITS - 1) / BARR_ELBITS)
Так, для того чтобы создать, массив из 14 бит:
BARR_ELBITS arr_bit(BARR_ARRAYSIZE(N))
14 бит — это 2 байта без 2 бит. Определение количества байт идет путем деления количества байт(N) на количество байт в одной переменной(BARR_ELBITS) и округления значения в большую сторону. Для округления используется формула (A + B — 1) / B. Данная формула верна если A и B не отрицательны и A + B не создает переполнения.
Теперь создадим макросы для изменения состояния битов и их проверки.
#define BARR_ELMUM(N) (N / BARR_ELBITS)
#define BARR_BITNUM(N) (N % BARR_ELBITS)
#define BARR_SET(barr, N) ( barr[BARR_ELMUM(N)] |= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) )
#define BARR_CLEAR(barr, N) ( barr[BARR_ELMUM(N)] &= ( ~( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) ) )
#define BARR_INV(barr, N) (barr[BARR_ELMUM(N)] ^= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))
#define BARR_TEST(barr, N) (barr[BARR_ELMUM(N)] & ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))
BARR_ELMUM — используется для определения элемента(байта) в котором находится бит, путем деления без остатка.
BARR_BITNUM — используется для определения позиции бита в элементе(байте), путем деления и взятия остатка.
BARR_SET и BARR_CLEAR — соответственно устанавливают и сбрасывают нужный бит в битовом массиве. Для этого они используют битовое «ИЛИ» и «И» с 1 и 0 в нужном разряде соответственно.
BARR_INV — инвертирует нужный бит. Для этого используется битовое исключающее «ИЛИ» с 1.
BARR_TEST — проверяет установлен ли нужный бит в 1 или нет. Если установлен, то возвращает не 0 (именно не 0, а не 1).
Пример:
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint8_t BARR_ELTYPE;
#define BARR_ELBITS 8
#define BARR_ARRAYSIZE(N) ((N + BARR_ELBITS - 1) / BARR_ELBITS)
#define BARR_ELMUM(N) (N / BARR_ELBITS)
#define BARR_BITNUM(N) (N % BARR_ELBITS)
#define BARR_SET(barr, N) ( barr[BARR_ELMUM(N)] |= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) )
#define BARR_CLEAR(barr, N) ( barr[BARR_ELMUM(N)] &= ( ~( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) ) )
#define BARR_INV(barr, N) (barr[BARR_ELMUM(N)] ^= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))
#define BARR_TEST(barr, N) (barr[BARR_ELMUM(N)] & ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))
BARR_ELTYPE arr_bit[BARR_ARRAYSIZE(16)];
void main(void)
{
BARR_SET(arr_bit, 0);
BARR_SET(arr_bit, 1);
BARR_CLEAR(arr_bit, 4);
BARR_CLEAR(arr_bit, 1);
BARR_INV(arr_bit, 1);
printf("%d \n", arr_bit[0]);
if(BARR_TEST(arr_bit, 0) != 0)
{
printf("%d \n", sizeof(arr_bit));
}
}
Note: помнить
Таблица истиности ^
x1 x2 res
1 1 0
1 0 1
0 1 1
0 0 0
Тогда когда ^ с 1, то всегда происходит изменение состояния.
Если с 0, то нет изменения состояния.
x = (x ^ (1<<n)) // изменение состояния n-го бита переменной x
Для определения члена массива и номера байта которого надо изменить состояние можно использовать такую конструкцию:
amt_net_temp = work_buf_trans / 8; // определили какой член массива
amt_net_temp_2 = work_buf_trans % 8; // определили какой бит
// меняем состояние
f_busy_buf[amt_net_temp] = (f_busy_buf[amt_net_temp] ^ (1<<amt_net_temp_2));