Blinking Two LEDs; Bit Manipulation Macros Oh My!

Before dealing with switches and debouncing switches, I wanted to add a second LED to the circuit.

Trivial:

#include <avr/io.h>
#include <util/delay.h>

int main(void) {
    DDRB = 255U; // Make all PB* -- PORT B -- pins output
    PORTB = 0x0; // turn all PB* -- PORT B -- pins off.
    
    while (1) {
        PORTB = 0x1; // B0 on, B1 off
        _delay_ms(200);
        PORTB |= 0x2; // B0 and B1 on
        _delay_ms(100);
        PORTB = 0x2; // B0 off, B1 on
        _delay_ms(200);
        PORTB = 0X0; // All off
        _delay_ms(2500); // 2.5 seconds off
    }
}

Each on/off port is represented by a single bit. So, each of the pins in PORTA, PORTB, PORTC, or PORTD — assuming they are in straight digital input/output mode and whatever AVR chip you are targeting has enough pins to have 4 port sets — will be controlled by a single bit in one of four bytes.

Clearly, some macros to toggle bits are in order. Now, it turns out that macros to toggle bits are a big source of contention amongst the AVR development community. They hide too much magic, or so they say, and, if you are going to be programming embedded systems, you ought to be comfortable with C level bit twiddling, damnit.

Seriously — I read about 25 messages on the subject of bit twiddling as a part of the AVR API. The arrogance and close-mindedness was pretty silly.

Whatever.

Two LEDs a Blinkin'

Now that the bit twiddling is going to be hidden by macros, it is time to also modify the circuit to connect the LEDs through to VCC. Thus, the circuit will be more inline with basically every tutorial and document found on the net.

I also moved to using 1K ohm resistors — less current draw on the chip and just as bright (meaning that I was driving them with way too much current before).

The code now looks like this:

#define LED_ON(port, nr) port &= ~(1 << (nr))
#define LED_OFF(port, nr) port |= (1 << (nr))

int main(void) {
    DDRB = 255U; // Make all PB* -- PORT B -- pins output
    PORTB = 0xFF; // turn all PB* -- PORT B -- pins off.
    
    while (1) {
        LED_ON(PORTB, PB0);
        _delay_ms(200);
        LED_ON(PORTB, PB1);
        _delay_ms(100);
        LED_OFF(PORTB, PB0);
        _delay_ms(200);
        LED_OFF(PORTB, PB1);
        _delay_ms(2500); // 2.5 seconds off
    }
}

The LED_ON/LED_OFF macros take care of inverting the logic.

For additional bit manipulation operations, here are some useful macros culled from the discussion about a possible bit manipulation API that may or may not be added to the avr-gcc distribution someday:

#define bit_set_8(var, mask)   ((var) |= (uint8_t)(mask))
#define bit_clear_8(var, mask)   ((var) &= (uint8_t)~(mask))
#define bit_toggle_8(var, mask)   ((var) ^= (uint8_t)(mask))
#define bit_read_8(var, mask)   ((var) & (uint8_t)(mask))

#define bit_set_16(var, mask)   ((var) |= (uint16_t)(mask))
#define bit_clear_16(var, mask)   ((var) &= (uint16_t)~(mask))
#define bit_toggle_16(var, mask)   ((var) ^= (uint16_t)(mask))
#define bit_read_16(var, mask)   ((var) & (uint16_t)(mask))

/* equivalent to the poorly named _BV */
#define BIT(x)   (1 << (x))

The above enables expressions like:

bit_set(PORTD, BIT(0) | BIT(2) | BIT(4) | BIT(7));
bit_clear(PORTG, BIT(1) | BIT(6));
bit_toggle(PORTE, BIT(5) | BIT(3));
bit_read(PORTF, BIT(7));




3 Responses to “Blinking Two LEDs; Bit Manipulation Macros Oh My!”

  1. Peter says:

    Wow, some people really like to make things hard, don’t they?

  2. Ben Holt says:

    Another set of macros I find useful allows you to define binary constants. With these the bit_set example above could be written as bit_set(PORTD, B8(10010101)); which is the more readable version depends, of course, on the context, but they’re handy to have around. (And why C doesn’t support this directly as 0b10010101 is beyond me…)

  3. Nathan Gray says:

    Actually, binary constants have been added as an extension to GCC 4.3.0, with just the syntax you describe. 🙂

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>