以下代码取自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/