NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Interesting code structure.

April 17, 2011
by Hexorg
Hexorg's Avatar

Hey guys, I've been researching some piece of code I need, and I found this German guy who has very interesting ways of doing the bit-wise arithmetic, so I decided to post this tricks here for your help and comments.

First of all, many of you already use this one, but not all:

    PORTB |= _BV(PB1); // instead of PORTB |= (1>>PB1);

Reason for this I found in avr-libc: "The bit shift is performed by the compiler which then inserts the result into the code. Thus, there is no run-time overhead when using _BV()."

Second, is a way to do "LED = 1" to make just one LED turn on or off:

    struct bits {

        char b0:1;

        char b1:1;

        char b2:1;

        char b3:1;

        char b4:1;

        char b5:1;

        char b6:1;

        char b7:1;

    } __attribute__((__packed__));

    #define PORTA_0 ((*(volatile struct bits*)&PORTA).b0)

    #define PORTA_1 ((*(volatile struct bits*)&PORTA).b1)

    #define PORTA_2 ((*(volatile struct bits*)&PORTA).b2)

    #define PORTA_3 ((*(volatile struct bits*)&PORTA).b3)

    #define PORTA_4 ((*(volatile struct bits*)&PORTA).b4)

    #define PORTA_5 ((*(volatile struct bits*)&PORTA).b5)

    #define PORTA_6 ((*(volatile struct bits*)&PORTA).b6)

    #define PORTA_7 ((*(volatile struct bits*)&PORTA).b7)

After that, if your led is on, say PORTA, 3rd bit, you do #define LED PORTA_3, and now you can assign 1 or 0 to LED to turn it off or on.

And third, is a way to convert 32-bit integer into four 8-bit integers, without getting confused with bit-shifts:

    typedef union conver_ {

        unsigned long dw;

        unsigned int w[2];

        unsigned char b[4];

    } CONVERTDW;

    typedef union conver2_ {

        unsigned int w;

        unsigned char b[2];

    } CONVERTW;

From what I understand, a "union" means that all the variables in this "structure" have the same beginning address, so if you write 0xAADD into CONVERTW.w, then CONVERTW.b[0] will be 0xDD, and CONVERTW.b[1] will be 0xAA

April 17, 2011
by Noter
Noter's Avatar

_BV() is a compiler macro defined as #define _BV(bit) (1<<(bit)) in <avr/sfr_defs.h> so it results in exactly the same code as (1<<PB?). I like _BV though and use it all the time.

I've worked for a while on coming up with preprocessor directives that make easy port manulipation. My goal was to define the pin usage once and not have to deal with the control registers separately. This is where I'm at on it and I think it's mostly done:

// preprocessor macros for port/pin manulipulation
//
#define INPUT2(port,pin) DDR ## port &= ~_BV(pin) 
#define OUTPUT2(port,pin) DDR ## port |= _BV(pin) 
#define CLEAR2(port,pin) PORT ## port &= ~_BV(pin) 
#define SET2(port,pin) PORT ## port |= _BV(pin) 
#define TOGGLE2(port,pin) PORT ## port ^= _BV(pin) 
#define READ2(port,pin) ((PIN ## port & _BV(pin))?1:0)
//
#define INPUT(x) INPUT2(x) 
#define OUTPUT(x) OUTPUT2(x)
#define CLEAR(x) CLEAR2(x)
#define SET(x) SET2(x)
#define TOGGLE(x) TOGGLE2(x)
#define READ(x) READ2(x)
#define PULLUP_ON(x) INPUT2(x); SET2(x)
#define PULLUP_OFF(x) INPUT2(x); CLEAR2(x)

Setup your pins like this using port letter and pin number:

// define ports, pins
//
#define LED_0       C,5
#define SW_1        C,4

Then us the macros for pin I/O:

OUTPUT(LED_0); // make output
SET(LED_0); // set to high
PULLUP(SW_1); // make input with pullup enabled
IF(READ(SW_1)==0){...}; // if low ...
April 17, 2011
by Rick_S
Rick_S's Avatar

