在
iom16.h中定义了
PORTA如下:
如果编译器( _SFR_ASM_COMPAT等于1)可以直接使用汇编、那么像 PORTA这样的变量,就是一个地址值。
否则、如果只能使用C语言,那么操作 PORTA这样的变量,就是一个指针操作的过程。
此时、 PORTA的定义展开如下:
到此、 PORTA的定义进一步展开如下:
这样对 代码的移植是很有好处的。
也可以进一步打包成这样来隐藏指针操作符:
#define PORTA _SFR_IO8(0x1B)
接着在sfr_defs.h中定义了_SFR_IO8()如下:
#if _SFR_ASM_COMPAT
......
#define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET)
#define _SFR_IO16(io_addr) ((io_addr) + __SFR_OFFSET)
......
#else /* !_SFR_ASM_COMPAT */
......
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + __SFR_OFFSET)
......
#endif /* !_SFR_ASM_COMPAT */
这个定义表示:
如果编译器( _SFR_ASM_COMPAT等于1)可以直接使用汇编、那么像 PORTA这样的变量,就是一个地址值。
此时、__SFR_OFFSET是IO寄存器的基地址,其值等于0x00,所以PORTA展开如下:
#define PORTA _SFR_IO8(0x1B)
= ((io_addr) + __SFR_OFFSET)
= ((0x1B) + 0x00)
= 0x1B
在汇编中操作
PORTA时,就是直接操作
PORTA的地址:
否则、如果只能使用C语言,那么操作 PORTA这样的变量,就是一个指针操作的过程。
此时、 PORTA的定义展开如下:
#define PORTA _SFR_IO8(0x1B)
= _MMIO_BYTE((io_addr) + __SFR_OFFSET)
_MMIO_BYTE()的定义如下:
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
这就是用指针的方式去操作一个内存地址,此时
__SFR_OFFSET等于
0x20。
到此、 PORTA的定义进一步展开如下:
#define PORTA _SFR_IO8(0x1B)
= _MMIO_BYTE((io_addr) + __SFR_OFFSET)
= (*(volatile uint8_t *)((io_addr) + __SFR_OFFSET))
= (*(volatile uint8_t *)((0x1B) + __SFR_OFFSET))
= (*(volatile uint8_t *)(0x3B))
__SFR_OFFSET是IO寄存器的基地址,0x1B是PORTA在寄存器文件中的偏移量,加在一起就是PORTA的真实的物理地址(端口地址)。
所以我们对PORTA赋值的时候,就是将数值写入上面这个地址处的寄存器中。
参考avr-libc的介绍:http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr__notes.html
-------------------------------------------------------------------------------------------------------------------------------------
因此、我们可以将PORTA重命名如下:
uint8_t *LED = (uint8_t *)(&PORTA);
然后就可以直接对
LED进行操作,来点亮
LED:
*LED = 0x55;
如果以后更换了
LED的
IO口,只需要修改
LED这个变量的定义即可。
这样对 代码的移植是很有好处的。
也可以进一步打包成这样来隐藏指针操作符:
#define LED (*(volatile uint8_t *)(&PORTA))
最后、如果可以不用#define的定义来隐藏指针操作符就更好了,因为#define定义的部分阅读起来不方便
<还在继续...>