# Why does AVR code use bit shifting [closed]

In AVR programming, register bits are invariably set by left-shifting a 1 to the appropriate bit position - and they're cleared by a ones' complement of the same.

Example: for an ATtiny85, I might set PORTB, b4 like this:

PORTB |= (1<<PB4);


or clear it like this:

PORTB &= ~(1<<PB4);


My question is: Why is it done this way? The simplest code ends up being a mess of bit-shifts. Why are bits defined as bit positions instead of masks.

For instance, the IO header for the ATtiny85 includes this:

#define PORTB   _SFR_IO8(0x18)
#define PB5     5
#define PB4     4
#define PB3     3
#define PB2     2
#define PB1     1
#define PB0     0


To me, it would be much more logical to define the bits as masks instead (like this):

#define PORTB   _SFR_IO8(0x18)
#define PB5     0x20
#define PB4     0x10
#define PB3     0x08
#define PB2     0x04
#define PB1     0x02
#define PB0     0x01


So we could do something like this:

// as bitmasks
PORTB |=  PB5 |  PB3 |  PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;


to turn bits b5, b3, and b0 on and off, respectively. As opposed to:

// as bit-fields
PORTB |=  (1<<PB5) |  (1<<PB3) |  (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);


The bitmask code reads much more clearly: set bits PB5, PB3, and PB0. Furthermore, it would seem to save operations since the bits no longer need to be shifted.

I thought maybe it was done this way to preserve generality in order to allow porting code from an n-bit AVR to an m-bit (example 8-bit to 32-bit). But this doesn't appear to be the case, since #include <avr/io.h> resolves to definition files specific to the target microcontroller. Even changing targets from an 8-bit ATtiny to an 8-bit Atmega (where bit definitions change syntactically from PBx to PORTBx, for example), requires code changes.

• I second this. Even utilizing the ubiquitous _BV(b) instead of (1<<b) makes things needlessly messy. I typically define bit mnemonics with _BV(), e.g. #define ACK _BV(1). Commented Feb 21, 2018 at 22:04
• Once it's realized that the compiler will interpret these as the same constant, which to use in source code is really a matter of preference. In your own code do whatever you think is wisest; in modifying existing projects, stick to their traditions. Commented Feb 21, 2018 at 22:42
• "Since a bitmask approach would clearly yield more readable end-user code" - your personal opinion. I find it far clearer shifting 1's and 0's to the right place than having to guess whether several numbers being added together are bitmasks or not. Commented Feb 21, 2018 at 23:36
• @TomCarpenter Interesting. Well, maybe I inadvertently asked an opinion-based question. Either way, there's been some good feedback. Coming from more of a (TI) DSP background (where bitmask is the norm), it just seemed like such odd syntax that I figured there was some concrete reason for it. Commented Feb 22, 2018 at 0:16
• @BlairFonville Maybe you already know this, but ARMs works exactly like you describe (with bitmasks).
– Chi
Commented Feb 22, 2018 at 6:29

The simplest code ends up being a mess of bit-shifts. Why are bits defined as bit positions instead of masks.

No. Not at all. The shifts are only in the C source code, not in the compiled machine code. All the examples you showed can and will be resolved by the compiler at compile time because they are simple constant expressions.

(1<<PB4) is just a way to say "bit PB4".

• So it not only works, it does not create more code size.
• It makes also sense for the human programmer to name the bits by their index (e.g. 5) and not by their bit mask (e.g. 32) because this way consecutive numbers 0..7 can be used to identify the bits instead of awkward power of two (1, 2, 4, 8, .. 128).

• And there is another reason (maybe the main reason):
The C-header files can not only be used for C-code but also for assembler source code (or assembler code inlined in C source code). In AVR assembler code you definitely not only want to use bit masks (which can be created from indices by bit shifting). For some AVR bit manipulation assembler instructions (e.g. SBI, CBI, BST, BLD) you have to use bit indices as immediate operator in their instruction op code.
Only if you identify bits of SFRs by indices (not by bit mask) you can use such identifiers directly as the assembler instructions immediate operand. Otherwise you had to have two definitions for each SFR bit: one defining its bit index (which can be used e.g. as operand in aforementioned bit manipulating assembler instructions) and one defining its bit mask (which can only be used for instructions where the whole byte is manipulated).

• I understand that. I’m not questioning whether it works or not. I know it does. I’m asking why the definitions are written as they are. To me it would greatly improve code readability if they were defined as masks instead of bit positions. Commented Feb 21, 2018 at 21:47
• I think this answer misses the point. He never talks about code efficiency or compiler. It's all about source code clutter.
– pipe
Commented Feb 21, 2018 at 22:48
• @Blair Fonville: there is no easy way to define such a macro. It needed to calculate the logarithm to base 2. There is no preprocessor functionality calculating the logarithm. I.e. it only could be done using a table and that, I think, would be a very bad idea.
– Curd
Commented Feb 21, 2018 at 22:50
• @pipe: I don't talk about it because I just don't consider it as "code pollution" or "source code clutter" (or whatever you want to call it). On the contrary I think it's even useful to remind the programmer/reader that the constant he is using is a power of two (and that is done by using the shift expression).
– Curd
Commented Feb 21, 2018 at 22:52
• @RJR, Blair Fonville: of course it is easily possible to define such macros BUT while using simple preprocessor defines is ok I'd avoid preprocessor macros (aka preprosessor functios) whenever possible because they can make debugging (stepping through C source code with the debugger) extremely intransparent.
– Curd
Commented Feb 22, 2018 at 8:14

Perhaps bit shifting is not the only use case for the PB* definitions. Perhaps there is another use cases where where the PB* definitions are used directly rather than as shift amounts. If so then I believe the DRY principle would lead you to implement one set of defines that can be used for both use cases (like these PB* defines) rather than two different sets of defines that have repetitive information.

For example, I wrote an application that can take measurements from up to 8 ADC channels. It has one interface to start a new measurement in which you can specify multiple channels via an 8-bit field, one bit for each channel. (The measurements are performed in parallel when multiple channels are specified.) Then it has another interface that returns the measurement results for an individual channel. So one interface uses the channel number as a shift into a bit-field and the other interface uses the channel number directly. I defined a single enumeration to cover both use cases.

typedef enum
{
CHANNEL_XL_X = 0,
CHANNEL_XL_Y = 1,
CHANNEL_XL_Z = 2,
CHANNEL_G_X = 3,
CHANNEL_G_Y = 4,
CHANNEL_G_Z = 5,
CHANNEL_AUX1 = 6,
CHANNEL_AUX2 = 7
} ChannelNum;

struct MeasurementResult;