C Syntax to Change Multiple Bits

Status
Not open for further replies.

tlb

Well-known member
Is there any C syntax to change multiple bits on a port while leaving the others the same?

I would like to do something like

PORTD.[7:6] = 0b01;

i.e.

PORTD.7=0;
PORTD.6=1;

in a single statement while leaving bits 5-0 unchanged.

I know how to set and clear individual bits, or to write the entire port, but can't find syntax for changing a subset of the bits. Is the only option to read the port, modify the bits I want to change, and write the entire port back?

This is for the Teensy 2.0 with the avr-gcc compiler.

Thanks,

tlb
 
Is there any C syntax to change multiple bits on a port while leaving the others the same?

I would like to do something like

PORTD.[7:6] = 0b01;

i.e.

PORTD.7=0;
PORTD.6=1;

in a single statement while leaving bits 5-0 unchanged.

I know how to set and clear individual bits, or to write the entire port, but can't find syntax for changing a subset of the bits. Is the only option to read the port, modify the bits I want to change, and write the entire port back?

This is for the Teensy 2.0 with the avr-gcc compiler.

Thanks,

tlb
Assuming that that the bits start at the bottom bit, you would do:

Code:
PORTD |= (1 << 7) | (1 << 6);                                        // set both bits
PORTD &= ~((1 << 7) | (1 << 6));                                  // clear both bits
PORTD = (PORTD & ~(1 << 7)) | (1 << 6);                     // set bit 7 and clear bit 6
 
A hidden gotcha with read-modify-write on I/O ports is interrupts that also manipulate any of the other bits on the same port. If the interrupt occurs between the time you read the port and write the new value, when you write your changed byte, you'll overwrite any changes the interrupt's code made.

On AVR, there are 2 special instructions that set or clear a single bit within a port. The compiler automatically uses then when you write "PORT |= value" or "port &= value", where the value causes only a single bit to change. Because they're a single instruction, the read-modify-write can't be interrupted. But if you change more than 1 bit, or if you use pointers or other stuff that the compiler can't optimize to the single instruction, then interrupts can be a problem.

To get a feel for how subtle and difficult to diagnose these types of problems can be, check out Arduino issue #146. This bug existed within Arduino for years. It was reported in November 2009 and finally fixed in June 2010, but if you read the comments, clearly several people had encountered strange problems for quite some time, but never managed to understand why. The Servo library would create glitchy output when the LiquidCrystal library was used more aggressively, but the bug was not in either library. It was actually within digitalWrite(), because LiquidCrystal was changing the pins so frequently that the odds of sometimes having Servo's interrupt occur at just that wrong moment became more likely.

On ARM, there are 2 ways these problems are managed. Like most modern microcontrollers, the MK20 on Teensy3 has special port register that sets any number of the port's bits, and another which clears bits. You just write to the register (a single instruction) and the hardware inside the port does the job of modifying the bits. The ARM Cortex-M4 also has a "bitband" feature, where a special region of memory is set aside for 1 bit modification of other registers. You write a 1 or 0 to a 32 bit location (the upper 31 bits are ignored). Special hardware inside the chip reads the intended memory or register, changes just 1 bit, and writes it back. The hardware does this as a single bus operation all within that 1 instruction, so it can't be interrupted between the read and write.

My long-winded point is you must be careful, either on AVR or ARM, when doing any read-modify-write operations on registers that are likely to be used by other code in interrupts. It's easy to craft buggy code that appears to work flawlessly. Then later you add code or a library which uses the port from an interrupt, and extremely difficult to reproduce and diagnose problems begin.
 
Status
Not open for further replies.
Back
Top