AVR128DB28 USART Bootloader Flash写入异常:main正常/ctors段boot函数失效
AVR128DB28 USART Bootloader Flash写入失败问题排查与修复
我在开发AVR128DB28的USART bootloader时碰到一个奇怪的问题:用int main()作为程序入口时,Flash编程功能完全正常;但换成带__attribute__((naked))和__attribute__((section(".ctors")))的void boot(void)作为入口后,就无法将数据写入指定的Flash页地址。相关代码如下:
#include "libcomp.h" const flash_adr_t address = 0x1fc00; #ifndef MAIN_TEST /* * Main boot function * It represents the enter point for the device to start execution * It represents the enter point for the device to start execution * Naked attribute instructs the compiler to omit the function prologue and epilogue */ __attribute__((naked)) __attribute__((section(".ctors"))) void boot(void) #else int main(void) #endif { int value; /* Initialize system for AVR GCC support, expects r1 = 0 */ asm volatile("clr r1"); SYSTEM_Initialize(); uint8_t buffer[PROGMEM_PAGE_SIZE]; uint8_t data_flash[PROGMEM_PAGE_SIZE] = "Main boot function. It represents the enter point for the device to start execution."; value = strlen((char)data_flash); FLASH_WriteFlashPage(address, data_flash); _delay_ms(500); BLD_Init(); uint16_t i = 0; while (1) { if (USART0_IsTxReady()) { for (int k = 0; k < value; k++) { USART0_Write(FLASH_ReadFlashByte(address + k)); } USART0_Write('\n'); } ClrWdt(); BLD_Tasks(); if (++i == 0) LED3_Toggle(); } #ifdef MAIN_TEST return 0; #endif }
原因分析
.ctors段是AVR GCC用于存放C++全局构造函数执行入口的区域,它的执行时机远早于标准启动流程中的堆栈初始化、全局变量拷贝等关键步骤。将bootloader入口放在这里,会导致Flash写入操作依赖的系统环境不完整。__attribute__((naked))会让编译器省略函数的堆栈指针初始化、寄存器保存等 prologue 代码,以及函数收尾的 epilogue 代码。而Flash写入函数(比如FLASH_WriteFlashPage)需要正确的堆栈环境才能执行,没有堆栈的话,函数调用的参数和返回地址无法正确保存,直接导致操作失败。- 对比
main()入口:AVR的默认启动代码(crt0.S)会先完成RAM初始化、堆栈设置、全局变量从Flash拷贝到RAM、构造函数调用等操作,再调用main(),此时系统环境已完全就绪,所以Flash写入能正常工作。
解决方案
- 指定正确的入口段:不要用
.ctors,应将bootloader入口指定到AVR的重置向量地址。根据AVR128DB28的熔丝配置(BOOTSZ),重置向量会指向boot区起始地址,用__attribute__((section(".reset")))可将函数放到重置向量位置。 - 手动初始化堆栈:因为用了
naked属性,编译器不会自动初始化堆栈,必须手动添加堆栈初始化代码,确保后续函数调用有合法的堆栈空间。 - 修正类型转换错误:原代码中
strlen((char)data_flash)是错误的,应改为(char*)data_flash,否则会把数组首地址当成单个char值传入,导致strlen计算出错误长度。
修改后的代码示例
#include "libcomp.h" const flash_adr_t address = 0x1fc00; // 将入口函数放到重置向量段,确保系统启动后第一时间执行 __attribute__((naked)) __attribute__((section(".reset"))) void boot(void) { // 手动初始化堆栈(AVR128DB28的RAMEND为0x1FFF,可根据实际RAM大小调整) asm volatile("ldi r16, lo8(RAMEND)"); asm volatile("out SPL, r16"); asm volatile("ldi r16, hi8(RAMEND)"); asm volatile("out SPH, r16"); // 初始化r1为0,符合AVR GCC调用约定 asm volatile("clr r1"); // 跳转到实际的bootloader主函数,避免在naked函数中编写复杂逻辑 asm volatile("rjmp boot_main"); } // 实际的bootloader主逻辑,不需要naked属性 void boot_main(void) { SYSTEM_Initialize(); uint8_t buffer[PROGMEM_PAGE_SIZE]; uint8_t data_flash[PROGMEM_PAGE_SIZE] = "Main boot function. It represents the enter point for the device to start execution."; // 修正类型转换错误 int value = strlen((char*)data_flash); FLASH_WriteFlashPage(address, data_flash); _delay_ms(500); BLD_Init(); uint16_t i = 0; while (1) { if (USART0_IsTxReady()) { for (int k = 0; k < value; k++) { USART0_Write(FLASH_ReadFlashByte(address + k)); } USART0_Write('\n'); } ClrWdt(); BLD_Tasks(); if (++i == 0) LED3_Toggle(); } }
内容的提问来源于stack exchange,提问作者duc nguyen
相关产品推荐
相关产品推荐

