问题描述
我目前正在查看其他人编写的 ARM Cortex-M 微控制器的一些启动代码。可以在此 Github repo 中找到整个文件。
它可以使用一些简单的 for 循环来设置堆栈指针并适当地初始化 .data
和 .bss
部分。
我正在努力理解用于定义中断向量表的语法:
#define DUMMY __attribute__ ((weak,alias ("irq_handler_dummy")))
//-----------------------------------------------------------------------------
void irq_handler_reset(void);
DUMMY void irq_handler_nmi(void);
DUMMY void irq_handler_hard_fault(void);
// etc.
extern int main(void);
extern void _stack_top(void);
// etc.
//-----------------------------------------------------------------------------
__attribute__ ((used,section(".vectors")))
void (* const vectors[])(void) =
{
&_stack_top,// 0 - Initial Stack Pointer Value
// Cortex-M0+ handlers
irq_handler_reset,// 1 - Reset
irq_handler_nmi,// 2 - NMI
irq_handler_hard_fault,// 3 - Hard Fault
// etc.
};
GCC 的 __attribute__
定义很明确,我在官方文档中找到了它的答案:GCC Function attributes。
我仍然不知道如何解析和表达这种语法的含义
void (* const vectors[])(void)
有人能帮我理解所有这些语法解包或代表什么吗?
解决方法
vectors
是一个 const 函数指针数组,它们接受并返回 void
。
_stack_top
不是函数指针,它是栈顶的地址,但对于皮层 m,它始终是向量表中的第一个元素。
Cortex M 架构以及您正在使用的它的实现定义了向量表的顺序和位置。这段代码是一些语法糖,用于生成表格并将其放置在正确的位置。
,向量表本质上只是一个 ISR 地址数组。其中翻译成C可以看作是一个函数指针数组。将向量表创建为函数指针数组是很常见的。
使 Cortex M 成为特殊雪花的原因是它通过硬件从闪存加载堆栈指针,而不是程序员在运行时手动设置它。向量表的第一项包含初始堆栈指针的值——它实际上不是一个函数地址。因此,某种方式的 hack 是必要的。 _stack_top
可能会归结为链接描述文件中设置的某个堆栈地址。您的代码永远不会直接使用这个项目,它只是在那里,以便在启动时正确设置堆栈。
除此之外,其余的只是指向 ISR 的普通函数指针。由于 ISR 不带参数并返回值,因此 ISR 函数指针的语法为:
void (*name) (void)
这样的函数指针数组声明为:
void (*name [n]) (void)
其中 n
可选择性地用于表示数组大小。
__attribute__ ((used,section(".vectors")))
只是将数组放置在特定地址,在这种情况下,从 0 开始。您可以检查链接器脚本,您会在那里找到 .vectors
。
我们希望这个向量表作为只读数据加载到闪存中。因此我们希望指针是只读的,而不是它们所指向的。这是通过将 const
放在 *
的右侧来实现的(同样的规则也适用于普通对象指针):
void (*const vectors[])(void)
如果使用了 typedef
,我们本可以写得更易读:
typedef void isr_vector_t (void);
...
isr_vector_t* const vectors[] = { ... };
,
vectors -- vectors
vectors[] -- is an array of
* const vectors[] -- const pointer to
(* const vectors[])( ) -- function taking
(* const vectors[])(void) -- no parameters
void (* const vectors[])(void) -- returning void
IOW,每个vectors[i]
都是一个函数指针;所指向的函数在初始化程序中指定:
vectors[1] == irq_handler_reset,// 1 - Reset
vectors[2] == irq_handler_nmi,// 2 - NMI
vectprs[3] == irq_handler_hard_fault,// 3 - Hard Fault
const
后面的 *
表示 vector[i]
初始化后无法更新; IOW,您不能将 vectors[1]
设置为指向 irq_handler_reset
以外的函数。
const T *p; // you can update p to point to different objects,but
// you cannot write to the pointed-to objects
T const *p; // same as above
T * const p = some_addr; // you can write a new value to the object at
// some_addr,but you can't write a new value
// to p.
,
void (* const vectors[])(void)
它是函数指针的const数组的定义和声明,引用了不带参数且不返回任何值的函数。
它通过对源代码中定义的中断处理程序(函数)的引用进行初始化。第一个值采用链接描述文件中定义的符号 _stack_top
的地址。该值将在微启动时设置初始堆栈指针。
具有 weak
链接的函数可以被其他函数替换,而不会产生链接器错误。