Noter,

Isn't that similar to what arduino does... create a bunch of defines that simplify the context of the program by separating it another layer from the underlaying c source and programmer? While I can understand how these "shortcuts" could be handy in code you write for yourself, I think they are something that should be left up to an individual to include as they see need in their own code... like you have. Otherwise, they may as well go to an arduino environment and really not know what is going on in the background without doing some major digging.

Rick

April 17, 2011
by Noter
Noter's Avatar

I haven't looked at the arduino environment so I can't comment on it but I found several examples on the web that are similar with only one layer of substitution. With a second layer (or pass of the preprocessor) a single definition like "C,3" is split for use with the port/pin register resulting in minimum lines of code in the main program. I just posted my defs for comparision with the german's method which I think is the more complex of the two.

C is full of definitions to simplify things for us. Take a look at just about any of the system includes and you'll see lots of them. The preprocessor is a powerful feature of the C language that we use all the time knowing or not. As I recall there are only about a dozen specific functions of the preprocessor and the most well known is simple substutition followed by perhaps macro definitions. Less known is the ## for concatenation.

Here is the doc on preprocessor directives if you are interested:

The C Preprocessor

April 17, 2011
by Rick_S
Rick_S's Avatar

Quote


C is full of definitions to simplify things for us.


You are very correct, and I'll admit, I add some myself at times. I know that without definitions in the "standard" indludes, we couldn't use common replacements such as PORTx or PINx or Pxx, etc... Those are all pretty well known in the avr world. I apologize for coming off the way I did, I didn't mean any disrespect by it. After I re-read what I posted, I did sound like an A$$. I can see in reading your posts that you have a good programming knowledge that someday I hope to have as well.

Finally, I want to thank both you and Hexorg for the informative post, Didn't mean to hijack...

Rick

April 17, 2011
by Noter
Noter's Avatar

Hi Rick, no problem here but thanks for the kind words.

April 18, 2011
by bretm
bretm's Avatar

I find the "volatile bit field" approach very useful when I need to set more than one pin at once in the same port. The code to read the port value, mask off the bits that aren't changing, shifting the values to the right places, OR'ing them in, and setting the new port value, is a lot simpler when you use the bit field technique assuming you do it enough times in your program to make the overhead worthwhile.

The most important thing about it, to me, is that the avr-gcc compiler generates smaller and faster machine code for complicated bit-manipulating operations than when I write the equivalent manipulations in C using shifts and ANDs and ORs. The optimizations it comes up with are pretty surprising.

April 19, 2011
by BobaMosfet
BobaMosfet's Avatar

HexOrg-

The bit-field/structure definition is a very old trick. I've used it a lot, and actually was pleasantly surprised to see someone find it again. It actually does something else no one today is aware of- it gets you around architecture issues.

In C, depending on processor and compiler, structures can be 'padded'. That means, extra bytes are added between fields and or at the ends of fields, in order to allow the processor to manage the memory in word-size chunks more easily (address alignment). Such tricks as above, allow you to get around that on different architectures without pain.

Secondly, the 'volatile' keyword is a directive that essentially tells the mcu to always use a register to hold the variable, and not put it on the stack, for performance. It's called 'volatile' because as soon as that register is used for something else, that value is gone- don't count on it hanging around.

I also saw 'union' mention in here. A union is really useful when you need to look at one single piece of data (a variable or even a 'aggregate variable' (a whole structure)) in two different ways, depending on the need of the moment.

BM

April 19, 2011
by Hexorg
Hexorg's Avatar

BobaMosfet, thanks for detailed information! Now that you mentioned padding data with zeroes, that reminded me of how I was writing a game once in Delphi for Windows, and it annoyed me a lot that a Boolean variable in a structure took a whole 8 bits, essentially "wasting" 7 bits of memory. I understand, when you have 85 899 34 592 bits of RAM (1Gb), wasting 7 doesn't seem like a big deal, but I'm a minimalistic-kind of person.

