avr-gcc-list
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [avr-gcc-list] Question about gcc preprocessing and port/pin assignm


From: Ned Konz
Subject: Re: [avr-gcc-list] Question about gcc preprocessing and port/pin assignments
Date: Tue, 7 Dec 2004 21:54:27 -0800
User-agent: KMail/1.7.1

On Sunday 05 December 2004 10:19 pm, James Washer wrote:
> I'd like to be able to say something like
>
> #define switch PB7
> .
> .
> .
> setpin(PB7)
>
> and have the following code generated
>
> PORTB |= 1<<7
>
>
> What I'm hoping for is the ability to change the definition of "switch" at
> any time, and have everything work
>
> For example
>
> if I change switch to PD2, the preprocessor would (auto)magically arrange
> for the following code
>
> PORTD |= 1<<2
>
>
> Any ideas?

Sure. My solution is simpler in terms of what you'd write in your program, 
though; hope that doesn't disappoint you.

I'm using header files that I generated with a program that I wrote (and 
posted here, of course) that eats the register definitions from Atmel's 
datasheets and spits out header files. I'll spare you the boring details, but 
here's the gist of it:

Each IO port (in fact, each special-function register) is defined in the 
header files with bitfield definitions like this (from my atmega16.h file):

/* PORTB ($38) (page 64) */
typedef union PORTB_t {
  uint8_t asByte;
  union {
    uint8_t bPORTB0 :1;
    uint8_t bPORTB1 :1;
    uint8_t bPORTB2 :1;
    uint8_t bPORTB3 :1;
    uint8_t bPORTB4 :1;
    uint8_t bPORTB5 :1;
    uint8_t bPORTB6 :1;
    uint8_t bPORTB7 :1;
  };
} PORTB_t;
#define PORTB_sfr (*(PORTB_t *) (0x38))
#define PORTB PORTB_sfr.asByte
#define PORTB0 PORTB_sfr.bPORTB0
#define PORTB1 PORTB_sfr.bPORTB1
#define PORTB2 PORTB_sfr.bPORTB2
#define PORTB3 PORTB_sfr.bPORTB3
#define PORTB4 PORTB_sfr.bPORTB4
#define PORTB5 PORTB_sfr.bPORTB5
#define PORTB6 PORTB_sfr.bPORTB6
#define PORTB7 PORTB_sfr.bPORTB7

Likewise, there's definitions for PORTD0-PORTD7, PINB-PINB7, etc. Multiple bit 
fields are also handled.

So with the above headers, you just write this to define your output pins:


#define SOME_OUTPUT  PORTB7
#define SOME_OUTPUT_DIRECTION DDB7


and then later you write code like this:


SOME_OUTPUT_DIRECTION = 1; /* set to output */

SOME_OUTPUT = 1; /* set it */
SOME_OUTPUT = 0; /* clear it */

if (SOME_OUTPUT) { /* test it */ }


And to change the pin definitions later, you'd change the two defines:


#define SOME_OUTPUT  PORTD2
#define SOME_OUTPUT_DIRECTION DDD2


If you really want macros with parentheses, we can do that too, but it doesn't 
get you too much...


#define setpin(pin) pin = 1
#define clearpin(pin) pin = 0


I've been using this for some time, and it really beats the bit clearing and 
setting in most cases. And it's typesafe; you don't have to worry about 
setting a bit number in the wrong place. Naturally, the compiler's 
optimization results in the same code. Of course, you can always do the 
manual bit clearing and setting yourself with my headers:

#define SOME_OUTPUT_PORT PORTD_sfr
#define SOME_OUTPUT_BIT 2
#define SOME_OUTPUT_MASK (1<<SOME_OUTPUT_MASK)

SOME_OUTPUT_PORT |= SOME_OUTPUT_MASK; /* set it the hard way */

etc., or you could use macros. But I think my way is nicer.

The compiler will combine these bitfield settings when the register isn't 
defined as volatile:

PORTB2 = 1;
PORTB3 = 1;

will result in the same thing as:

PORTB_sfr |= (1<<2) | (1<<3);

-- 
Ned Konz
http://bike-nomad.com



reply via email to

[Prev in Thread] Current Thread [Next in Thread]