Let's assume a function that gets a port as a parameter, it sets up the SPI module on that port and also sets the required pins to an output state.

The declaration looks something like this:

void SPIInit(PORT_t *portname);

However, inside the function I'll also need to use SPIx with x being the specific port. For example, if the function gets called with PORTD, I'll to use SPID for the SPI module. Is there anyway to get SPIx from PORTx without using 'if' statements?


3 Answers 3


I'm unfamiliar with the XMega's, but here's something I've used before. It might look tricky if you haven't used pointers this way.

For this example, let's say that you have three SPI controllers, and they each have three registers associated with them. Let's assume these names and addresses are defined in the XMega's header files:

SPI1_CR1 0x4100
SPI1_CR2 0x4102
SPI1_CR3 0x4104

SPI2_CR1 0x4200
SPI2_CR2 0x4202
SPI2_CR3 0x4204

SPI3_CR1 0x4300
SPI3_CR2 0x4302
SPI3_CR3 0x4304

This example assumes that each SPI register's configuration registers follow a similar address scheme. It's possible that this technique won't work with the XMega.

  1. Create a struct to be used as address pointers:

    typedef struct
        volatile uint16_t CR1; // assumes 16-bit registers
        volatile uint16_t CR2;
        volatile uint16_t CR3;
    } SPI_Regs_t

    If the register addresses are not contiguous, you will need to adjust for it in your struct by adding useless struct members:

    typedef struct
        volatile uint16_t CR1; // assumes 32 bits of space between addresses
        uint16_t USELESS1;
        volatile uint16_t CR2;
        uint16_t USELESS2;
        volatile uint16_t CR3;
    } SPI_Regs_t
  2. Define pointers to your SPI register addresses:

    #define SPI1 ((SPI_Regs_t *) SPI1_CR1)
    #define SPI2 ((SPI_Regs_t *) SPI2_CR1)
    #define SPI3 ((SPI_Regs_t *) SPI3_CR1)
  3. Now, you can create functions like so:

    void SPIInit(SPI_Regs_t* SPIx)
        uint16_t myvar;
        SPIx->Reg1 = 0xFF;
        uint16_t myvar = SPIx->Reg2;

Of course, you'll probably have to choose different names. I expect "SPI1" is already used in the XMega headers. :)

So, now, the question is: Is it worth it?


It is bit questionable if something like this is good idea from beginning, but yes, you can do that. It starts with assumption that addresses for PORTx registers blocks are spaced evenly and the same is true for SPIx blocks. This seems to be true looking into datasheets, but be warned that it is in no way officially guaranteed.

Anyway, starting with this assumption, you can do something like:

uint8_t n;
SPI_t *spi;

n = ((void*)portname - (void*)&PORTC) / ((void*)&PORTD - (void*)&PORTC);
spi = (SPI_t *) ((void *)&SPIC + n*((void *)&SPID - (void *)&SPIC));
spi->DATA = ...;

With some macros you can let preprocessor to take care of all this arithmetic, which would be better. Still, this piece of code does not check resulting address is really valid and existing peripheral module. And you can not use this trick to get, for example, ISR vector number.

Calculating it other way around -- from SPI module address to PORT address would be marginally more safe as for valid SPI module there is always respective port. Note also that to make code above more universal it would need at least one #ifdef anyway -- to check if SPID is defined (XMEGA-E has one SPI module only).


As I recall from days gone by working with the AVR family there is no way to do what you want without some conditional selection.

The most direct is using "if / else if / ..... / else structure.

An alternative is to create some enumerations that have elements named similar to your PORT and SPI peripheral block names. Then when you pass an argument to the configuration subroutine pass in the enumeration value if port instead and then use a switch statement inside the subroutine to select specific SPI port configs.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.