((Port *)0x41004400UL)在这里是什么意思?

我正在开发一种基于32位ARM的微控制器的开发板(即电路板是Atmel SAM D21J18A)。 我还处于学习阶段,我还有很多工作要做,但我真的很喜欢嵌入式系统。

我有一些C背景。但是,这显然是不够的。 我正在查看Atmel的一个示例项目的代码,我并没有真正得到它的一些部分。 这是其中之一:

#define PORT ((Port *)0x41004400UL) /**< \brief (PORT) APB Base Address */ 

端口定义为:

  typedef struct { PortGroup Group[2]; /**< \brief Offset: 0x00 PortGroup groups [GROUPS] */ } Port; 

和PortGroup定义为:

 typedef struct { __IO PORT_DIR_Type DIR; /**< \brief Offset: 0x00 (R/W 32) Data Direction */ __IO PORT_DIRCLR_Type DIRCLR; /**< \brief Offset: 0x04 (R/W 32) Data Direction Clear */ __IO PORT_DIRSET_Type DIRSET; /**< \brief Offset: 0x08 (R/W 32) Data Direction Set */ __IO PORT_DIRTGL_Type DIRTGL; /**< \brief Offset: 0x0C (R/W 32) Data Direction Toggle */ __IO PORT_OUT_Type OUT; /**< \brief Offset: 0x10 (R/W 32) Data Output Value */ __IO PORT_OUTCLR_Type OUTCLR; /**< \brief Offset: 0x14 (R/W 32) Data Output Value Clear */ __IO PORT_OUTSET_Type OUTSET; /**< \brief Offset: 0x18 (R/W 32) Data Output Value Set */ __IO PORT_OUTTGL_Type OUTTGL; /**< \brief Offset: 0x1C (R/W 32) Data Output Value Toggle */ __I PORT_IN_Type IN; /**< \brief Offset: 0x20 (R/ 32) Data Input Value */ __IO PORT_CTRL_Type CTRL; /**< \brief Offset: 0x24 (R/W 32) Control */ __O PORT_WRCONFIG_Type WRCONFIG; /**< \brief Offset: 0x28 ( /W 32) Write Configuration */ RoReg8 Reserved1[0x4]; __IO PORT_PMUX_Type PMUX[16]; /**< \brief Offset: 0x30 (R/W 8) Peripheral Multiplexing n */ __IO PORT_PINCFG_Type PINCFG[32]; /**< \brief Offset: 0x40 (R/W 8) Pin Configuration n */ RoReg8 Reserved2[0x20]; } PortGroup; 

所以在这里,我们正在查看地址0x41004400UL,在那里获取数据,然后会发生什么?

我抬头看了这个,但找不到任何有用的东西。 如果您有任何建议(教程,书籍等),请让我听听。

通常,您可以通过以下方式访问C中的硬件寄存器:

 #define PORT (*(volatile uint8_t*)0x1234) 
  • 0x1234是寄存器地址
  • uint8_t是寄存器的类型,在这种情况下是1字节大。
  • volatile是必需的,因此编译器知道它不能优化这样的变量,但是每次读取或写入代码中声明的变量都必须实际完成。
  • (volatile uint8_t*)将整数文字强制转换为所需类型的地址。
  • 最左边的*然后获取该地址的内容,以便可以使用宏,就像PORT是常规变量一样。

请注意,这不会分配任何东西! 它只是假定给定地址存在硬件寄存器,可以通过指定的类型( uint8_t )访问。

使用相同的方法,您还可以使用其他C数据类型直接对应硬件寄存器。 例如,通过使用方便的结构,您可以映射特定硬件外设的整个寄存器区域。 然而,这样的代码有点危险和可疑,因为它必须考虑对齐/结构填充和别名等。


至于您的示例中的特定代码,它是特定微控制器上特定硬件外设(看起来像普通的通用I / O端口)的典型可靠寄存器映射。 通常为每个支持MCU的编译器提供一个这样的野兽。

遗憾的是,这种寄存器映射总是以可怕的,完全不可移植的方式编写。 例如,两个下划线__是C中的禁用标识符。编译器和程序员都不允许声明这样的标识符(7.1.3)。

真正奇怪的是他们省略了volatile关键字。 这意味着您在此处拥有以下方案之一:

  • volatile关键字隐藏在Port定义下方。 很可能就是这种情况,或者
  • 寄存器映射充满致命错误,或
  • 编译器是一个非常糟糕的废话,它根本不优化变量。 这将使volatile问题消失。

我会进一步研究这个问题。

对于struct padding和别名,编译器供应商可能隐含地假设只使用它们的编译器。 他们没有兴趣为您提供便携式寄存器映射,因此您可以为同一MCU切换竞争对手的编译器。

没有任何事情发生,因为你只提出一些声明。 我不完全确定问题究竟是什么,但要简要解释一下代码:

  • 0x41004400UL显然是端口启动的I / O空间(非常规存储器)中的地址(一组I / O寄存器)

  • 该端口由两组组成,具有类似的单个寄存器排列

  • struct PortGroup完全在硬件上的布局中对这些寄存器进行建模

要了解寄存器的含义,请查看硬件文档。