Anyway, now that I found this packed type structure, making flags will be so much easier!!!

April 19, 2011
by Noter
Noter's Avatar

I don't remember having ever used bitfields and I still don't see the advantage. Maybe a little code example would drive it home for me if one of you wouldn't mind posting one. Thanks, Paul

April 19, 2011
by Hexorg
Hexorg's Avatar

To me, it simplifies the use of bit access, for example, when you need to read isNewGame flag, which is the 12th bit of a 16-bit variable called status, you'd need

if (status & (1<<12))>>12) { }

which is not that complex, once you learn what and how it works. But doesn't this look much easier when you do

struct gameFlags {
    char b0:1;
    char b1:1;
    char b2:1;
    // ... //
    char isNewEnemy:1;
    char isGamePaused:1;
    char isNewGame:1;
    char isAbleToFly:1;
    char isDead:1;
} __attribute__((__packed__));

struct GameFlags status;

if (status.isNewGame) { }
April 19, 2011
by Hexorg
Hexorg's Avatar

And according to bretm, the latter not only looks better but is more efficient:

"The most important thing about it, to me, is that the avr-gcc compiler generates smaller and faster machine code for complicated bit-manipulating operations than when I write the equivalent manipulations in C using shifts and ANDs and ORs. The optimizations it comes up with are pretty surprising."

April 19, 2011
by bretm
bretm's Avatar

it gets you around architecture issues

Not really. Since the C language specification doesn't completely specify how bit-field packing is actually implemented, you're still at the mercy of the compiler. The order of bits within a storage unit and the behavior of bit-fields that straddle storage units are implementation-defined. Luckily for us, avr-gcc for 8-bit processors implements this in a desirable way.

April 19, 2011
by bretm
bretm's Avatar

I would never do

if (status & (1<<12))>>12) { }

in the first place. I would instead do

if ((status >> 12) & 1) { }

so that the "12" is only specified in one place.

And according to bretm, the latter not only looks better but is more efficient

It's not a blanket statement--sometimes the compiler can optimize both ways just as well. If you want to maximize performance you have to look at how the compiler optimizes each case. But in my experience so far I've seen better code come out of the packed bit-field approach than the shift-and-mask approach because some types of shifts produce inefficient shifting loops.

April 19, 2011
by Noter
Noter's Avatar

Thanks Hexorg, I see now there is a performance advantage to using bitfields if your bitmap is greater than 8 bits in size. The mask/shift method loads all the bytes in the field while the bitfield struct loads only the byte containing the target bit.

Hey bretm, Can you give an example of setting more than one pin at once in the same port using a "volatile bit field". I don't think I understand yet how you would do that.

Thanks, Paul

April 19, 2011
by bretm
bretm's Avatar

The mask/shift doesn't necessarily move all the bits, or even any bit. For example in the case where you test the 12th bit using shifts in C and use it in an if statement, the compiler optimizes it into a single bit test against the upper byte, with no shifting at all.

Example of setting multiple bits...setting 4 bits to write to the LCD in 4-bit mode, assuming the four bits are PB5 through PB2:

struct bits2b4 {
    char : 2; // padding
    char data : 4;
};

#define LCD_DATA (((volatile struct bits2b4*)&PORTB)->data)
April 20, 2011
by Noter
Noter's Avatar

For bit manulipation of I/O pins I like the shift/mask method a little better. One thing I try to accomidate in my code is that I may have to move pins around at PCB layout and I prefer to make that as simple as possible. Ideally to swap pins only two simple defines would require change in the code even if ports are different. I like the multiple bit method using bitfields but I may stay clear of it for I/O because it is not flexible concerning pin assignment.

For static bitmaps, particularily those with more than 8 bits, there is a clear performance advantage to using bitfields and use seems easier too. I like the ability to set or read the bit value in a simple assign statement like NEW_GAME=1 or x=NEW_GAME vs shift/mask SET(NEW_GAME) or x=READ(NEW_GAME).

This is a good thread, thank you all.

