【问题标题】:Macro expansion to set bits设置位的宏扩展
【发布时间】:2013-08-15 12:10:25
【问题描述】:

我正在对来自 Atmel 的 AT91SAM7X256 进行编程,但我对定义寄存器的宏以及如何使用它们感到有些困惑。

我使用如下行在寄存器中设置一个位:

AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PWMC;

现在,当我在宏扩展之后查看它时,它看起来像这样:

((AT91PS_PMC) 0xFFFFFC00)->PMC_PCER = 1 << (10);

我可以在文件 AT91SAM7X256.h 中找到的定义

在文件顶部:

#ifndef __ASSEMBLY__
typedef volatile unsigned int AT91_REG;// Hardware register definition
#define AT91_CAST(a) (a)
#else
#define AT91_CAST(a)
#endif

然后我发现 AT91C_BASE_PMC 的定义进一步向下

...
#define AT91C_BASE_CKGR      (AT91_CAST(AT91PS_CKGR)    0xFFFFFC20) // (CKGR) Base Address
#define AT91C_BASE_PMC       (AT91_CAST(AT91PS_PMC)     0xFFFFFC00) // (PMC) Base Address
#define AT91C_BASE_RSTC      (AT91_CAST(AT91PS_RSTC)    0xFFFFFD00) // (RSTC) Base Address
...

所以看起来这个扩展非常简单。同样,宏 AT91C_ID_PWMC 定义为

...
#define AT91C_ID_TWI    ( 9) // Two-Wire Interface
#define AT91C_ID_PWMC   (10) // PWM Controller
#define AT91C_ID_UDP    (11) // USB Device Port
...

但是解引用 ((AT91PS_PMC) 0xFFFFFC00)->PMC_PCER 是如何工作的?我可以找到 PMC_PCER 的一些定义。

#ifndef __ASSEMBLY__
typedef struct _AT91S_SYS {
    ...
    AT91_REG     Reserved20[1];     // 
    AT91_REG     PMC_PCER;  // Peripheral Clock Enable Register
    AT91_REG     PMC_PCDR;  // Peripheral Clock Disable Register
    ...
} AT91S_SYS, *AT91PS_SYS;
#else

#endif

然后再往下走。

#ifndef __ASSEMBLY__
typedef struct _AT91S_PMC {
    ...
    AT91_REG     Reserved0[1];  // 
    AT91_REG     PMC_PCER;  // Peripheral Clock Enable Register
    AT91_REG     PMC_PCDR;  // Peripheral Clock Disable Register
    ...
} AT91S_PMC, *AT91PS_PMC;
#else
...
#define PMC_SCSR        (AT91_CAST(AT91_REG *)  0x00000008) // (PMC_SCSR) System Clock Status Register
#define PMC_PCER        (AT91_CAST(AT91_REG *)  0x00000010) // (PMC_PCER) Peripheral Clock Enable Register
#define PMC_PCDR        (AT91_CAST(AT91_REG *)  0x00000014) // (PMC_PCDR) Peripheral Clock Disable Register
...
#endif

所以我的问题是

  • 这些宏是如何工作的?
  • 我的印象是,我可以使用表达式 AT91C_BASE_PMC->PMC_PCER = 1
  • 如何取消设置该位?
  • 为什么这么复杂而不是直接设置值?

【问题讨论】:

    标签: c macros embedded arm atmel


    【解决方案1】:

    它不是可移植的代码,但它说明了 C 的强大功能以及它与内存映射 I/O 设备接口的能力。

    1. 转换((AT91PS_PMC) 0xFFFFFC00) 意味着内存地址0xFFFFFC00 将被视为指向AT91PS_PMC 类型结构的指针。在该结构中,有一个外设时钟启用寄存器,PMC_PCER 字段,位于地址 0xFFFFFC00 的某个适当偏移处。

      因此,假设基地址 0xFFFFFC00 正确,分配安排将 1024 (1 &lt;&lt; 10) 写入 PMC_PCER 寄存器。

    2. 这会将寄存器设置为包含单个设置位的值。这与大多数人所说的“设置一个位”不同。这将通过如下一行来完成:

      AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_PWMC;
      

      请注意,这会读取 PMC_PCER 的当前值,对其进行修改,然后将其写回。读写是否安全取决于芯片的细节。

    3. 至少有两种方法可以做到这一点:

      AT91C_BASE_PMC->PMC_PCER = 0;
      AT91C_BASE_PMC->PMC_PCER &= ~(1 << AT91C_ID_PWMC);
      

      第一个假设只设置了一个位,因此将值设置为 0 会取消设置该单个位。第二个读取 PMC_PCER 寄存器的当前值,对除 AT91C_ID_PWMC 位以外的所有位置都有 1 位的值进行按位与运算(因此仅将该位归零),然后将值写回。

    4. 为什么会这样?将所有特定于平台的详细信息收集在一起,避免必须手动编写每个地址,以便以后可以修改电路板并重新编译代码,而不必重新编写。

    【讨论】:

    • 谢谢。很好的解释。我意识到我对单个位感到困惑,因为我正在写入启用功能的只写寄存器,但单独的寄存器会禁用该功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    相关资源
    最近更新 更多