avr_sfr_notes(3)

πŸ“¦ version 2.2.1, in fedora 43, fedora 44, opensuse-leap 16.0

πŸ’– NAME πŸ’–

avr_sfr_notes β€” the whole vibe behind those magical special-function-register macros, bestie πŸ’…


✨ SYNOPSIS ✨

Detailed Description

okay so first things first: <avr/sfr_defs.h> gets included by every single one of the <avr/ioXXXX.h> files 🫢 and those files use the macros defined in here to make the special function register definitions look like either C variables or plain simple constants β€” it literally depends on the _SFR_ASM_COMPAT define. no cap.

here are some examples straight out of <avr/iocanxx.h> showing how you'd define macros like this:

#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)

so if _SFR_ASM_COMPAT is not defined, C programs can just use names like PORTA directly in C expressions (yes, even on the left side of assignment operators, she's versatile πŸ’β€β™€οΈ) and GCC will do the right thing β€” it'll use short I/O instructions if possible. and in this case the __SFR_OFFSET definition is not used in any way at all. it's just sitting there. not invited. πŸ™ˆ

now define _SFR_ASM_COMPAT as 1 and those same names work as simple constants (the addresses of the I/O registers). this is necessary when the file gets included in preprocessed assembler (*.S) source files, so it happens 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). but to use these addresses in in/out instructions, you must subtract 0x20 from them. yes really. do the math. ✨

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 lowkey it's a hack 🚩 so the recommendation is to actually change your source: wrap such addresses in the macros defined here, as shown below. once you've done that, the __SFR_OFFSET definition is no longer necessary and can be removed. glow up achieved. πŸ’…

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 β€” she's an icon, she knows her place πŸ‘‘). if it isn't, the assembler will complain (I/O address out of range 0...0x3f), so this should be fairly safe. fairly. πŸ’€

and 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 heads up bestie, 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

and 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). every combination ate, no crumbs. πŸ”₯


πŸ‘‘ AUTHOR πŸ‘‘

Generated automatically by Doxygen for avr-libc from the source code. (not me writing it by hand, the robots did that part πŸ€–βœ¨)


and that's the whole page, babe πŸŽ€ this was Version 2.1.0, dated Sat Jan 29 2022, and honestly? iconic behavior from the avr-libc girlies. go forth and <avr/sfr_defs.h> responsibly πŸ’œπŸ¦‹πŸŒΈ

πŸ“¦ where this page comes from