以下代码取自LPC54618.h头文件:

typedef struct {
     //...structure elements
     __IO uint32_t SDIOCLKSEL;
     //...more elements

} SYSCON_Type;

#define SYSCON_BASE         (0x40000000u)
#define SYSCON              ((SYSCON_Type *)SYSCON_BASE)



据我所能猜测的含义

#define SYSCON ((SYSCON_Type *)SYSCON_BASE)


我假设它创建了一个名为SYSCON的指针,该指针指向类型为SYSCON_Type的变量,该变量存储在地址0x40000000u中。这真的发生了吗?是否有任何资源说明此处使用的语法(即在宏内定义指针)?


当我尝试直接更改SDIOCLKSEL的值时,即:

SYSCON->SDIOCLKSEL = some value;

我收到一个错误:

error: expected ')'error: expected parameter declaratorerror: expected ')'error: expected function body after function declarator


但是如果我在函数中使用它,例如:

void foo(void)
{
   SYSCON->SDIOCLKSEL = some value;
}


没有错误。这是为什么?为什么我不能直接写结构?

任何答案将不胜感激!

最佳答案

#define SYSCON_BASE         (0x40000000u)


这只是在物理地址0x40000000上列出。

#define SYSCON ((SYSCON_Type *)SYSCON_BASE)


这将通过强制转换将整数常量0x40000000u转换为指向struct的指针。它实际上并没有分配任何东西-实际的寄存器已经分配为内存映射的硬件。

简而言之,它说“在地址0x40000000处有一个硬件外设SYSCON”(无论是什么计时器?)。常见的情况是,MCU内有多个相同类型的硬件外设(许多SPI,ADC等),每个外设具有相同的寄存器布局,但位于不同的地址。我们可以为每个此类外设使用相同的结构类型,也可以使用相同的驱动程序代码。

该结构本身将具有一个内存映射,该映射对应于寄存器布局的100%。这里重要的是要确保填充/对齐不会搞砸,但希望MCU制造商已经想到了这一点(尽管不要认为这是理所当然的)。

假设SDIOCLKSEL的寄存器偏移量为0x10,那么当您键入SYSCON->SDIOCLKSEL = some value;时,将获得如下所示的机器代码(伪汇编代码):

LOAD 0x40000000 into index register X
LOAD 0x10 into register A
ADD A to X
MOVE some value into the address of X


(ARM获得了可以根据偏移量移动的特殊指令,因此实际机器代码中的指令可能更少。后续的寄存器访问可以使“ X”保持不变,并反复使用该基址来获得有效的代码。)

__IO限定符只是隐藏volatile的代码膨胀。

尝试“直接写入结构”时出现错误的原因仅仅是因为您无法在所有函数之外执行代码,而与该结构无关。

关于c - 了解#define预处理程序指令宏语法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56869716/

10-11 04:04