Search |
 |
|
 |
| Author |
Message |
|
|
Posted: Mar 21, 2010 - 06:51 PM |
|

Joined: Sep 05, 2001
Posts: 2496
|
|
We all know, how a port pin can be cleared:
Code:
PORTB &= ~(1<<PB3);
But then it can happen, that we make mistakes, e.g.:
Code:
PORTB &= ~1<<PB3; // forget brackets
PORTB &= (1<<PB3); // forget negation
PORTB &= !(1<<PB3); // wrong negation
So using a macro can avoid mistakes:
Code:
#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:
Code:
#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:
Code:
#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:
Code:
#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 <avr/io.h>
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:
Code:
#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 <avr/io.h>
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 |
|
|
| |
|
|
|
|
|
Posted: Mar 25, 2010 - 10:14 PM |
|

Joined: May 27, 2002
Posts: 737
Location: Alabama USA
|
|
I am not familiar with: (__VA_ARGS__).
Is that for the GCC compiler only?
I currently use ICCAVR.
Thanks. |
|
|
| |
|
|
|
|
|
Posted: Mar 25, 2010 - 11:21 PM |
|

Joined: Sep 05, 2001
Posts: 2496
|
|
|
|
|
|
|
Posted: Mar 25, 2010 - 11:23 PM |
|

Joined: Nov 17, 2004
Posts: 13814
Location: Vancouver, BC
|
|
|
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.
|
| |
|
|
|
|
|
Posted: May 11, 2010 - 07:29 AM |
|


Joined: Apr 25, 2004
Posts: 3808
Location: Denmark
|
|
@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 |
|
|
| |
|
|
|
|
|
Posted: May 14, 2010 - 12:37 AM |
|

Joined: Nov 26, 2009
Posts: 1
|
|
hello all
maybe a safer way for this could be :
Code:
#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") |
|
|
| |
|
|
|
|
|
Posted: May 19, 2010 - 01:29 PM |
|

Joined: Sep 05, 2001
Posts: 2496
|
|
|
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.
Peter |
|
|
| |
|
|
|
|
|
|
|
|