I want to write a driver that can use either timer0 or timer2, based on a choice made at compile time.
So I want all references to timer registers to be abstracted, e.g. OCRA for OCR0A/OCR2A, etc.
I could do it with plain macros, like so:
#define _CONCAT3(a,b,c) a##b##c
#define CONCAT3(a,b) _CONCAT3(a,b,c)
// set timer number globally
#define MY_TIMER 2
// define an alias for each control register
#define OCRA CONCAT3(OCR, MY_TIMER, A)
but for some reason I don't want to use macros there.
So I came up with this scheme:
typedef volatile uint8_t& tRegister;
constexpr volatile uint8_t& timer_register (volatile uint8_t& a, volatile uint8_t& b)
{ return MY_TIMER==0 ? a : b; }
tRegister OCRA = timer_register (OCR0A, OCR2A);
It works pretty well for all its awkwardness, but when I try to use this definition in an inline assembly directive, I get strange results:
asm volatile("lds r24, %0" : : "i"(&OCRA));
will get an error: impossible constraint in 'asm'
Now this:
asm volatile("lds r24, %0" : : "i"(&timer_register (OCR0A , OCR2A )));
will compile (and produce lds r24, 0x00B3
as it should).
By definition a reference to a static variable is a constant address, known at compile time, so I can't see why it can't be used in this context.
So my questions are:
- what is going on there?
- how to alias these registers (without macros) to please the inline assembler?