AVR Libc Home Page |  | AVR Libc Development Pages
Main Page | User Manual | Library Reference | FAQ | Alphabetical Index | Example Projects

Additional notes from <avr/sfr_defs.h>
<avr/sfr_defs.h>: Special function registers

The `<avr/sfr_defs.h>` file is included by all of the `<avr/ioXXXX.h>` files, which use macros defined here to make the special function register definitions look like C variables or simple constants, depending on the `_SFR_ASM_COMPAT` define. Some examples from `<avr/iocanxx.h>` to show how to define such macros:

#define PORTA _SFR_IO8(0x02)
#define EEAR _SFR_IO16(0x21)
#define UDR0 _SFR_MEM8(0xC6)
#define TCNT3 _SFR_MEM16(0x94)
#define CANIDT _SFR_MEM32(0xF0)

If `_SFR_ASM_COMPAT` is not defined, C programs can use names like `PORTA` directly in C expressions (also on the left side of assignment operators) and GCC will do the right thing (use short I/O instructions if possible). The `__SFR_OFFSET` definition is not used in any way in this case.

Define `_SFR_ASM_COMPAT` as 1 to make these names work as simple constants (addresses of the I/O registers). This is necessary when included in preprocessed assembler (*.S) source files, so it is done automatically if `ASSEMBLER` is defined. By default, all addresses are defined as if they were memory addresses (used in `lds/sts` instructions). To use these addresses in `in/out` instructions, you must subtract 0x20 from them.

For more backwards compatibility, insert the following at the start of your old assembler source file:

#define __SFR_OFFSET 0

This automatically subtracts 0x20 from I/O space addresses, but it's a hack, so it is recommended to change your source: wrap such addresses in macros defined here, as shown below. After this is done, the `__SFR_OFFSET` definition is no longer necessary and can be removed.

Real example - this code could be used in a boot loader that is portable between devices with `SPMCR` at different addresses.

    <avr/iom163.h>: #define SPMCR _SFR_IO8(0x37)
    <avr/iom128.h>: #define SPMCR _SFR_MEM8(0x68)

#if _SFR_IO_REG_P(SPMCR)
out _SFR_IO_ADDR(SPMCR), r24
#else
sts _SFR_MEM_ADDR(SPMCR), r24
#endif

You can use the `in/out/cbi/sbi/sbic/sbis` instructions, without the `_SFR_IO_REG_P` test, if you know that the register is in the I/O space (as with `SREG`, for example). If it isn't, the assembler will complain (I/O address out of range 0...0x3f), so this should be fairly safe.

If you do not define `__SFR_OFFSET` (so it will be 0x20 by default), all special register addresses are defined as memory addresses (so `SREG` is 0x5f), and (if code size and speed are not important, and you don't like the ugly #if above) you can always use lds/sts to access them. But, this will not work if `__SFR_OFFSET` != 0x20, so use a different macro (defined only if `__SFR_OFFSET` == 0x20) for safety:

sts _SFR_ADDR(SPMCR), r24

In C programs, all 3 combinations of `_SFR_ASM_COMPAT` and `__SFR_OFFSET` are supported - the `_SFR_ADDR(SPMCR)` macro can be used to get the address of the `SPMCR` register (0x57 or 0x68 depending on device).

Automatically generated by Doxygen 1.8.3.1 on Sun Sep 29 2013.