avr_sfr_notes(3avr)
π avr_sfr_notes(3avr) β the tea on <avr/sfr_defs.h> π
okay bestie, welcome to avr_sfr_notes(3avr), aka the extra notes page for <avr/sfr_defs.h> π it's giving "the lore behind the macros that make your AVR registers look cute in C." let's get into it π
β¨ SYNOPSIS β¨
πΈ Detailed Description πΈ
so here's the vibe: every single one of the <avr/ioXXXX.h> files includes <avr/sfr_defs.h> π«Ά and those io files use the macros defined here to make special function register definitions look like either C variables or plain simple constants β depending entirely on the _SFR_ASM_COMPAT define, iykyk.
and like, some examples straight from <avr/iocanxx.h> to show you how you'd define those 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 β¨ then C programs can just use names like PORTA directly in C expressions β yes bestie, even on the left side of assignment operators β and GCC will do the right thing (it'll use short I/O instructions if it can). and the __SFR_OFFSET definition? literally not used in any way in this case. it's just there vibing π
define _SFR_ASM_COMPAT as 1 and those names now work as simple constants (the addresses of the I/O registers). this is necessary when you're in a preprocessed assembler (*.S) source file, which is why it gets done automatically if __ASSEMBLER__ is defined π by default, all addresses are defined as if they were memory addresses (the kind you'd use in lds/sts instructions). so to use those addresses in in/out instructions, you must subtract 0x20 from them. no cap, that's mandatory π¨
for more backwards compatibility, you can insert this at the start of your old assembler source file:
#define __SFR_OFFSET 0
this automatically subtracts 0x20 from I/O space addresses β‘οΈ but bestie, it's a hack. so it is recommended that you change your source instead: wrap those addresses in the macros defined here, like the example below. once that's 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's 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
and you can use the in/out/cbi/sbi/sbic/sbis instructions without the _SFR_IO_REG_P test, if you already know the register lives 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'll be 0x20 by default), then 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 that 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 for safety (one defined only if __SFR_OFFSET == 0x20):
sts _SFR_ADDR(SPMCR), r24
and in C programs? all 3 combinations of _SFR_ASM_COMPAT and __SFR_OFFSET are supported π so the _SFR_ADDR(SPMCR) macro can be used to get the address of the SPMCR register β 0x57 or 0x68 depending on the device. iconic.
π¦ Author π¦
Generated automatically by Doxygen for AVR-LibC from the source code. (not me writing it by hand, the robot did it, and honestly? ate. π )
and that's the whole page bestie π this was AVR-LibC Version 2.2.1, section 3avr β mwah, ily, go read your register definitions responsibly πβ¨
π¦ where this page comes from
- ubuntu resolute: avr-libc 1:2.2.1-1build1 Β· BSD-3-Clause
- ubuntu stonking: avr-libc 1:2.2.1-1build1 Β· BSD-3-Clause
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms