Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C macro to set multiple bits

I am working on a microcontroller in C. Part of this involves changes bit values in registers. I have come up with a few macros to make things easier:

#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
...

However, now I want to make a macro which will change several bits at a time. For example, may want to change the first 3 bits of 10101010 to 110, resulting in the number 11001010.

Can I do this in C? Or am I approaching this the wrong way?

like image 541
riverrun Avatar asked Jan 25 '19 16:01

riverrun


People also ask

How do I set up multiple bits?

to set the bit n of num (for instance num |= (1 << 0); sets the bit 0 ). To set (clear) multiple bits you have just to OR ( AND ) the num with the appropriate constant. num &= ~0xFF; clears the same bits.

How do you set a bit in a macro?

Use the bitwise OR operator (|) to set a bit. number |= 1 << x; That will set bit x. Use the bitwise AND operator (&) to clear a bit.

Which macro clears the nth bit in a byte by keeping the other bits unchanged?

Clearing bit using macro: We use the bitwise AND operator (&) to clear a bit. x &= ~(1UL << pos); it will clear nth bit. You must invert the bit string with the bitwise NOT operator (~), then AND it.

How do you reset a bit in a byte?

Use the bitwise AND operator ( & ) to clear a bit. number &= ~(1UL << n); That will clear the n th bit of number . You must invert the bit string with the bitwise NOT operator ( ~ ), then AND it.


2 Answers

My experience has been having bit masks for multiple bits is much easier. So the individual bits are identified with a particular define whose name indicates the function of the bit as in

#define ENABLE_T1  0x0001
#define ENABLE_T2  0x0002

and then these are used with the bitwise operators to turn bits on and off.

unsigned short usReg1 = GetRegister(REG1);

// disable T1 and enable T2

usReg1 &= ~ ENABLE_T1;
usReg1 |= ENABLE_T2;

or

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

or

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

or to test bits

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

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

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

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

A Note on Macros

By the way you need to make sure that you use parenthesis in macro definitions to isolate the arguments as much as possible for safety. Without using parenthesis to explicitly force a macro argument to be evaluated as a single expression, you will be at the mercy of the operator precedence rules and those may not always be what you want (see https://en.cppreference.com/w/c/language/operator_precedence ). So

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

really should be written as

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

For your example, since the bitwise shift operators are rather low on the precedence chart, it will probably be fine in most cases but with other macros, depending on operator precedence can lead to defects as you never know how conservative someone will be when using a macro.

like image 100
Richard Chambers Avatar answered Oct 04 '22 15:10

Richard Chambers


You could introduce a fullwidth mask which defines the bits you want to set. E.g. for setting the first 3 bits use 0xe0, which is in binary 11100000.

Along with that mask, provide the value to write also in full width.
E.g. 0xc0 for the desired binary 110 in the first three bits.

Then a little bit-operatino magic is needed to only write the desired bit positions.

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

Here ((0xff)^(mask)) inverts the mask, so that anding with this deletes the existing bits from the value.
((value)&(mask)) is the value but with only set bits in the desired positions, i.e. this prevents undesired setting of bits elsewhere.

In total, oring the two parts, gives you the desired result.

There is a design choice here, which I want to mention:
Call it paranoia. If somebody tells me "I want to set only the first three bits, to this value which has bits elsewhere", then I prefer to take it safe this way, i.e. removing the "elsewhere bits".
Yes, I am assuming that setting bits without intention is more of a risk than not setting them, which is a matter of taste.

like image 42
Yunnosch Avatar answered Oct 04 '22 16:10

Yunnosch