了解#define 预处理器指令宏语法
Understanding #define preprocessor directive macro syntax
以下代码摘自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
的指针,该指针指向存储在地址 0x40000000u
的 SYSCON_Type
类型的变量。这真的发生了吗?是否有任何资源可以解释此处使用的语法(即在宏中定义指针)?
当我试图直接改变 SDIOCLKSEL 的值时,即:
SYSCON->SDIOCLKSEL = some value;
我得到一个错误:
error: expected ')'
error: expected parameter declarator
error: expected ')'
error: expected function body after function declarator
但是如果我在一个函数中使用它,例如:
void foo(void)
{
SYSCON->SDIOCLKSEL = some value;
}
没有错误。这是为什么?为什么我不能直接写入结构?
任何答案将不胜感激!
非常简单。
that it creates a pointer named SYSCON that points to a variable of
type SYSCON_Type which is stored at the address 0x40000000u. Is this
really what happens?
是也不是。当你使用宏 SYSCON
void foo(uint32_t value)
{
SYSCON->SDIOCLKSEL = value;
}
预处理器转换为:
void foo(uint32_t value)
{
((SYSCON_Type *)0x40000000u)->SDIOCLKSEL = value;
}
将 32 位无符号 value
写入地址为 0x40000000u + 结构成员偏移量的内存位置。
通常用于访问映射到内存地址space的硬件寄存器。
你需要在函数内部完成(C语言中的所有代码)
#define SYSCON_BASE (0x40000000u)
这只是列出物理地址 0x40000000
。
#define SYSCON ((SYSCON_Type *)SYSCON_BASE)
这通过强制转换将整数常量 0x40000000u
转换为指向结构的指针。它实际上并没有分配任何东西——实际的寄存器已经分配为 memory-mapped 硬件。
简单地说,它表示 "at address 0x40000000 there's a hardware peripheral SYSCON
"(不管那是什么,某个计时器?)。这是一种常见的情况,您在 MCU 中有多个相同类型的硬件外设(许多 SPI、ADC 等),每个都具有相同的寄存器布局,但位于不同的地址。我们可以为每个这样的外围设备使用相同的结构类型,也可以使用相同的驱动程序代码。
结构本身将有一个与寄存器布局 100% 对应的内存映射。这里重要的是要确保 padding/alignment 不会搞砸,但希望 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 有特殊指令可以根据偏移量移动 etc,因此实际机器代码中的指令可能更少。后续的寄存器访问可以保持 "X" 不变并重复使用该基地址,因为有效代码。)
__IO
限定符只是代码膨胀隐藏 volatile
。
你尝试"write directly into the structure"时出错的原因很简单,你不能执行所有函数之外的代码,它与这个结构无关。
以下代码摘自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
的指针,该指针指向存储在地址 0x40000000u
的 SYSCON_Type
类型的变量。这真的发生了吗?是否有任何资源可以解释此处使用的语法(即在宏中定义指针)?
当我试图直接改变 SDIOCLKSEL 的值时,即:
SYSCON->SDIOCLKSEL = some value;
我得到一个错误:
error: expected ')' error: expected parameter declarator error: expected ')' error: expected function body after function declarator
但是如果我在一个函数中使用它,例如:
void foo(void)
{
SYSCON->SDIOCLKSEL = some value;
}
没有错误。这是为什么?为什么我不能直接写入结构?
任何答案将不胜感激!
非常简单。
that it creates a pointer named SYSCON that points to a variable of type SYSCON_Type which is stored at the address 0x40000000u. Is this really what happens?
是也不是。当你使用宏 SYSCON
void foo(uint32_t value)
{
SYSCON->SDIOCLKSEL = value;
}
预处理器转换为:
void foo(uint32_t value)
{
((SYSCON_Type *)0x40000000u)->SDIOCLKSEL = value;
}
将 32 位无符号 value
写入地址为 0x40000000u + 结构成员偏移量的内存位置。
通常用于访问映射到内存地址space的硬件寄存器。
你需要在函数内部完成(C语言中的所有代码)
#define SYSCON_BASE (0x40000000u)
这只是列出物理地址 0x40000000
。
#define SYSCON ((SYSCON_Type *)SYSCON_BASE)
这通过强制转换将整数常量 0x40000000u
转换为指向结构的指针。它实际上并没有分配任何东西——实际的寄存器已经分配为 memory-mapped 硬件。
简单地说,它表示 "at address 0x40000000 there's a hardware peripheral SYSCON
"(不管那是什么,某个计时器?)。这是一种常见的情况,您在 MCU 中有多个相同类型的硬件外设(许多 SPI、ADC 等),每个都具有相同的寄存器布局,但位于不同的地址。我们可以为每个这样的外围设备使用相同的结构类型,也可以使用相同的驱动程序代码。
结构本身将有一个与寄存器布局 100% 对应的内存映射。这里重要的是要确保 padding/alignment 不会搞砸,但希望 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 有特殊指令可以根据偏移量移动 etc,因此实际机器代码中的指令可能更少。后续的寄存器访问可以保持 "X" 不变并重复使用该基地址,因为有效代码。)
__IO
限定符只是代码膨胀隐藏 volatile
。
你尝试"write directly into the structure"时出错的原因很简单,你不能执行所有函数之外的代码,它与这个结构无关。