I have a circuit that uses D4-D7. I would like to set the state all D4-D7 in the same instruction without affecting D0-D3.
Example:
Current state is PORTD = 10010000
PORTD = 01000000 // Turn D6 on, Turn off D4 & D7
Another example:
Current state is PORTD = 01000000
PORTD = 10010000 // Turn D7 & D4 on, Turn off D6
The above instructions also have the side effect of acting on D0-3. I need whatever state D0-D3 are in to remain in that state when I set the state of D4-D7 simultaneously.
I think Bitwise operations are probably the answer, but after looking at it how they work, I'm still not sure how I can define both the on & off state of only specific bits within a byte, while also completely ignoring the other bits.
You can't. You'll at least have to (1) read PORTD, (2) apply a bitwise OR to it and (3) write back the result.
You can do it in one line of C code, but it will still be multiple instructions.
The question is of course: why does it have to be the same instruction? What is your application that apparently requires this kind of strict timing and are you sure you're using the right kind of hardware if you really have this requirement (which in all likelihood you don't, but you know, for sake of the argument...)
Should the interrupts be turned off when using "PORTD &= ~0b11110000;" if the other bits are changed in a interrupt ? I think so.
No one mentions the toggle command ?
If you know the output state, then all 4 pins can be changed at the same time with the toggle command. Write 0b10010000 to PIND when PortD is an output to toggle D7 and D4 at the same time. No disabling of interrupts are needed, it is a single write.
It's a read, followed by the AND in registers, followed by a write. Overall, not atomic at all. If it were a single bit being updated, you could get away with assuming it was atomic, as long as you're dealing with constant values for the port and bit.
(all assuming we're talking about an AVR (implied by "PORTD", right?) Other chips have other ways of updating parts of an IOPort.)
It would be write-only, and so atomicity wouldn't matter.
It would be two instructions (LDI 0xF0 into register, OUT register to PORTD), and there could be an interrupt in between the two instructions, but there aren't any circumstances where PORTD would wind up with a different value than expected.
Desired State: 00001010
Actual End State: 10010000
Is what we were looking for PORTD &= ~0b00001111;? With that, we get the following (I checked on a binary calculator, but please correct if I'm wrong):
Desired State: 00001010
Actual End State: 00001010
PORTD |= 0b11110000; // set bits 7-4, bits 3-0 unchanged
Actual End State: 11111010
I don't have a lot of experience with binary math, but I think the above works.
Using a temporary variable where the AND and OR are done makes the code slightly fasted by only accessing PORTD 2 times instead of 4, and prevents the intermediate state (bits cleared but not set yet) from possibly causing problems.
ATOMIC_BLOCK shuts out interrupts around the code segment...
000002d8 <loop>:
2d8: 9f b7 in r25, 0x3f ; read current interrupt status
2da: f8 94 cli ; shut off interrupts
2dc: 8b b1 in r24, 0x0b ; read portd
2de: 80 7f andi r24, 0x0F ; clear upper bits
2e0: 80 6c ori r24, 0xC0 ; or in new value
2e2: 8b b9 out 0x0b, r24 ; write portd
2e4: 9f bf out 0x3f, r25 ; restore previous interrupt state.
I've got an idea that is atomic and interrupt-safe. Use the special feature of PIND that toggles bits in PORTD where a 1 is written.
// Set 5 and 7, clear 4 and 6.
byte DesiredValue = 0b1010;
byte CurrentValue = PORTD;
byte ChangedBits = CurrentValue ^ (DesiredValue << 4);
ChangedBits &= 0b11110000; // Only change 4 through 7
PIND = ChangedBits; // Toggle the bits to the desired state.
For example, if PORTD is 0b11100111 the
DesiredValue = 0b1010;
ChangedBits will be 0b01000111 and then masked to 0b01000000.
Writing that to PIND will toggle bit 6 and result in the desired pattern of 0b1010. The bottom four bits will remain unchanged because no 1's were written there.
This can be reduced to: PIND = (PORTD ^ (DesiredValue << 4)) & 0b11110000;