我听说过 gcc 中的
__attribute__((naked))
,这是将一些代码放入您不想进行额外调用的部分的常见方法。
例如在 AVR 中,您经常可以找到这样的代码存根:
static void __preinit(void) __attribute__((used, naked, section(".init3")));
void __preinit(void) {
wdt_disable();
RESET_Flags = MCUSR;
MCUSR = 0;
}
此代码将放置在
.init3
部分,这是芯片初始化的部分,甚至在RAM初始化之前(.bss
和.data
)。这是通过 RAM 初始化所必需的,因为否则看门狗将在 RAM 初始化例程中启动并创建引导循环。
基于此我有一个想法。在我的程序中,我有很多模块,它们总是有 3 个功能:
*_init
、*_main
和 *_tick
。在这种情况下,main.c 变得一团糟:
int main() {
init();
while(1) {
loop();
}
}
void init() {
mod1_init();
mod2_init();
...
modN_init();
}
void loop() {
mod1_main();
mod2_main();
...
modN_main();
}
// Same goes for systick handler
如果你忘记调用某个模块,调试就变成无用的浪费时间。 我想为我的项目创建一个平台,您只需在其中插入模块的 c 文件,它们就会被自动调用。就像与汇编相比,您不需要手动分配 RAM 的方式一样。
如果我定义所有模块内函数放置在相应的部分中会怎样:
static void modX_init() __attribute__((used, naked, section(".mod_init")));
static void modX_main() __attribute__((used, naked, section(".mod_main")));
static void modX_tick() __attribute__((used, naked, section(".mod_tick")));
我可以将这些函数合并到某种部分中,可以使用链接器将其加入:
.text :
{
. = ALIGN(4);
*(.text)
*(.text*)
*(.init_epilogue)
*(.init_epilogue*)
*(.mod_init)
*(.mod_init*)
*(.init_prologue)
*(.init_prologue*)
...
} >ROM
但是我如何进入这些部分呢?我应该自己写序言和尾声吗?我怎么知道应该将哪些寄存器压入堆栈?
目前我使用ARM微控制器,所以这里的AVR只是作为示例。
有没有更自动化的方法来完成我的计划?
我如何进入这些部分?
只需将指针存储在节中即可。
以
.
开头的部分在技术上是为链接器保留的。无论如何,每个人都使用它们。
仅考虑:
// module.h
struct module {
void (*init)();
void (*main)();
};
#define CONCAT(a, b) a##b
#define XCONCAT(a, b) CONCAT(a, b)
#define MODULE_REGISTER(init, main) \
__attribute__((__section__("modules"))) \
static const struct module XCONCAT(_module_, __LINE__) = \
{ init, main }
extern struct module __start_modules;
extern struct module __stop_modules;
#define MODULE_FOREACH(VAR) \
for (const struct module *VAR = __start_modules; \
VAR != __stop_modules; \
++VAR)
// module1
MODULE_REGISTER(mod1_init, mod1_main);
// main
void init() {
MODULE_FOREACH(i) {
i->init();
}
}
void loop() {
MODULE_FOREACH() {
i->main();
}
}