宏扩展设置位

Luc*_*cas 1 c embedded macros arm atmel

我正在编写Atmel的AT91SAM7X256,我对定义寄存器的宏以及如何使用它们感到困惑.

我使用如下行来设置寄存器中的位:

AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PWMC;
Run Code Online (Sandbox Code Playgroud)

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

((AT91PS_PMC) 0xFFFFFC00)->PMC_PCER = 1 << (10);
Run Code Online (Sandbox Code Playgroud)

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

在文件的顶部:

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

然后我发现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
...
Run Code Online (Sandbox Code Playgroud)

所以看起来这个扩展非常简单.类似地,宏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
...
Run Code Online (Sandbox Code Playgroud)

但解除引用((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
Run Code Online (Sandbox Code Playgroud)

并再次下来.

#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
Run Code Online (Sandbox Code Playgroud)

所以我的问题是

  • 这些宏如何工作?
  • 我的印象是,我可以用表达式AT91C_BASE_PMC-> PMC_PCER = 1 << AT91C_ID_PWMC设置一个位; ?这是真的?
  • 我怎么会取消这一点?
  • 为什么这样做很复杂而不是简单地直接设置值?

Jon*_*ler 6

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

  1. 转换((AT91PS_PMC) 0xFFFFFC00)意味着内存地址0xFFFFFC00将被视为指向该类型结构的指针AT91PS_PMC.在该结构中,外围时钟使能寄存器是该PMC_PCER字段,与地址0xFFFFFC00有一些合适的偏移量.

    因此,赋值安排将1024(1 << 10)写入PMC_PCER寄存器,假设基址0xFFFFFC00是正确的.

  2. 这将寄存器设置为包含单个设置位的值.它与大多数人所谓的"设置一位"的意思不同; 这将通过如下行来完成:

    AT91C_BASE_PMC->PMC_PCER |= 1 << AT91C_ID_PWMC;
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,这将读取PMC_PCER的当前值,修改它并将其写回.读取和写入是否安全取决于芯片的细节.

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

    AT91C_BASE_PMC->PMC_PCER = 0;
    AT91C_BASE_PMC->PMC_PCER &= ~(1 << AT91C_ID_PWMC);
    
    Run Code Online (Sandbox Code Playgroud)

    第一个假设只设置了一个位,因此将该值设置为0将取消该单个位的设置.第二个读取PMC_PCER寄存器的当前值,按位AND进行除了AT91C_ID_PWMC位之外的任何位置都有1位的值(因此将该位置零),并将值写回.

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