I looked into one of these libraries, namely the SparkFun CAN-Bus
Arduino
Library.
The macro appears to be used inside other macros that require two
arguments, for example RESET(P_MOSI);
. Given the following
definitions from the library:
#define RESET(x) _XRS(x)
#define _XRS(x,y) PORT(x) &= ~(1<<y)
#define PORT(x) _port2(x)
#define _port2(x) PORT ## x
#define P_MOSI B,3
the call RESET(P_MOSI);
expands to
PORTB &= ~(1<<3);
PORTB
is the name of an IO port of the MCU which controls the outputs
of the pins. It is actually a macro from
avr-libc that expands to something
like:
(*(volatile uint8_t *)(0x05 + 0x20))
where 0x05
is the IO port number of the register (the number depends
on the particular AVR chip) and 0x20
is an offset needed to get the
memory-mapped address from the IO port number. When the compiler does
constant folding, RESET(P_MOSI);
resolves to
*(volatile uint8_t *)0x25 = 8;
which is then compiled into
cbi 0x05, 3
a single assembly instruction (Clear Bit in Io port) that executes in
two CPU cycles and sets to zero the bit 3 of IO port 0x05.
The same end result could be achieved in a much simpler way:
#define P_MOSI 11 // assuming digital 11 = PB3, as in the Uno
#define RESET(x) digitalWrite(x, LOW)
However, the function digitalWrite()
is very slow, mostly because it
has to map an Arduino pin number to a port address and a bit number, and
it does it at run time. The SparkFun approach, though somewhat
convoluted, is way more efficient, because the mappings are performed
at compile time.