Microcontroller coding style for bit assignments - Page 1
Forum section: electronics / general

author: tsmz
author_rank: Contributor
timestamp: June 18, 2011, 08:24:37 pm
content: There are quite a few coding styles for microcontrollers, especially when it comes to setting bits in registers. While they are all functionally and computationally equivalent (the compiler will most likely take care of replacing everything with constants), they do differ in style quite a lot.I was wondering on the advantages and disadvantages of the various styles and what people use and why.Style 1:BITx definitionsCode:#define BIT1 0x01#define BIT2 0x02#define BIT3 0x04#define BIT4 0x08#define BIT5 0x10#define BIT6 0x20#define BIT7 0x40#define BIT8 0x80// LED on Port1.5#define MY_LED BIT6// program codeP1OUT |= BIT3;P1OUT ^= MY_LED;Style 2:BitshiftsCode:// LED on Port1.5#define MY_LED 5// program codeP1OUT |= (1<<3);P1OUT ^= (1<<MY_LED);Style 3:Direct usage of constantsCode:// LED on Port1.5#define MY_LED 0x20;// program codeP1OUT |= 0x04;P1OUT ^= MY_LED;Are there any other styles? What do you use and why? What should one use? I've seen several styles in code so far. I'll keep back my own preference until later to not give a preference from the start.

author: Bored@Work
author_rank: Super Contributor
timestamp: June 18, 2011, 09:00:11 pm
content: Quote from: tsmz on June 18, 2011, 08:24:37 pmThere aren't any technical advantage or disadvantages of the various styles.The only reason to use a particular style is because it is the common style on the particular platform you are working on. The value in using the platform's typical style is in consistency on that platform. Which in turn contributes to maintainability.If you see someone imposing his one personal style on every platform, instead of using the platform's normal style, you know you are dealing with one of those annoying jokers who have no clue and make a marginal issue (the style) a matter of life and death.

author: Psi
author_rank: Super Contributor
timestamp: June 19, 2011, 02:09:02 am
content: i use style 2.Only because when i learned avr's that's what all the examples used.

author: westfw
author_rank: Super Contributor
timestamp: June 19, 2011, 04:22:04 am
content: fourth style: C bitfields:Code:struct ioport {bit0:1;bit1:1;bit2:1;bit3:1;bit4:1;bit5:1;bit6:1;bit7:1;} *porta = (0x123);porta->bit0 = 1;porta->bit7 = 0;Fifth style: non standard compiler extensions:Code:PORTA.5 = 1;// orbit statbit @ PORTA.4;statbit = 1;In general, you're at the mercy of the manufacturer or compiler vendor for the constants in include files for the CPUs in use, and should stick with similar structures for bits within user defined structures/etc. (although they may come with their own standards.) This may be influenced by whether the particular CPU architecture has instructions that have arguments that are bit numbers or not. PIC and AVR (and many microcontrollers) feature such instructions, but other CPUs (say, the x86 and MSP430) do not.

author: tsmz
author_rank: Contributor
timestamp: June 19, 2011, 01:20:10 pm
content: Compilers will calculate the value of the bitshift and write a constant in the code. So (1<<3) is really equivalent to 0x04. Styles 1 to 3 are equivalent, you won't find a compiler that messes up these things.Style 4 is quite elegant, but I wouldn't be so sure if all compilers will do the right thing.By the way, i = i + 1 and i += 1 are identical and will thus most likely be optimized in the same way by a decent compiler. Compilers are quite good today and will, if you don't mess up their configuration, compute constant expressions at compile time.Personally, I use style 2 because I think it's the clearest way to express things. Also, numbering is absolutely obvious with this. Since the BITx definitions usually start with BIT1, but ports are numbered from 0 to y, PORT1.0will actually be set by P1OUT |= BIT1, which I think is slightly ugly and confusing. It's much nicer and clearer to use P1OUT |= (1<<0), even if it doesn't look as obvious at first.

author: Unknown
author_rank: Unknown
timestamp: June 19, 2011, 04:50:42 pm
content: Agreed, any decent compiler will optimize arithmetic with only constants, like 1+1 or 1<<3. The issue with bitfields is that there is no efficient way to do something like PORTA |= (1<<3) & (1<<1), which is just a single OR.

