4

How do I write to a single bit? I have a variable that is either a 1 or 0 and I want to write its value to a single bit in a 8-bit reg variable.

I know this will set a bit:

reg |= mask; // mask is (1 << pin)

And this will clear a bit:

reg &= ~mask; // mask is (1 << pin)

Is there a way for me to do this in one line of code, without having to determine if the value is high or low as the input?

Lundin
  • 195,001
  • 40
  • 254
  • 396
tylerjw
  • 802
  • 4
  • 14
  • 28

6 Answers6

10

Assuming value is 0 or 1:

REG = (REG & ~(1 << pin)) | (value << pin);

I use REG instead of register because as @KerrekSB pointed out in OP comments, register is a C keyword.

The idea here is we compute a value of REG with the specified bit cleared and then depending on value we set the bit.

Community
  • 1
  • 1
ouah
  • 142,963
  • 15
  • 272
  • 331
  • This actually generates branchfree code with `GCC -O2` For restricting the value to {0,1} I'd suggest to use `... | ((!!value) << pin)` – wildplasser Sep 01 '13 at 19:22
  • @wildplasser for the general case I think this is highly compiler dependent. On some compilers I would be afraid using logical operators would actually *add* some branch code. – ouah Sep 01 '13 at 19:35
  • 1
    IMHO it would need loops on architectures that don't have bitshift instructions in hardware (DEC alpha didnot have them, IIRC), but the `reg = a | b;` itself does not imply a branch, obviously. – wildplasser Sep 01 '13 at 19:43
  • 1
    @wildpasser or MSP-430 which can bitwise shift only one bit at a time. – ouah Sep 01 '13 at 19:51
  • IIRC the first 8088 could not shift by the CX register yet. (or was it only the 6502?) – wildplasser Sep 01 '13 at 19:56
  • Because I'm using the MSP430 I'll test it to see if this is more expensive than branch statements. I didn't relize I could only shift one bit at a time. I like the solution though. The values for `(1 << pin)` could be stored in an array to speed things up. – tylerjw Sep 02 '13 at 08:30
  • @tylerjw note that if `pin` is a constant, `1 << pin` will be computed at compiler time by the compiler and not by the target. – ouah Sep 02 '13 at 12:04
  • @ouah Is that still true if the code is within a function that gets called with different values of pin? – tylerjw Sep 02 '13 at 16:27
  • @tylerjw if `pin` is a function parameter, this no longer holds as parameters are not C constants. – ouah Sep 02 '13 at 17:59
  • @tylerjw Did you ever find out if this is more expensive than branches ? – Tejas Kale Mar 18 '21 at 19:59
  • For me (on the msp430) this generated a bunch of machine code and the if statement with a branch generated less code. I couldn't figure out how to get a useful benchmark of this and ended up just using the if statements in the end sadly. – tylerjw Mar 30 '21 at 22:27
2

Because you tagged this with embedded I think the best answer is:

if (set)
    reg |= mask; // mask is (1 << pin)
else
    reg &= ~mask; // mask is (1 << pin)

(which you can wrap in a macro or inline function). The reason being that embedded architectures like AVR have bit-set and bit-clear instructions and the cost of branching is not high compared to other instructions (as it is on a modern CPU with speculative execution). GCC can identify the idioms in that if statement and produce the right instructions. A more complex version (even if it's branchless when tested on modern x86) might not assemble to the best instructions on an embedded system.

The best way to know for sure is to disassemble the results. You don't have to be an expert (especially in embedded environments) to evaluate the results.

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
  • 1
    for these microcontrollers, you will have to use the correct register to perform the reset and the correct one to perform the clear. Something like: `if ( set) reg_set = mask; else reg_clr = mask;`. Registers being defined as `volatile` objects, the compiler does not have the right to perform this optimization (=replacing `reg` with`reg_set / reg_clr`) itself. – ouah Sep 01 '13 at 20:06
  • Ouah gives good advise; you can't actually tell whether reg |= mask results in a bit set or a read-mofify-write, this is CPU and compiler dependant (I'd say that it is more likely to results in the later). In cases like this, always disassemble the code to see what actually happens. – Lundin Sep 04 '13 at 14:27