April 20, 2011
by bretm
bretm's Avatar

The shifting/masking is easy for me, which is why I usually use it. For new programmers, not so much. Shifting and masking seems hard for people to understand. But so is casting to a volatile pointer and dereferencing a structure pointer, so it's a lose-lose situation for hobby programmers in this regard.

If avr-libc defined the macros with this functionality in mind in the first place, things could have been much nicer. Instead of "ADIE" being equal to a bit position within the ADCSR register, it could have been a direct volatile reference to that bit field within that register, and you could just say "ADIE = 1" to enable the ADC interrupt.

But using shifts and masks doesn't really make the code any easier to maintain, compared to bit fields. In the 4-bit data example above, to set bits PB5 through PB2 would require doing

PORTB = (PORTB & ((1<<PB7)|(1<<PB6)|(1<<PB1)|(1<<PB0))) | ((x & 0xF) << PB2);

I would argue that that line of code is harder to update (if your pinout changes) than the bit-field structure. And it's much less clear what is going on, namely that it is setting PB5 through PB2 to the 4-bit value "x".

April 20, 2011
by Noter
Noter's Avatar

That's not quite what I had in mind for ease of maintenance. I have a LCD interfaced via 8 bits of PORTD. It's working fine but now I realize I need to switch from SPI to the USART for external communication so PD0 and PD1 must be reassigned for that to happen. Here's the code using my preprocessor macros above:

#define LCD_P0      D,0
#define LCD_P1      D,1
#define LCD_P2      D,2
#define LCD_P3      D,3
#define LCD_P4      D,4
#define LCD_P5      D,5
#define LCD_P6      D,6
#define LCD_P7      D,7

void data_init(void){
    OUTPUT(LCD_P0);
    OUTPUT(LCD_P1);
    OUTPUT(LCD_P2);
    OUTPUT(LCD_P3);
    OUTPUT(LCD_P4);
    OUTPUT(LCD_P5);
    OUTPUT(LCD_P6);
    OUTPUT(LCD_P7);
}

void data_out(uint8_t value){
    if(value&1){SET(LCD_P0);}else{CLEAR(LCD_P0);}
    if(value&2){SET(LCD_P1);}else{CLEAR(LCD_P1);}
    if(value&4){SET(LCD_P2);}else{CLEAR(LCD_P2);}
    if(value&8){SET(LCD_P3);}else{CLEAR(LCD_P3);}
    if(value&16){SET(LCD_P4);}else{CLEAR(LCD_P4);}
    if(value&32){SET(LCD_P5);}else{CLEAR(LCD_P5);}
    if(value&64){SET(LCD_P6);}else{CLEAR(LCD_P6);}
    if(value&128){SET(LCD_P7);}else{CLEAR(LCD_P7);}
}

Now to reassign the pins, I modify two defines and move the wires:

#define LCD_P0      B,3
#define LCD_P1      B,4

The trade off is that it takes a little longer to set each pin individually in terms of cycles and flash but it sure simplifies I/O pin configuration. No problem now to switch pin assignments during PCB layout to gain efficient routing.

I like direct volatile references for bit fields within registers though because their layout is static. It's only for I/O that I desire additional flexibility.

April 20, 2011
by Hexorg
Hexorg's Avatar

Noter, good point, although, I think having "P0" addressing B3 would confuse me. However I'm starting to wonder if we can unite the bit field and your method.

April 20, 2011
by Noter
Noter's Avatar

You can name it anything you like, _P? is just the name I used although I wouldn't name it according to the port/pin that is assigned in the define. The point is that one definition associates the name with the port/pin and nothing else in the code needs to be touched to reassign pins.

Hmm ... I'll have to think a bit about uniting the two methods, that would be good. Although for static registers and bitmaps I wouldn't bother with the all the stuff that's needed for I/O pins.

Post a Reply

Please log in to post a reply.

Did you know that talking to the microcontroller over the USB/Serial link is easy under Windows, Linux, and OS X? Learn more...