Putting a value in a bit location

This code works to read the state of pin 2 on the ATmega328P and store it in bit 4 of a register, and read the state of pin 3 and store it in register bit 3

  if (PIND & (1 << 2)) { // if pin D2 high
    GPIOR1 |= (1 << 4);  // set bit 4 of register
  }
  else {
    GPIOR1 &= ~(1 << 4); // clear bit 4 of register
  }
  // and in some other part of the program...
  if (PIND & (1 << 3)) { // if pin D3 high
    GPIOR1 |= (1 << 3);  // set bit 3 of register
  }
  else {
    GPIOR1 &= ~(1 << 3); // clear bit 3 of register
  }

Is there a more efficient way to do this sort of thing, put the value of a bit in a location without disturbing the other bits? In the various references and tutorials I have read such as this one they talk about setting and clearing bits as separate operations, but I do not find if there is a way to simply write the value of a bit into a bit location, regardless of whether it is 0 or 1 that I’m writing.

I prefer to use direct port and register manipulation instead of Arduino abstractions for the project I am working on. I’m editing a bootloader and compiling with avr-gcc.

What you are doing is correct. Standard AVR based Arduinos do not have a machine instruction to store a single bit in a general register. Some other uPs do have such an instruction.

Arduino has the bitset and bitclear instructions, but the way you do it is efficient. That's the way I do it.

jremington:
Standard AVR based Arduinos do not have a machine instruction to store a single bit in a general register.

How about sbi and cbi?

Description:

Sets a specified bit in an I/O register. This instruction operates on the lower 32 I/O registers - addresses 0-31.

Not a general register.

There are only those 32 general registers, which registers are you talking about?

If doing it in assembly isn't an issue the BLD and BST instructions may be of interest - check the instruction set reference.

Shouldn’t need the if statements, if I have the logic correct this should do the same as your code:

//clear bit 4 in register, then set to bit 2 from port D
  GPIOR1 = (GPIOR1 & ~(1 << 4)) | ((PIND & (1 << 2)) << 2);
  //clear bit 3 in register, then set to bit 3 from port D
  GPIOR1 = (GPIOR1 & ~(1 << 3)) | (PIND & (1 << 3));

A "general register" would include registers numbered 0-31 and others that are not bit-addressable on AVR processors.

registers.png

registers.png

Ol, I stand corrected. sbi and cbi work only on the first half of the IO registers.

I was misled by the addresses and overlooked the I/O

Clears a specified bit in an I/O register. This instruction operates on the lower 32 I/O registers - addresses 0-31.

These work on the first 32 bytes

dougp:
If doing it in assembly isn't an issue the BLD and BST instructions may be of interest - check the instruction set reference.

What are about these?

bitWrite(GPIOR1, 4, digitalRead(2));
bitWrite(GPIOR1, 3, digitalRead(3));

david_2018:
Shouldn’t need the if statements, if I have the logic correct this should do the same as your code:

//clear bit 4 in register, then set to bit 2 from port D

GPIOR1 = (GPIOR1 & ~(1 << 4)) | ((PIND & (1 << 2)) << 2);
 //clear bit 3 in register, then set to bit 3 from port D
 GPIOR1 = (GPIOR1 & ~(1 << 3)) | (PIND & (1 << 3));

Yes, I see. Although not as human readable, it is a sleeker way and 4 fewer bytes of instruction in flash.

GolamMostafa:
What are about these?

bitWrite(GPIOR1, 4, digitalRead(2));

bitWrite(GPIOR1, 3, digitalRead(3));

The guy is looking for an efficient method (whatever that means), not necessarily a platform-agnostic, easily readable and maintainable method

GolamMostafa:
What are about these?

bitWrite(GPIOR1, 4, digitalRead(2));

bitWrite(GPIOR1, 3, digitalRead(3));

Yes, those require Arduino functions, so they don't work when compiling the bootloader with avr-gcc. By efficiency, I just mean I want to not generate unnecessary instructions. I don't need to worry about every byte of the code, I just want to be mindful of doing things the correct way. I wanted to make sure I was not missing out on using an efficient bit value write (which it appears is not available in AVR).

Thanks folks, I appreciate the education. So, in summary, there are 2 ways:

  1. use if-then-else to decide and take action to either clear or set the bit, or
  2. just clear the bit and then set it (or not) according to the value I'm writing in the bit location (david_2018's way).

Assembly version of if-else structure:

sbis  $09, 2          ;skip next instruction if bit-2 of PIND Register is found Set
rjmp LX
sbi    $1E, 4          ;set bit-4 of GPIOR0 Register
rjmp LY
LX: cbi    $1E, 4    ; clear bit-4 of GPIOR0 Register
LY: ____

Thanks, that looks fairly simple.

On the ATmegaxx8 series and some others, three GPIORx registers are available for general purpose storage and don't seem to be used by much, if any Arduino software. Of the three, the bit set/clear instruction works only on GPIOR0.