AVR I/O 宏定义如何工作以允许访问寄存器?
Posted
技术标签:
【中文标题】AVR I/O 宏定义如何工作以允许访问寄存器?【英文标题】:How do AVR I/O macro definitions work to allow access to registers? 【发布时间】:2020-08-24 16:24:53 【问题描述】:我正在查看 ATmega2560 寄存器映射 (iom2560.h
) 的标头,其中包含所有寄存器的定义。例如:
#define PINA _SFR_IO8(0X00)
//Macro definition:
#define _SFR_IO8(io_addr) ((io_addr) + 0X20)
所以 PINA 是一个 8 位十六进制值,对应 8 位微控制器寄存器的地址。当我编写代码时,只需键入以下代码即可更改寄存器中的值:
PINA |= (1 << 3); // Setting the third bit.
问题来了:为什么我可以通过将值分配给 PINA 来写入寄存器值(“由他的地址 _SFR_IO8(0X00)
指向”)?不是指向寄存器的地址吗?编译器是如何工作的?
非常感谢您
【问题讨论】:
问题中有:#define _SFR_IO8(io_addr) ((io_addr) + 0X20)
嗯,但这个定义看起来不正确,它应该确实是一个内存访问(在中间涉及另一个宏 _MMIO_BYTE
,其中 确实 包含一个 *
, 因为它的计算结果为 *(volatile uint8_t *)(addr)
): electronics.stackexchange.com/a/463593/109961 - 我认为你忽略了 _SFR_IO8
定义中的 _MMIO_BYTE
部分!
感谢 CherryDT! - 实际上我两者都有 - 定义与标志 _SFR_ASM_COMPAT 有关。
是否定义了_SFR_ASM_COMPAT?因为这个定义在#if _SFR_ASM_COMPAT
里面
"编译器是如何工作的?[原文如此]这个问题你可以自己回答,如果你真的想知道的话。gcc 是开源的。
【参考方案1】:
简短的回答 - 隐藏在 Atmel 包含的标头中的是一组宏,它们创建指向寄存器位置的指针。以下是该过程的简要概述:
您的 Makefile 定义要使用的设备,然后将定义传递给编译器。
DEVICE = atmega2560
...
-D__$(DEVICE)__
然后包含 io.h,它会根据您的设备自动包含必要的标头:
// In main source file
#include <io.h>
// In io.h
#include <avr/sfr_defs.h>
// ...
#elif defined (__AVR_ATmega2560__)
# include <avr/iom2560.h>
// In sfr_defs.h
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define __SFR_OFFSET 0x20
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
// In iom2560.h
#include <avr/iomxx0_1.h>
// Other device specific definitions
// Om iomxx0_1.h
#define PINA _SFR_IO8(0X00)
// Other device family shared definitions
所以如果你展开所有这些,你得到的是一个指向寄存器地址的易失性指针。当您在代码中使用 PINA 时,预编译器会将其替换为所有扩展宏:
PINA
_SFR_IO8(0X00)
_MMIO_BYTE((0X00) + __SFR_OFFSET)
(*(volatile uint8_t *)((0X00) + 0x20))
其中指定 PINA 是指向 0x20 的易失性 8 位内存地址的指针。然后,每当访问该地址时,内部芯片架构都会将该地址映射到相应的外设寄存器。
【讨论】:
通过-save-temps
参数avr-gcc
你可以看到预处理后的C源代码(称为*.i
)和编译后的汇编代码(称为*.s
)来验证实际是什么在您的实际情况中进行。以上是关于AVR I/O 宏定义如何工作以允许访问寄存器?的主要内容,如果未能解决你的问题,请参考以下文章