1

i know, there are many posts on it, but i still do not get it. I want to simplify port handling an a microcontroller with #define. Usually you do like this

DDRA |= (1 << PORTA0)  // set pin 0 at port A as OUTPUT
PORTA |= (1 << PORTA0) // set pin 0 at port A

"PORTA", "DDRA" and "PORTA0" are macros of the avr/io.h which expand to numbers.

Currently i do like this:

// pinControl.h
#define setPin(port, pin)       (port |= (1 << pin))
#define setOutput(ddr, pin)     (ddr |= (1 << pin))

// some other file.h
#define myPinPort  PORTA
#define myPinDDR   DDRA
#define myPin      PORTA0

setOutput(myPinDDR, myPin)
setPin(myPinPort, myPin)

now, if the port changes and i have lots of pins, i need always to change every DDRA and PORTA to e.g. to DDRB and PORTB. I'd like to simplify it like this:

// pinControl.h
#define stringify1(x)   #x
#define stringify(x)    stringify1(x)

#define setPin(port, pin)      (stringify(PORT)##port |= (1 << stringify(PORT)##port##pin))
#define setOutput(port, pin)   (stringify(DDR)##port |= (1 << stringify(PORT)##port##pin))


// some other file.h
#define myPinPort   A
#define myPin       0

setOutput(myPinPort, myPin); // shall expand to "DDRA |= (1 << PORTA0)";
setPin(myPinPort, myPin);    // shall expand to "PORTA |= (1 << PORTA0);"

I am really confused with the syntax to get this result out :(:(

Pooop
  • 11
  • 1

1 Answers1

0

The stringify operation is dead end road, once something is converted into string there is no way to get that back to be part of the name of an identifier.

Since you want to avoid generation of identifiers like PORTmyPinPort you need the pre-processor to to have one additional level of evaluation that will "unpack" myPinPort into A. Common strategies for this it to define a VALUE macro that just returns its argument value, but in the process of expanding that VALUE macro the argument is also expanded.

Now for token pasting that approach does not work because of evaluation order, so another common approach is to do the token pasting inside another macro, typically named PASTE.

From memory I think the standard says that token pasting for more than two tokens is unspecified or possibly undefined, so the more correct approach is probably to nest PASTE2 calls, but when testing PASTE3 works. Possibly a gcc extension.

The following test code generates the exact same assembly code for f1 and f2.

int PORTA;
int DDRA;
int PORTA0;

#define VALUE(v_) v_
#define PASTE2(a_, b_) a_##b_
#define PASTE3(a_, b_, c_) a_##b_##c_

#define setPin(port, pin)      PASTE2(PORT, port) |= (1 << PASTE3(PORT, port, pin))
#define setOutput(port, pin)   PASTE2(DDR, port) |= (1 << PASTE3(PORT, port, pin))

#define myPinPort   A
#define myPin       0

void f1()
{
    setPin(myPinPort, myPin);
    setOutput(myPinPort, myPin);
}

void f2()
{
    PORTA |= (1 << PORTA0);
    DDRA |= (1 << PORTA0);
}
hlovdal
  • 26,565
  • 10
  • 94
  • 165