2

One overlooked feature of C is bit packing, which is great for embedded work. You can define a struct to access each bit individually.

typedef struct
{
    unsigned char bit0 : 1;
    unsigned char bit1 : 1;
    unsigned char bit2 : 1;
    unsigned char bit3 : 1;
    unsigned char bit4 : 1;
    unsigned char bit5 : 1;
    unsigned char bit6 : 1;
    unsigned char bit7 : 1;
} T_BitArray;

The : 1 tells the compiler that you only want each variable to be 1 bit long. And then just access the address that your variable reg sits on, cast it to your bit array and then access the bits individually.

((T_BitArray *)&reg)->bit1 = value;

&reg is the address of your variable. ((T_BitArray *)&reg) is the same address, but now the complier thinks of it as a T_BitArray address and ((T_BitArray *)&reg)->bit1 provides access to the second bit. Of course, it's best to use more descriptive names than bit1

DrRobotNinja
  • 1,381
  • 12
  • 14
  • +1 this is a very useful abstraction that I've seen used by embedded compiler vendors in driver code for peripherals – Morten Jensen Sep 01 '13 at 22:01
  • 1
    This is 100% non-portable and unsafe. Using bit fields is always a bad idea, particularly in embedded systems. What you call bit 7 in the above is not necessarily bit 7. It is not necessarily even allocated in the same byte as you hoped for. [More info here](http://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields/6044223#6044223). – Lundin Sep 04 '13 at 14:06
  • @Lundin If you disable padding, is it still bad idea? – MightyPork Dec 23 '14 at 15:08
  • For the most part the issues raised by @Lundin are implementation-specific. I agree with the 100% non-portable. The question remains: is your code required to be portable? A lot of my work is drivers for specific microcontrollers, where I look up how the compiler will pack bits, and it usually provides enough detail to guarantee the results I want. BUT ONLY FOR A PARTICULAR PROCESSOR! – DrRobotNinja Dec 24 '14 at 21:15
  • This is a risky way to access bits in registers. It might work for one version of a compiler with one set of compiler flags, but can easily break leading to hard to find bugs. – bobc Aug 14 '16 at 08:37
1
//Through Macro we can do set resset Bit
#define set(a,n)   a|=(1<<n);
#define reset(a,n) a&=(0<<n);
//toggle bit value given by the user
#define toggle(a,n)  a^=(1<<n);

int a,n;
int main()
{
     printf("Set Reset particular Bit given by User ");
     scanf("%d %d",&a,&n);
     int b =set(a,n)   //same way we can call all the macro
     printf("%d",b);
     return 0;
}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 19 '22 at 21:42
0

I think what you're asking is if you can execute a write instruction on a single bit without first reading the byte that it's in. If so, then no, you can't do that. Has nothing to do with the C language, just microprocessors don't have instructions that address single bits. Even in raw machine code, if you want to set a bit you have to read the byte it's in, change the bit, then write it back. There's just no other way to do it.

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
0

Duplicate of how do you set, clear, and toggle a single bit and I'll repost my answer too as no-one's mentioned SET and CLEAR registers yet:

As this is tagged "embedded" I'll assume you're using a microcontroller. All of the above suggestions are valid & work (read-modify-write, unions, structs, etc.).

However, during a bout of oscilloscope-based debugging I was amazed to find that these methods have a considerable overhead in CPU cycles compared to writing a value directly to the micro's PORTnSET / PORTnCLEAR registers which makes a real difference where there are tight loops / high-frequency ISR's toggling pins.

For those unfamiliar: In my example, the micro has a general pin-state register PORTn which reflects the output pins, so doing PORTn |= BIT_TO_SET results in a read-modify-write to that register.

However, the PORTnSET / PORTnCLEAR registers take a '1' to mean "please make this bit 1" (SET) or "please make this bit zero" (CLEAR) and a '0' to mean "leave the pin alone". so, you end up with two port addresses depending whether you're setting or clearing the bit (not always convenient) but a much faster reaction and smaller assembled code.

Community
  • 1
  • 1
John U
  • 2,886
  • 3
  • 27
  • 39