我有一些使用GCC构建的固件,可以在基于ARM Cortex M0的微控制器上运行。构建当前生成一个二进制映像,可以写入微控制器的程序存储器中。
由于与字段更新有关的原因,我需要将此图像分成两个部分,可以单独更新。我会将这些核心和应用称为。
核心:包含中断向量表,main()
例程以及各种驱动程序和库例程。它将位于程序存储器的前半部分。
应用:包含特定于应用程序的代码。它将位于程序存储器的后半部分。它将在一个已知地址处有一个入口点,由核心调用以启动应用程序。它将通过已知地址访问核心中的函数和数据。
这里有一些明显的限制,我很清楚:
构建应用时,需要知道核心中符号的地址。因此必须首先构建核心,并且在链接应用程序时必须可用。
应用图片只会与其构建的特定核心图片兼容。
可以在不更新核心的情况下更新应用程序,反之亦然。
所有这一切都没问题。
我的问题很简单,如何使用GCC和GNU binutils构建这些图像?
基本上我想像正常的固件映像一样构建核心,然后构建应用程序映像,应用程序将核心视为库。但是,共享链接(这将需要动态链接机制)或静态链接(将复制用于应用程序二进制文件的核心功能)都不适用于此处。我尝试做的事情实际上要简单得多:使用已知的固定地址链接现有的二进制文件。我不清楚如何使用这些工具。
答案 0 :(得分:3)
我们现在有这个工作所以我将回答我自己的问题。这是必要的,从正常的单一图像构建开始,将其转换为"核心"然后为" app"。
设置构建决定如何将闪存和RAM分成核心和应用程序的单独区域。定义每个区域的起始地址和大小。
为核心创建链接描述文件。这将与平台的标准链接描述文件相同,只是它必须仅使用为核心保留的区域。这可以通过更改闪存和放大器的ORIGIN
和LENGTH
来完成。链接描述文件的MEMORY
部分中的RAM条目。
创建一个头文件,声明应用的入口点。这只需要一个原型,例如:
void app_init(void);
。
从核心C代码中包含此标头,并使用核心调用app_init()
启动该应用。
创建一个符号文件,声明入口点的地址,该地址将是应用程序闪存区域的起始地址。我打电话给app.sym
。它可以是以下格式的一行:
app_init = 0x00010000;
使用核心链接描述文件构建核心,并将--just-symbols=app.sym
添加到链接器参数,以提供app_init
的地址。保留构建中的ELF文件,我将调用core.elf
。
为应用创建链接描述文件。这将再次基于该平台的标准链接器脚本,但使用闪存和放大器。 RAM内存范围已更改为为应用程序保留的范围。此外,还需要一个特殊部分,以确保在app_init
部分的其余代码之前将.text
放置在应用内Flash区域的开头:
SECTIONS { .text : { KEEP(*(.app_init)) *(.text*)
编写app_init
函数。这将需要在汇编中,因为它必须在应用程序中的任何C代码被调用之前执行一些低级别的工作。它需要标记为.section .app_init
,以便链接器将其放在应用程序闪存区域开头的正确位置。 app_init
函数需要:
.data
部分填充变量。.bss
部分中的变量设置为零。app_start()
。编写启动应用程序的app_start()
函数。
使用app链接描述文件构建应用程序。此链接步骤应传递包含app_init
,app_start
以及app_start
调用的任何尚未包含在核心中的代码的目标文件。链接器参数--just-symbols=core.elf
应通过其地址传递给核心中的链接函数。此外,应传递-nostartfiles
以省略正常的C运行时启动代码。
花了一段时间来解决这一切,但它现在运作良好。
答案 1 :(得分:0)
首先......如果仅用于字段更新,则无需依赖核心空间中的中断向量表应用 。我认为 ARM M0部件总是能够移动它。我知道它可以在一些(全部?)STM32Fx的东西上完成,但我相信这是一个ARM M-x的东西,而不是ST的东西。在决定让你的应用程序ISR都是从核心调用的钩子之前,先看看这个。
如果你打算与你的核心进行大量的交互(顺便说一句,我总是把那些在MCU上自我更新的片段称为“bootloader”),这是另一个建议:
让 Core 将指向功能的struct / table的指针传递到 App 入口点?
这将允许完全分离应用程序与核心的代码,除了共享标头(假设您的ABI不会更改)并防止名称冲突。
它还提供了一种合理的方法来防止GCC优化掉您可能只从 App 调用的任何函数,而不会弄乱您的优化设置或搞砸pragma。
core.h:
struct core_functions
{
int (*pcore_func1)(int a, int b);
void (*pcore_func2)(void);
};
core.c:
int core_func1(int a, int b){ return a + b; }
void core_func2(void){ // do something here }
static const struct core_functions cfuncs=
{
core_func1,
core_func2
};
void core_main()
{
// do setup here
void (app_entry*)(const struct core_functions *) = ENTRY_POINT;
app_entry( &cfuncs );
}
app.c
void app_main(const struct core_functions * core)
{
int res;
res = core->pcore_func1(20, 30);
}
下行/成本是一个轻微的运行时间&内存开销和更多代码。