[TUT] [C] Easy bit fiddling

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We all know, how a port pin can be cleared:

PORTB &= ~(1<<PB3);

But then it can happen, that we make mistakes, e.g.:

PORTB &= ~1<<PB3;         // forget brackets
PORTB &= (1<<PB3);        // forget negation
PORTB &= !(1<<PB3);       // wrong negation

So using a macro can avoid mistakes:

#define	bit_clear_(x,y)		x &= ~(1<<y)			// clear a bit

bit_clear( PORTB, PB3);

But this was not really helpful, since we must always look on the schematic to know, which function has this pin.

So we can define further macros:

#define	bit_clear_(x,y)		x &= ~(1<<y)			// clear a bit

#define LED0_PORT PORTB
#define LED0_BIT PB3

bit_clear( LED0_PORT, LED0_BIT);

But this looks not fully nice, since we need always two defines for every pin.
If we try to write port and pin into a single define, we get an error message, that the argument count was different.
For such cases we can define a second macro to switch off the argument check:

#define	bit_clear(...)		bit_clear_(__VA_ARGS__)
#define	bit_clear_(x,y)		x &= ~(1<<y)			// clear a bit

#define	LED0		PORTB, PB3

bit_clear( LED0 );

This looks pretty nice, so we can write further macros to set and to read a bit:

#define	bit_set(...)		bit_set_(__VA_ARGS__)
#define	bit_set_(x,y)		x |= 1<<y			// set a bit
#define	bit_clear(...)		bit_clear_(__VA_ARGS__)
#define	bit_clear_(x,y)		x &= ~(1<<y)			// clear a bit
#define	bit_test(...)		bit_test_(__VA_ARGS__)
#define	bit_test_(x,y)		(!!(x & (1<<y)))		// test a bit


#define	LED0		PORTB, 0
#define	LED0_DDR	DDRB, 0
#define	KEY0		PINB, 2
#define	KEY0_PULLUP	PORTB, 2


#include 


int main( void )
{
  bit_set( KEY0_PULLUP;		// pullup on
  bit_set( LED0_DDR );		// output

  for(;;){
    if( bit_test( KEY0 ))
      bit_set( LED0 );
    else
      bit_clear( LED0 );
  }
}

This looks better, but we can prettify it further.
We see, that we often need to access PORTx, DDRx and PINx registers together and then we need separate bit names for this.
But the address relation of these registers on the AVRs was constant, so we can define further macros to access different functions of a port pin and need only one definition on every port pin.
To do so, the definition was related to the PORTx register, even, we want to read the input register.
So we get finally the following macro definitions on this code example:

#define	bit_set(...)		bit_set_(__VA_ARGS__)
#define	bit_set_(x,y)		x |= 1<<y			// set a bit
#define	bit_clear(...)		bit_clear_(__VA_ARGS__)
#define	bit_clear_(x,y)		x &= ~(1<<y)			// clear a bit
#define	bit_test(...)		bit_test_(__VA_ARGS__)
#define	bit_test_(x,y)		(!!(x & (1<<y)))		// test a bit

/*** following macros related to the PORTx - register only !	***/
/***								***/
#define	bit_dir_outp(...)	bit_dir_outp_(__VA_ARGS__)
#define	bit_dir_outp_(x,y)	*(&x-1) |= 1<<y			// access DDRx of PORTx !
#define	bit_dir_inp(...)	bit_dir_inp_(__VA_ARGS__)
#define	bit_dir_inp_(x,y)	*(&x-1) &= ~(1<<y)		// access DDRx of PORTx !
#define	bit_test_in(...)	bit_test_in_(__VA_ARGS__)
#define	bit_test_in_(x,y)	(!!(*(&x-2) & (1<<y)))		// access PINx of PORTx !


#define	LED0		PORTB, 0
#define	KEY0		PORTB, 2


#include 


int main( void )
{
  bit_dir_inp( KEY0 );		// input
  bit_set( KEY0 );		// pullup on
  bit_set( LED0 );		// LED off
  bit_dir_outp( LED0 );		// output

  for(;;){
    if( bit_test_in( KEY0 ))
      bit_clear( LED0 );
    else
      bit_set( LED0 );
  }
}

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am not familiar with: (__VA_ARGS__).
Is that for the GCC compiler only?
I currently use ICCAVR.

Thanks.

I'll believe corporations
are people when Texas executes one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your compiler meet the C99 standard, it should support variadic macros:

http://en.wikipedia.org/wiki/Var...

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I am not familiar with: (__VA_ARGS__).

It is the preprocessor equivalent to "..." used for representing a variable number of arguments to a macro.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Peter

Nice ..

But AFAIK you "Will be bitten" if using PORTF on a M64/128 , as &DDRF isn't &(PORTF-1)

You could "ifdef" you out of it , or emit a warning if it's a M64/128

/Bingo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hello all

maybe a safer way for this could be :

#define   bit_set(...)       bit_set_(__VA_ARGS__)
#define   bit_set_(x,y)      PORT ## x |= 1<<y
#define   bit_clear(...)     bit_clear_(__VA_ARGS__)
#define   bit_clear_(x,y)    PORT ## x &= ~(1<<y)
#define   bit_test(...)      bit_test_(__VA_ARGS__)
#define   bit_test_(x,y)     (!!(PORT ## x & (1<<y)))

#define   bit_dir_outp(...)  bit_dir_outp_(__VA_ARGS__)
#define   bit_dir_outp_(x,y) DDR ## x |= 1<<y
#define   bit_dir_inp(...)   bit_dir_inp_(__VA_ARGS__)
#define   bit_dir_inp_(x,y)  DDR ## x &= ~(1<<y)
#define   bit_test_in(...)   bit_test_in_(__VA_ARGS__)
#define   bit_test_in_(x,y)  (!!(PIN ## x & (1<<y)))

usage :
#define   LED0      B, 0
#define   KEY0      B, 2

it uses the concatenation feature of the preprocessor (##) to replace with the real names of registers (DDRx and so on).

But I admit it forces to define our pins in a less pretty way (#define LED0 B, 0 instead of "PORTB, 0")

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Bingo600 wrote:
But AFAIK you "Will be bitten" if using PORTF on a M64/128 , as &DDRF isn't &(PORTF-1)

Yes, you are right. :!:

But I assume, a new user start not to program the old M64/128.

And on the pin compatible ATmega1281 this rule fits again. :D

Peter