0

我正在开发一个带有 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,获取那里的数据,然后会发生什么?

我查找了这个,但找不到任何有用的东西。如果您有任何建议(教程、书籍等),请告诉我。

4

2 回答 2

2

什么都没有发生,因为您只提出了一些声明。我不完全确定问题实际上是什么,但简要解释一下该代码:

  • 0x41004400UL显然是 I/O 空间(不是常规内存)中端口开始的地址(一组 I/O 寄存器)

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

  • structPortGroup完全按照硬件上的布局对这些寄存器进行建模

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

于 2015-07-07T08:07:30.053 回答
0

通常,您可以通过以下方式访问 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消失。

我将对此进行进一步调查。

至于结构填充和别名,编译器供应商可能已经隐式假设只使用他们的编译器。他们没有兴趣为您提供可移植的寄存器映射,以便您可以为同一个 MCU 切换竞争对手的编译器。

于 2015-07-07T11:01:16.737 回答