I've often seen
sbi() in Arduino code. I usually don't mind them as I know what they do (clear or set the bit given as second argument in the register of the microcontroller given as first). I've always thought that those functions where only a clear way to perform simple bit manipulation, which can be quite error-prone. Something like that:
#define cli(reg,bit) (*reg &= ~(1 << bit) and
#define sbi(reg,bit) (*reg |= (1 << bit)).
Then I found out that the actual implementation is as follows:
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
It's quite like I used to think... but with two extra macros. I looked for them in the Arduino libraries:
#define _BV(bit) (1 << (bit))
#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#if _SFR_ASM_COMPAT #if (__SFR_OFFSET == 0x20) #define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr) #elif !defined(__ASSEMBLER__) #define _SFR_ADDR(sfr) (_SFR_IO_REG_P(sfr) ? (_SFR_IO_ADDR(sfr) + 0x20) : _SFR_MEM_ADDR(sfr)) #endif #else /* !_SFR_ASM_COMPAT */ #define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr)
Further research took me to definitions I couldn't understand (for example because they are explained in comments using a lot of acronyms I don't know). So I have the following questions:
- Why is
*(volatile uint8_t *)?
- What do the acronyms used in that code mean? (
- Under what circumstances is
__ASSEMBLER__defined, and where (in which file)?
- Why can't we simply write
#define cli(reg,bit) (*reg &= ~(1 << bit)and
#define sbi(reg,bit) (*reg |= (1 << bit))?