((Port *)0x41004400UL) 在这里是什么意思? [英] What does ((Port *)0x41004400UL) mean here?

查看:65
本文介绍了((Port *)0x41004400UL) 在这里是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发板上有一个基于 32 位 ARM 的微控制器(即该板是 Atmel SAM D21J18A).我仍处于学习阶段,还有很多事情要做,但我真的很喜欢嵌入式系统.

I'm working on a developing board that has a 32-bit ARM based microntroller on it (namely the board is Atmel SAM D21J18A). I'm still at the learning phase and I have a lot to go, but I'm really into embedded systems.

我有一些 C 语言背景.但是,这显然还不够.我正在查看 Atmel 示例项目的代码,但我并没有真正了解其中的某些部分.这是其中之一:

I have some background in C. However, it's obviously not enough. I was looking at the codes of an example project by Atmel, and I didn't really get some parts of it. Here is one of them:

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

端口定义为:

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

和端口组定义为:

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,在那里获取数据,然后会发生什么?

So here, we are looking at the address 0x41004400UL, get the data in there, and then what happens?

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

I looked up for this but couldn't find anything useful. If you have any suggestions (tutorials, books etc.), please let me hear.

推荐答案

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

Generally you can access a hardware register in C in this manner:

#define PORT  (*(volatile uint8_t*)0x1234)

  • 0x1234 是寄存器地址
  • uint8_t 是寄存器的类型,在本例中为 1 个字节.
  • volatile 是必需的,以便编译器知道它无法优化这样的变量,但是必须实际完成对代码中声明的变量的每次读取或写入.
  • (volatile uint8_t*) 将整数文字转换为所需类型的地址.
  • 最左边的 * 然后获取该地址的内容,这样宏就可以像 PORT 是一个常规变量一样使用.
    • 0x1234 is the register address
    • uint8_t is the type of the register, in this case 1 byte large.
    • volatile is required so that the compiler knows it cannot optimize such a variable, but that each read or write to the variable stated in the code must actually be done.
    • (volatile uint8_t*) casts the integer literal to an address of the desired type.
    • The left-most * then take the contents of that address, so that the macro can be used just as if PORT was a regular variable.
    • 注意,这不会分配任何东西!它只是假设在给定地址处存在一个硬件寄存器,可以通过指定的类型 (uint8_t) 访问该寄存器.

      Note that this does not allocate anything! It just assumes that there is a hardware register present at the given address, which can be accessed by the type specified (uint8_t).

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

      Using the same method you can also have other C data types to correspond directly hardware registers. For example by using a handy struct, you can map the whole register area of a particular hardware peripheral. Such code is however a bit dangerous and questionable, since it must take things like alignment/struct padding and aliasing in account.

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

      As for the specific code in your example, it is a typical awful register map for a particular hardware peripheral (looks like a plain general-purpose I/O port) on a certain microcontroller. One such beast is typically provided with each compiler supporting the MCU.

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

      Such register maps are sadly always written in awful, completely non-portable ways. For example, two underscores __ is a forbidden identifier in C. Neither the compiler nor the programmer is allowed to declare such identifiers (7.1.3).

      真正奇怪的是他们省略了 volatile 关键字.这意味着您在这里遇到了以下场景之一:

      What's really strange is that they have omitted the volatile keyword. This means that you have one of these scenarios here:

      • volatile 关键字隐藏在 Port 定义之下.很可能是这种情况,或者
      • 寄存器映射充满了致命的错误,或者
      • 编译器太糟糕了,根本不优化变量.这将使 volatile 的问题消失.
      • The volatile keyword is hidden beneath the Port definition. Most likely this is the case, or
      • The register map is full of fatal bugs, or
      • The compiler is such an awful piece of crap that it doesn't optimize variables at all. Which would make the issues with volatile go away.

      我会进一步调查这个.

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

      As for struct padding and aliasing, the compiler vendor has likely implicitly assumed that only their compiler is to be used. They have no interest in providing you with a portable register map, so that you can switch the the competitor's compiler for the same MCU.

      这篇关于((Port *)0x41004400UL) 在这里是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