author: westfw
author_rank: Super Contributor
timestamp: June 20, 2011, 06:59:26 am
content: In the bad old days, compilers might have generated better code for "i += 1" than for "i = i+1"In modern times, if your compiler does crap like this, it is time to look for another compiler.(ditto if various forms of infinite loop take different times, or it doesn't compiler "v |= bit" to the appropriate single instruction when it ought to be able to do so.)

author: codepinger
author_rank: Newbie
timestamp: June 26, 2011, 11:13:58 pm
content: Quote from: alm on June 19, 2011, 04:50:42 pmCorrect me if I'm wrong, but (1<<3) & (1<<1) would equal zero, and thus no change to PORTA.Perhaps the "&" should be a "|" ?

author: Unknown
author_rank: Unknown
timestamp: June 27, 2011, 12:22:03 am
content: You're of course correct. So a smart compiler may actually optimize it zero instructions.

author: ejeffrey
author_rank: Super Contributor
timestamp: June 27, 2011, 08:13:53 pm
content: Except that memory mapped IO should be declared volatile and the compiler will not modify any access to it. The compiler will still do constant folding in the first three examples and generate the same code. However, with bitfield access each statement always causes a write. Even a 'no-op' statement that adds zero to a register shouldn't be optimized away.

author: Unknown
author_rank: Unknown
timestamp: June 27, 2011, 09:03:41 pm
content: The or statement will probably be optimized away, the load/store not.

author: westfw
author_rank: Super Contributor
timestamp: June 28, 2011, 08:17:59 pm
content: There are some anti-optimizations to watch out for. For example, I often see code like:Code:sbi(ADCSRA, ADPS2);sbi(ADCSRA, ADPS1);sbi(ADCSRA, ADPS0);Where sbi is defined as something so that these become ADCSRA |= 1<<ADPS2 and similar. The compiler would hapily optimize these to single sbi instructions (Atmel AVR "set bit in IO register")IFit were possible to do so. But the compiler, and use of high-level expressions, end up hiding the fact that the registers being set aren't in the IO address space (I don't know if that's true of these particular registers, BTW. It's just an example I pulled of a github page.) So the "sbi" instruction becomes a load/or immediate/store sequence. And since the register is volatile, it will do that separately for each statement. What you expected to be three instructions (6 bytes) is now 9 instructions and 30 bytes. Ouch! Suddenly "ADCSRA |= (1<<ADPS2)+(1<<ADPS1)+(1<<ADPS0)" would have been better, and you probably should have used "ADCSRA=(1<<ADPS2)+(1<<ADPS1)+(1<<ADPS0)"But it's hard to notice unless you're really paying attention!

author: Unknown
author_rank: Unknown
timestamp: June 28, 2011, 09:41:29 pm
content: I believe GCC will automatically convert FOO |= (1<<BAR) to sbi if it's a single bit and it's in the IO address space, so there's no point in using the sbi/cbi macros anymore (which is why they're deprecated).

author: Mechatrommer
author_rank: Super Contributor
timestamp: June 29, 2011, 05:48:21 am
content: beside compiler optimization in mind, how about maintainability? how about let say you finished up with your code for a particular uC, and then later decided to rewire the pin and change which pin to light up led etc, or the uC become obsolete and you are forced to use new pin/bit assignment on newer chip. with style 2, if you have hundreds of fix constant bit operations, changing all those is a certain pain in the arse (and in style 3 line P1OUT |= 0x04;). with #define (style 1), all you need is to change those definitions. but surely will not 100% applicable, you may as well forced to re-code all over again, depending on your particular case. but prevention in the first place could save you something later. my 2cnts.

author: TheDirty
author_rank: Frequent Contributor
timestamp: June 29, 2011, 12:46:42 pm
content: Quote from: Mechatrommer on June 29, 2011, 05:48:21 amAm I missing something? All of these methods, except 4 use defines for the constants, so any of them would just require a change to the #define statements.I generally like #2 since the numbers are more easily translated to the bits 0 thru 7 rather than 1 thru 8. I work with 3 uC cores and flip code back and forth with method 1, 2, and 3 without issue. I've avoided bitfields as I've worked with iffy compilers that just do not handle them properly.