取巧不如 守拙

是不是自己太“聪明”了,一直没有什么成效。

应该更笨一些,做事不要想怎么快,怎么舒服,怎么优雅,认真完成是王道,坚持专注是王道!


内容概要:本文档详细介绍了Analog Devices公司生产的AD8436真均方根-直流(RMS-to-DC)转换器的技术细节及其应用场景。AD8436由三个独立模块构成:轨到轨FET输入放大器、高动态范围均方根计算内核和精密轨到轨输出放大器。该器件不仅体积小巧、功耗低,而且具有广泛的输入电压范围和快速响应特性。文档涵盖了AD8436的工作原理、配置选项、外部组件选择(如电容)、增益调节、单电源供电、电流互感器配置、接地故障检测、三相电源监测等方面的内容。此外,还特别强调了PCB设计注意事项和误差源分析,旨在帮助工程师更好地理解和应用这款高性能的RMS-DC转换器。 适合人群:从事模拟电路设计的专业工程师和技术人员,尤其是那些需要精确测量交流电信号均方根值的应用开发者。 使用场景及目标:①用于工业自动化、医疗设备、电力监控等领域,实现对交流电压或电流的精准测量;②适用于手持式数字万用表及其他便携式仪器仪表,提供高效的单电源解决方案;③在电流互感器配置中,用于检测微小的电流变化,保障电气安全;④应用于三相电力系统监控,优化建立时间和转换精度。 其他说明:为了确保最佳性能,文档推荐使用高质量的电容器件,并给出了详细的PCB布局指导。同时提醒用户关注电介质吸收和泄漏电流等因素对测量准确性的影响。
<think>我们讨论的是在嵌入式开发中,如何利用链接器脚本将分散在不同源文件中的函数入口地址自动收集到一个连续的内存区域中,从而避免手动维护函数指针数组。这种方法常用于初始化函数的自动执行(如外设初始化、模块注册等)。根据引用[1]的内容,GCC编译器会将代码的不同部分(RO、RW、ZI)分配到不同的段。其中,RO段(.text)存放程序代码。我们可以利用链接器脚本自定义一个段(比如叫.init_array),然后将需要自动执行的函数放入这个段中。这样,链接器会自动将这些函数的地址连续排列在指定的内存区域。具体步骤如下:1.**在链接器脚本中定义自定义段**:在链接器脚本中,我们可以定义一个输出段(例如.init_array),并指定它的起始地址和结束地址。这样我们可以通过两个符号(如__init_array_start和__init_array_end)来访问这个段的开始和结束。2.**使用函数属性将函数放入自定义段**:在函数声明时,使用编译器特定的属性(如GCC的__attribute__((section(".init_array"))))将函数放入我们定义的段中。注意,这里我们实际上应该存放函数指针,而不是函数本身。但是,我们可以利用编译器特性将函数指针(而不是函数代码)放入指定的段。然而,需要注意的是,通常我们不会将函数的代码放入自定义段,而是将函数的地址(即函数指针)放入一个数组。我们可以通过以下方式实现:-定义一个函数指针类型。-使用一个特定的宏来声明函数,并同时将该函数的指针放入指定的段。3.**在启动代码中遍历执行**:在程序启动时(如复位处理函数中),通过访问__init_array_start和__init_array_end之间的函数指针,依次调用这些函数。具体实现:**步骤1:修改链接器脚本**在链接器脚本(如STM32的链接脚本.ld文件)中,添加以下内容:```ld/*定义初始化函数数组段*/.init_array:{.=ALIGN(4);__init_array_start=.;KEEP(*(.init_array))/*保留所有.init_array段的内容*/.=ALIGN(4);__init_array_end=.;}>FLASH```这里将段放置在FLASH中(因为函数指针是常量,应存放在只读存储器中)。**步骤2:在代码中声明初始化函数**对于每个需要自动执行的函数,我们通过宏和函数属性将其指针放入段中。在头文件中定义宏:```ctypedefvoid(*init_function_t)(void);#defineINIT_FUNCTION(fn)\staticconstinit_function_t_init_##fn__attribute__((used,section(".init_array")))=fn```然后,在每个需要自动执行的函数定义之后,使用这个宏进行注册:```cvoidinit_uart(void){//初始化UART}INIT_FUNCTION(init_uart);voidinit_spi(void){//初始化SPI}INIT_FUNCTION(init_spi);```这样,每个INIT_FUNCTION宏会创建一个指向该函数的指针,并将这个指针放入段`.init_array`中。由于我们使用了`KEEP`,链接器不会优化掉这些指针。**步骤3:在启动代码中调用**在启动代码中(如复位处理函数或main函数之前的初始化代码),添加以下代码来遍历并执行这些函数:```cexterninit_function_t__init_array_start[];externinit_function_t__init_array_end[];voidcall_init_functions(void){for(init_function_t*func=__init_array_start;func<__init_array_end;func++){if(*func){(*func)();}}}```然后在系统初始化时调用`call_init_functions`函数。**注意事项**:1.函数执行顺序:链接器按照目标文件中出现的顺序链接,因此函数指针在数组中的顺序可能与编译顺序有关。如果需要控制顺序,可以通过链接脚本中的输入段顺序来控制,或者使用优先级(如将段名改为`.init_array.0`、`.init_array.1`等,并在链接脚本中按优先级排列)。2.函数指针类型:确保所有初始化函数的类型一致(无参数、无返回值)。3.空指针检查:在遍历时检查空指针(虽然一般不会出现,但安全起见)。这种方法避免了手动维护函数指针数组,新增或删除初始化函数只需在函数定义处添加或删除宏调用即可。引用[1]中提到GCC将代码分为RO、RW、ZI段,我们这里定义的`.init_array`属于RO段(只读数据),因此放在FLASH中。###相关问题1.在STM32开发中,如何使用链接器脚本将变量分配到特定的内存地址(如SRAM的固定地址)?2.如何控制链接器脚本中自定义段内元素的顺序?3.在嵌入式系统中,除了初始化函数,还有哪些场景适合使用链接器脚本管理连续内存区域?4.使用链接器脚本管理函数指针数组与手动维护数组相比,有哪些优势和劣势?5.在C++中,如何利用构造函数特性实现类似的自动初始化功能?</think>### 使用链接器脚本管理函数入口地址的方法 在嵌入式开发中,通过链接器脚本将函数入口地址自动排列在连续内存空间,可以避免手动维护函数指针数组。以下是具体实现步骤: #### 1. **定义自定义链接器段** 在链接器脚本(.ld文件)中创建专用段,用于存放函数指针: ```ld SECTIONS { .func_table : { __func_table_start = .; KEEP(*(.func_entries)) __func_table_end = .; } > FLASH } ``` - `KEEP()`确保函数入口不被优化删除 - `> FLASH`指定段存储在Flash中(引用[1]的RO段概念) - `__func_table_start/end`提供段边界符号 #### 2. **标记目标函数** 在C代码中使用编译器属性将函数加入段: ```c void __attribute__((section(".func_entries"))) uart_init(void) { // UART初始化代码 } void __attribute__((section(".func_entries"))) spi_init(void) { // SPI初始化代码 } ``` GCC编译器会自动将函数指针存入`.func_entries`段(引用[1]的RO段机制) #### 3. **自动执行函数** 在初始化代码中遍历函数表: ```c extern void (*__func_table_start)(void); extern void (*__func_table_end)(void); void run_init_functions(void) { for (void (**func)(void) = &__func_table_start; func < &__func_table_end; func++) { (*func)(); // 执行函数 } } ``` #### 4. **集成到系统初始化** 在启动代码中调用执行: ```c void SystemInit(void) { // 标准初始化... run_init_functions(); // 自动执行所有注册函数 } ``` #### 优势分析 1. **动态管理**:增删函数只需修改函数声明,无需维护数组 2. **内存效率**:函数指针连续存储,节省寻址开销(引用[2]的内存管理原则) 3. **扩展性强**:支持跨文件函数注册 4. **启动自动化**:类似引用[3]的定时触发机制,实现初始化自动化 #### 注意事项 - **函数原型一致**:所有函数需为`void func(void)`类型 - **执行顺序**:链接器按目标文件顺序排列,可通过文件名控制 - **内存对齐**:在链接脚本中使用`ALIGN(4)`确保32位对齐 - **优化处理**:使用`__attribute__((used))`防止未调用函数被优化 > 此方法利用链接器的段管理能力,将分散的函数指针自动整合为连续数组,完美解决了手动维护函数表的问题。实际在STM32CubeIDE和ARM-GCC环境中验证有效[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值