Programming re-usable modules for microcontrollers (AVR in my case) requires flexibility in general IO pins. Each pin defined by a letter (A-G) and a number (0-7). It is, however, controlled by one bit at the same positon in three registers. Thus the configuration file needs to contain four entries (3 pointer to the registers + 1 position), which is not that elegant.
The easy fix is to simply accept this, but since it is such a common problem it deserves at least a little attention.
It would be nice to let the precompiler do the repetitive work like this:
//CONFIGURATION
#define IO_NAME B5
//MACRO
#define PORT_(ID)
#define PIN_(ID)
#define DDR_(ID)
#define BIT_(ID)
The results should look like this
PORT_(IO_NAME) => PORTB
PIN_(IO_NAME) => PINB
DDR_(IO_NAME) => DDRB
BIT_(IO_NAME) => 5
The resulting expressions are defined in AVR Studio.
I couldn't figure out how to ignore either the letter not the number, so I tried concatention instead:
#define PORT_(REG, BIT) PORT_2(REG, BIT)
#define PIN_(REG, BIT) PIN_2(REG, BIT)
#define DDR_(REG, BIT) DDR_2(REG, BIT)
#define PORT_2(REG, BIT) (PORT ## REG)
#define PIN_2(REG, BIT) (PIN ## REG)
#define DDR_2(REG, BIT) (DDR ## REG)
#define BIT(REG, BIT) (BIT)
The extra layer is required to use any #defined value as either REG or BIT.
The following code works as intended:
#define IO_NAME_REG B
#define IO_NAME_BIT 5
PORT_(B, 5) => PORTB
PORT_(IO_NAME_REG, IO_NAME_BIT) => PORTB
When I however try
#define IO_NAME B, 5
PORT_(IO_NAME)
it results in the Error:
macro "PORT_" requires 2 arguments, but only 1 given
As far as I figured out the comma is interpreted as the comma operator and thus the left value is ignored. I concluded this from this experiment:
#define IO_NAME B, 5
PORT_(IO_NAME,) => PORT_5
Replacing the comma with a define:
#define comma ,
#define IO_NAME B comma 5
PORT_(IO_NAME,) => PORT_5
leads to the same result
Of course it is possible to seperate the 'B' and '5' into two seperate defines. While this is an improvment over four defines it's still not that comfortable.
... is not stricly limited to the title. Any solution which is shorter or simpler than
#define IO_NAME_REG B
#define IO_NAME_BIT 5
PORT_(IO_NAME_REG, IO_NAME_BIT) => PORTB
is welcome to me.
If this is simply not possible I'd like to know the reasons why.
This should do what you want:
#include <stdio.h>
#define IO_NAME B, 5
#define PORT_(arg) PORT_2(arg)
#define PIN_(arg) PIN_2(arg)
#define DDR_(arg) DDR_2(arg)
#define BIT_(arg) BIT_2(arg)
#define PORT_2(reg, bit) (PORT ## reg)
#define PIN_2(reg, bit) (PIN ## reg)
#define DDR_2(reg, bit) (DDR ## reg)
#define BIT_2(reg, bit) (bit)
#define PORTB 1
#define PINB 2
#define DDRB 3
int main()
{
printf("%d\n", PORT_(IO_NAME));
printf("%d\n", PIN_(IO_NAME));
printf("%d\n", DDR_(IO_NAME));
printf("%d\n", BIT_(IO_NAME));
return 0;
}
This produces the following output:
1
2
3
5
The ##
in the macro bodies causes the preprocessor to create new tokens by concatenating the operands.
This is almost what you had. The difference is your top-level macros need to take a single argument, while the secondary macros take two.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With