# Static_Module **Repository Path**: ufbycd/static_module ## Basic Information - **Project Name**: Static_Module - **Description**: 本项目在MCU(STM32)平台实现类似于Linux内核模块的动态加载功能,但不是将模块加载到内存(SRAM)而是加载到Flash。 所以叫做静态加载。 - **Primary Language**: C - **License**: LGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 17 - **Forks**: 11 - **Created**: 2020-12-26 - **Last Updated**: 2025-06-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Static Module Demo #### 介绍 本项目在MCU(STM32)平台实现类似于Linux内核模块的动态加载功能,但不是将模块加载到内存(SRAM)而是加载到Flash。 所以叫做静态加载,而被加载的模块就叫静态模块。 #### 软件架构 本演示项目有两个工程,分别是APP工程和Module工程。前者是常规应用工程,是芯片的入口;Module工程类似于静态库,不能单独运行而依赖于APP,但它编译生成的目标文件内所有符号均已链接。两个工程各自单独编译,烧录时也可以分开单独烧录。 ##### 软件架构说明 1. APP和Module的Flash和SRAM空间各自分开并且固定排布,Module空间位于APP空间之后并紧密相连; 2. APP和Mouule都有常规的中断向量表,并且都位于各自FLASH的起始地址; 3. APP的中断向量表是芯片的入口,而Module的中断向量表由APP调用; 4. APP和Module都各自维护一张公开的函数向量表:`APP_Vector_t`、`Module_Vector_t`;跨区域的函数调用即是通过函数向量表内的函数指针实现; 5. Module的`Reset_Handler`由于不是芯片的入口,所以可以修改其原型;APP在调用Module的Reset_Handler时获取模块的函数向量表并告知APP的函数向量表给模块;此时APP和Module都获取得到对方的函数向量表,两者也就连接起来了。 ##### Module的主要接口 Module由于不是程序主体而只是功能的扩展,所以不能执行带有死循环的函数,也就不能执行Module里的main函数。(本项目的Module工程里没有`main`函数实现。) 1. `Reset_Handler`:位于中断向量表内,依然是Module的入口,但供APP调用; 2. `init`:位于函数向量表内,用于执行整体(全局变量、外设等)的初始化; 3. `schedule`: 位于函数向量表内,相当于不带死循环的main函数。 ##### Module的Reset_Handler 常规的应用中,芯片复位时其程序入口为`Reset_Handler`。`Reset_Handler`要负责初始化C语言运行时环境(主要是DATA段及BSS段内变量的初始化)并调用main函数。 由于Module的`Reset_Handler`并不是芯片的入口而由APP调用,其实现就是整个架构的关键。本项目Module的Reset_Handler的实现如下: ``` void Reset_Handler(const APP_Vector_t *app, Module_Vector_t *module) { _initialize_data(& _sidata, & _sdata, & _edata); _initialize_bss(& _sbss, & _ebss); Module_SetAppVector(app); Module_GetModuleVector(module); } ``` 其主要工作有两个:初始化DATA段、BSS段及跟APP交换函数向量表。函数向量表一但确定,跨区域的函数调用就畅通无阻了。 ##### Module的中断处理函数的进入 为了在Module内灵活使用(APP未占用的)外设资源,需要处理Module的中断服务。而Module的中断向量表并不是芯片的入口而由APP调用,那么就需要从APP的中断向量表跳转到Module的中断向量表。 在APP的中断向量表内,未使用的中断服务其实都是`Default_Handler`的别名(并且都是弱(weak)符号)。而APP未使用的中断有可能会被Module使用;所以只要修改APP的`Default_Handler`,使其跳转到Module那边就行了。本项目的APP的`Default_Handler`实现如下: ``` void __attribute__ ((weak)) Default_Handler(void) { typedef void (* IrqHandler_t)(void); extern unsigned int _eflash; // 来自链接脚本,指向Flash末尾 uint32_t isrNum; IrqHandler_t *moduleIsrVector; IrqHandler_t handler; if(Main_IsModuleLoaded()) { moduleIsrVector = (IrqHandler_t *) & _eflash; isrNum = __get_IPSR(); handler = moduleIsrVector[isrNum]; handler(); } else { __DEBUG_BKPT(); while(1); } } ``` * 其中`_eflash`定义于链接脚本,其值指向APP的Flash空间的末尾即Module的Flash的起始亦即Module的中断向量表。 * `Default_Handler`的主要工作为:当Module已经载入时,获取当前的中断号并直接跳转到Module的中断服务函数;当Module未载入时,则进行常规处理,进入死循环。 #### 使用说明 1. 本项目基于STM32CubeIDE实现,硬件平台为NUCLEO-F446RE,即MCU为STM32F446RE。在IDE内很容易就可以切换到其他硬件平台。 2. APP和Module工程都包含一个跟git代码仓库挂钩的版本头文件version.h,这个文件是在编译时自动通过python脚本(version.py)生成的。所以要保证电脑内安装有python解释器才能通过编译。(PS:Linux和Windows 10已内置python)。 #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)