使用 CMake 构建 STM32 固件时的中断处理

问题描述

我调试了用 CMake 创建的 STM32 项目。它使用 HAL 库,一开始我配置了 HAL 和 SysTick。

root/
├─ core/
│  ├─ core.c
│  ├─ core.h
│  ├─ stm32g4xx_it.c
│  ├─ stm32g4xx_it.h
│  ├─ CMakeLists
├─ main.c
├─ startup.s
├─ CMakeLists.txt

startup.s 文件包含弱 SysTick_Handler,如下:

.weak   SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler

stm32g4xx_it.c 还包含覆盖弱声明的 SysTick_Handler。 问题是当编译、链接和加载应用程序时,如上所示,SysTick 中断以 Default_Handler 结束。如果我从 startup.s 文件删除弱声明,那么它可以正常工作并执行正确的处理程序函数。我认为这是 cmake 工作方式的结果。在根目录中有一个主要的 CMakeLists.txt 文件,它编译 startup.smain.c 文件,然后链接核心库。正确的 SysTick handler 放在核心库中,因此编译 startup.s 文件“看不到”此处理程序函数的其他实现。 解决它的正确而优雅的方法是什么?

我想到的另一个问题是我应该如何为其他中断处理程序编写代码以及如何在 CMake 构建系统中链接它们?假设我有一个 UART 模块,并且该模块有自己的中断处理程序。我应该创建一个基于 startup.s 文件的单独库并将所有其他具有中断处理程序的模块链接到它吗?我认为 CMake 应该使构建更加透明、可读和容易,但这样它看起来更像意大利面条。可能我遗漏了什么......


编辑 4/14/2021

我发现问题只与启动asm文件有关。例如,我可以在 main.c 中创建一个函数符号,例如:

__attribute__((weak)) void SystemInit(void)

在核心库中,在 core.c 中我创建函数

void SystemInit(void)

然后在编译和链接来自 core.c 的正确函数后使用。所以看起来这里有一些与 asm 文件严格相关的东西。

解决方法

这种链接器行为的原因是它以不同的方式对待库。当它在源文件中找到弱符号时,它会停止在库中寻找相同的符号。这样做的原因是库提供了一些功能,开发人员应该能够覆盖这些功能以使其与自己的项目保持一致。

无需在微控制器的嵌入式项目中使用库。我发现它会引起更多的疼痛。一些开发人员不在他们的 add_library() 中使用库 CMakeLists.txt。使用 CMake 时我觉得这不是很合理。它用于更好地组织项目文件和管理模块之间的依赖关系。

这里的问题可以通过使用 OBJECT 库来简单解决。这种类型的链接已在 3.12 版本中添加到 target_link_libraries。我认为它是专门为微控制器项目添加的,而且效果非常好。