找回密码
 立即注册
首页 业界区 业界 使用C语言实现重写stm32的启动文件

使用C语言实现重写stm32的启动文件

仟仞 前天 16:45
适用型号:stm32f103c8t6
编译器:GCC
传统的启动文件使用汇编语言实现,可读性很低,现在分析其内容,使用C语言重新实现一遍。
首先附上成品,使用C23标准:
  1. #include <stddef.h>
  2. #include <stdint.h>
  3. #include <string.h>
  4. // some macros
  5. #define cast(__value, __type) ((__type)(__value))
  6. #define ptr(__type)           typeof(__type *)
  7. #define array(__type, ...)    typeof(__type[##__VA_ARGS__])
  8. #define func(__ret, ...)      typeof(__ret(__VA_ARGS__))
  9. #define readonly(__type)      typeof(const __type)
  10. /**
  11. * @syntax unified
  12. * @cpu cortex-m3
  13. * @fpu softvfp
  14. * @thumb
  15. */
  16. typedef func(void) inteHandlerType;                    // 定义中断处理函数类型
  17. typedef readonly(ptr(inteHandlerType)) inteVectorType; // 定义中断向量表元素类型
  18. array(inteVectorType) f_pfnVectors; // 声明 中断向量表
  19. inteHandlerType Default_Handler;    // 声明 默认中断处理函数
  20. inteHandlerType Reset_Handler;      // 声明 复位函数
  21. extern func(void) SystemInit; // defined in @system_stm32f1xx.c
  22. extern func(int) main;        // defined in @main.c
  23. extern func(void) __libc_init_array;
  24. static func(void) data_init;
  25. static func(void) bss_init;
  26. // 栈顶地址
  27. /* Highest address of the user mode stack */
  28. extern uint8_t _estack; // 为了和 @sysmem.c 中的定义保持一致,使用uint8_t
  29. // 定义在链接器脚本中的符号
  30. /* defined in linker script */
  31. extern uint32_t _sidata; /* start address for the initialization values of the .data section.*/
  32. /* start address for the .data section. defined in linker script */
  33. extern uint32_t _sdata;
  34. /* end address for the .data section. defined in linker script */
  35. extern uint32_t _edata;
  36. /* start address for the .bss section. defined in linker script */
  37. extern uint32_t _sbss;
  38. /* end address for the .bss section. defined in linker script */
  39. extern uint32_t _ebss;
  40. readonly(uint32_t) BootRAM = 0xF108F85F;
  41. /*******************************************************************************
  42. *
  43. * Provide weak aliases for each Exception handler to the Default_Handler.
  44. * As they are weak aliases, any function with the same name will override
  45. * this definition.
  46. *
  47. *******************************************************************************/
  48. #define __HandlerAttribute [[gnu::weak]] [[gnu::alias("Default_Handler")]]
  49. __HandlerAttribute func(void) NMI_Handler;
  50. __HandlerAttribute func(void) HardFault_Handler;
  51. __HandlerAttribute func(void) MemManage_Handler;
  52. __HandlerAttribute func(void) BusFault_Handler;
  53. __HandlerAttribute func(void) UsageFault_Handler;
  54. __HandlerAttribute func(void) SVC_Handler;
  55. __HandlerAttribute func(void) DebugMon_Handler;
  56. __HandlerAttribute func(void) PendSV_Handler;
  57. __HandlerAttribute func(void) SysTick_Handler;
  58. __HandlerAttribute func(void) WWDG_IRQHandler;
  59. __HandlerAttribute func(void) PVD_IRQHandler;
  60. __HandlerAttribute func(void) TAMPER_IRQHandler;
  61. __HandlerAttribute func(void) RTC_IRQHandler;
  62. __HandlerAttribute func(void) FLASH_IRQHandler;
  63. __HandlerAttribute func(void) RCC_IRQHandler;
  64. __HandlerAttribute func(void) EXTI0_IRQHandler;
  65. __HandlerAttribute func(void) EXTI1_IRQHandler;
  66. __HandlerAttribute func(void) EXTI2_IRQHandler;
  67. __HandlerAttribute func(void) EXTI3_IRQHandler;
  68. __HandlerAttribute func(void) EXTI4_IRQHandler;
  69. __HandlerAttribute func(void) DMA1_Channel1_IRQHandler;
  70. __HandlerAttribute func(void) DMA1_Channel2_IRQHandler;
  71. __HandlerAttribute func(void) DMA1_Channel3_IRQHandler;
  72. __HandlerAttribute func(void) DMA1_Channel4_IRQHandler;
  73. __HandlerAttribute func(void) DMA1_Channel5_IRQHandler;
  74. __HandlerAttribute func(void) DMA1_Channel6_IRQHandler;
  75. __HandlerAttribute func(void) DMA1_Channel7_IRQHandler;
  76. __HandlerAttribute func(void) ADC1_2_IRQHandler;
  77. __HandlerAttribute func(void) USB_HP_CAN1_TX_IRQHandler;
  78. __HandlerAttribute func(void) USB_LP_CAN1_RX0_IRQHandler;
  79. __HandlerAttribute func(void) CAN1_RX1_IRQHandler;
  80. __HandlerAttribute func(void) CAN1_SCE_IRQHandler;
  81. __HandlerAttribute func(void) EXTI9_5_IRQHandler;
  82. __HandlerAttribute func(void) TIM1_BRK_IRQHandler;
  83. __HandlerAttribute func(void) TIM1_UP_IRQHandler;
  84. __HandlerAttribute func(void) TIM1_TRG_COM_IRQHandler;
  85. __HandlerAttribute func(void) TIM1_CC_IRQHandler;
  86. __HandlerAttribute func(void) TIM2_IRQHandler;
  87. __HandlerAttribute func(void) TIM3_IRQHandler;
  88. __HandlerAttribute func(void) TIM4_IRQHandler;
  89. __HandlerAttribute func(void) I2C1_EV_IRQHandler;
  90. __HandlerAttribute func(void) I2C1_ER_IRQHandler;
  91. __HandlerAttribute func(void) I2C2_EV_IRQHandler;
  92. __HandlerAttribute func(void) I2C2_ER_IRQHandler;
  93. __HandlerAttribute func(void) SPI1_IRQHandler;
  94. __HandlerAttribute func(void) SPI2_IRQHandler;
  95. __HandlerAttribute func(void) USART1_IRQHandler;
  96. __HandlerAttribute func(void) USART2_IRQHandler;
  97. __HandlerAttribute func(void) USART3_IRQHandler;
  98. __HandlerAttribute func(void) EXTI15_10_IRQHandler;
  99. __HandlerAttribute func(void) RTC_Alarm_IRQHandler;
  100. __HandlerAttribute func(void) USBWakeUp_IRQHandler;
  101. /******************************************************************************
  102. *
  103. * The minimal vector table for a Cortex M3.  Note that the proper constructs
  104. * must be placed on this to ensure that it ends up at physical address
  105. * 0x0000.0000.
  106. *
  107. ******************************************************************************/
  108. [[gnu::section(".isr_vector")]] // gnu extension to place the vector table in a specific address
  109. array(inteVectorType) f_pfnVectors =
  110.     {
  111.         cast(&_estack, ptr(void)),          /* Stack pointer */
  112.         Reset_Handler,                      /* Reset Handler */
  113.         NMI_Handler,                        /* NMI Handler */
  114.         HardFault_Handler,                  /* Hard Fault Handler */
  115.         MemManage_Handler,                  /* MPU Fault Handler */
  116.         BusFault_Handler,                   /* Bus Fault Handler */
  117.         UsageFault_Handler,                 /* Usage Fault Handler */
  118.         nullptr, nullptr, nullptr, nullptr, /* Reserved */
  119.         SVC_Handler,                        /* SVCall Handler */
  120.         DebugMon_Handler,                   /* Debug Monitor Handler */
  121.         nullptr,                            /* Reserved */
  122.         PendSV_Handler,                     /* PendSV Handler */
  123.         SysTick_Handler,                    /* SysTick Handler */
  124.         /* IRQ Handlers */
  125.         WWDG_IRQHandler,
  126.         PVD_IRQHandler,
  127.         TAMPER_IRQHandler,
  128.         RTC_IRQHandler,
  129.         FLASH_IRQHandler,
  130.         RCC_IRQHandler,
  131.         EXTI0_IRQHandler,
  132.         EXTI1_IRQHandler,
  133.         EXTI2_IRQHandler,
  134.         EXTI3_IRQHandler,
  135.         EXTI4_IRQHandler,
  136.         DMA1_Channel1_IRQHandler,
  137.         DMA1_Channel2_IRQHandler,
  138.         DMA1_Channel3_IRQHandler,
  139.         DMA1_Channel4_IRQHandler,
  140.         DMA1_Channel5_IRQHandler,
  141.         DMA1_Channel6_IRQHandler,
  142.         DMA1_Channel7_IRQHandler,
  143.         ADC1_2_IRQHandler,
  144.         USB_HP_CAN1_TX_IRQHandler,
  145.         USB_LP_CAN1_RX0_IRQHandler,
  146.         CAN1_RX1_IRQHandler,
  147.         CAN1_SCE_IRQHandler,
  148.         EXTI9_5_IRQHandler,
  149.         TIM1_BRK_IRQHandler,
  150.         TIM1_UP_IRQHandler,
  151.         TIM1_TRG_COM_IRQHandler,
  152.         TIM1_CC_IRQHandler,
  153.         TIM2_IRQHandler,
  154.         TIM3_IRQHandler,
  155.         TIM4_IRQHandler,
  156.         I2C1_EV_IRQHandler,
  157.         I2C1_ER_IRQHandler,
  158.         I2C2_EV_IRQHandler,
  159.         I2C2_ER_IRQHandler,
  160.         SPI1_IRQHandler,
  161.         SPI2_IRQHandler,
  162.         USART1_IRQHandler,
  163.         USART2_IRQHandler,
  164.         USART3_IRQHandler,
  165.         EXTI15_10_IRQHandler,
  166.         RTC_Alarm_IRQHandler,
  167.         USBWakeUp_IRQHandler,
  168.         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, /* Reserved */
  169.         cast(BootRAM, ptr(void))
  170.         /* @0x108. This is for boot in RAM mode for STM32F10x Medium Density devices. */
  171. };
  172. /**
  173. * @brief  This is the code that gets called when the processor receives an
  174. *         unexpected interrupt.  This simply enters an infinite loop, preserving
  175. *         the system state for examination by a debugger.
  176. *
  177. * @param  None
  178. * @retval : None
  179. */
  180. void Default_Handler(void)
  181. {
  182.     while (true) {
  183.         /* Infinite loop */
  184.     }
  185. }
  186. /**
  187. * @brief  This is the code that gets called when the processor first
  188. *          starts execution following a reset event. Only the absolutely
  189. *          necessary set is performed, after which the application
  190. *          supplied main() routine is called.
  191. * @param  None
  192. * @retval : None
  193. */
  194. [[noreturn]] // the function will never return
  195. void Reset_Handler(void)
  196. {
  197.     /* Call the clock system initialization function */
  198.     SystemInit();
  199.     /* Initialize data and bss sections */
  200.     data_init();
  201.     bss_init();
  202.     /* Call static constructors */
  203.     __libc_init_array();
  204.     /* Call the application's entry point */
  205.     main();
  206.     /* Should never reach here */
  207.     while (true) {
  208.         /* Infinite loop */
  209.     }
  210. }
  211. /**
  212. * @brief data sector initialization function
  213. *
  214. */
  215. static void data_init(void)
  216. {
  217.     auto   src       = cast(&_sidata, ptr(uint8_t)); // flash addr
  218.     auto   dst_start = cast(&_sdata, ptr(uint8_t));  // ram start
  219.     auto   dst_end   = cast(&_edata, ptr(uint8_t));  // ram end
  220.     size_t data_size = dst_end - dst_start;          // get data size
  221.     memcpy(dst_start, src, data_size);               // copy data from flash to ram
  222. }
  223. /**
  224. * @brief bss sector zero initialization function
  225. *
  226. */
  227. /* BSS zero initialization function */
  228. static void bss_init(void)
  229. {
  230.     auto   dst_start = cast(&_sbss, ptr(uint8_t)); // ram start
  231.     auto   dst_end   = cast(&_ebss, ptr(uint8_t)); // ram end
  232.     size_t bss_size  = dst_end - dst_start;        // get bss size
  233.     memset(dst_start, 0x00, bss_size);             // clear bss section
  234. }
复制代码
以STM32CubeMX生成使用的CMake工具链的stm32f103c8t6的项目为例,有一个启动文件 startup_stm32f103xb.s 和链接器脚本 STM32F103XX_FLASH.ld,启动文件中定义的内容是上电以后执行的第一件事情,而链接器脚本指定程序的链接方式和内存区域分配方式。
首先分析链接器文件:
  1. /* Entry Point */
  2. ENTRY(Reset_Handler)
复制代码
定义了入口函数,也即上电之后执行的第一个函数,此处为 Reset_Handler。
  1. /* Specify the memory areas */
  2. MEMORY
  3. {
  4. RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
  5. FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 64K
  6. }
复制代码
定义了内存区域,分别为:

  • RAM 区域,可执行(x)、可读(r)、可写(w)的区域,大小为 20K;
  • FLASH 区域,可读(r)、可执行(x),大小为 64K,起始地址为 0x8000000。
  1. /* Highest address of the user mode stack */
  2. _estack = ORIGIN(RAM) + LENGTH(RAM);    /* end of RAM */
复制代码
定义程序堆栈起始地址 _estack,由于堆栈是从高地址向低地址延伸,所以起始地址定义为 RAM 区域的结束位置。考虑到寻址方式为“word”,因此在C语言中,可以使用 uint32_t 类型来表示。
  1. /* Generate a link error if heap and stack don't fit into RAM */
  2. _Min_Heap_Size = 0x0;      /* required amount of heap  */
  3. _Min_Stack_Size = 0x400; /* required amount of stack */
复制代码
定义堆和栈的大小。如果需要使用 malloc 等动态内存分配,则分配的内存就位于堆中。此处不用,因此设为0。
然后是段定义:
  1. /* Define output sections */
  2. SECTIONS
  3. {
  4.     /*...... */
  5. }
复制代码
分为几个段:
  1.   /* The startup code goes first into FLASH */
  2.   .isr_vector :
  3.   {
  4.     . = ALIGN(4);
  5.     KEEP(*(.isr_vector)) /* Startup code */
  6.     . = ALIGN(4);
  7.   } >FLASH
复制代码
为中断向量表所在的地址
  1.   /* Constant data goes into FLASH */
  2.   .rodata :
  3.   {
  4.     . = ALIGN(4);
  5.     *(.rodata)         /* .rodata sections (constants, strings, etc.) */
  6.     *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
  7.     . = ALIGN(4);
  8.   } >FLASH
复制代码
定义const 变量存放在 Flash 中。
接下来的 .ARM.extab、.ARM、.preinit_array、.init_array、.fini_array 与C++有关,略过。
接着是
  1.   /* used by the startup to initialize data */
  2.   _sidata = LOADADDR(.data);
复制代码
定义符号 _sidata,用于指定 .data 段在Flash中的起始地址,这样就可以在C代码文件中使用这个“变量”
  1.   /* Initialized data sections goes into RAM, load LMA copy after code */
  2.   .data :
  3.   {
  4.     . = ALIGN(4);
  5.     _sdata = .;        /* create a global symbol at data start */
  6.     *(.data)           /* .data sections */
  7.     *(.data*)          /* .data* sections */
  8.     *(.RamFunc)        /* .RamFunc sections */
  9.     *(.RamFunc*)       /* .RamFunc* sections */
  10.     . = ALIGN(4);
  11.   } >RAM AT> FLASH
复制代码
此处定义了 .data 段,用于存放已经初始化过的全局变量,这些变量的值会存放在Flash中,在程序运行时,在启动文件中将其复制到RAM中的对应位置。
  1.   .bss (NOLOAD) : ALIGN(4)
  2.   {
  3.     *(.bss)
  4.     *(.bss*)
  5.     *(COMMON)
  6.       . = ALIGN(4);
  7.     _ebss = .;         /* define a global symbol at bss end */
  8.     __bss_end__ = _ebss;
  9.       PROVIDE( __bss_end = .);
  10.   } >RAM
复制代码
定义了 .bss 段,用于存放未初始化的全局变量,这些变量的值在程序运行时会被初始化为0。
  1. /* User_heap_stack section, used to check that there is enough RAM left */
  2.   ._user_heap_stack (NOLOAD) :
  3.   {
  4.     . = ALIGN(8);
  5.     PROVIDE ( end = . );
  6.     PROVIDE ( _end = . );
  7.     . = . + _Min_Heap_Size;
  8.     . = . + _Min_Stack_Size;
  9.     . = ALIGN(8);
  10.   } >RAM
复制代码
定义了一个 .user_heap_stack 段,用于堆栈的分配,保存在RAM中。
  1.   /* Remove information from the standard libraries */
  2.   /DISCARD/ :
  3.   {
  4.     libc.a:* ( * )
  5.     libm.a:* ( * )
  6.     libgcc.a:* ( * )
  7.   }
复制代码
移除 libc.a、libm.a、libgcc.a 等标准库符号,因为使用newlib。
接着分析启动文件:
首先是
  1.   .syntax unified
  2.   .cpu cortex-m3
  3.   .fpu softvfp
  4.   .thumb
复制代码
定义了cpu、fpu、指令集等内容,略过。
然后
  1. .global g_pfnVectors
  2. .global Default_Handler
复制代码
相当于C语言中定声明了两个全局变量,第一个是中断向量组,第二个是默认的中断处理函数。中断向量组中是紧凑排列的各种中断函数的入口地址,我们知道所有的中断函数都是 void handler(void) 类型,因此它等价为一个函数指针数组,为了防止不小心修改,可以添加 const 限定符:
  1. typedef void(*const inteFunp)(void);
  2. const inteFunp g_pfnVectors[];
复制代码
然后是
  1. /* start address for the initialization values of the .data section.
  2. defined in linker script */
  3. .word _sidata
  4. /* start address for the .data section. defined in linker script */
  5. .word _sdata
  6. /* end address for the .data section. defined in linker script */
  7. .word _edata
  8. /* start address for the .bss section. defined in linker script */
  9. .word _sbss
  10. /* end address for the .bss section. defined in linker script */
  11. .word _ebss
  12. .equ  BootRAM, 0xF108F85F
复制代码
声明了几个 .word 类型的变量,相当于C语言中的 uint32_t 类型。这些符号定义在链接器脚本中,用于指定各个数据段的起始、终止地址。最后的 .equ  BootRAM, 0xF108F85F 定义了从RAM中启动的地址,需要加在中断向量组的最后。
接着是 Reset_Handler 函数的定义,它是上电之后第一个执行的函数。下面是它的声明:
  1.   .section .text.Reset_Handler // 定义代码段
  2.   .weak Reset_Handlel           // 定义为弱符号
  3.   .type Reset_Handler, %function // 定义为函数类型
复制代码
这一部分可以改写为C代码:
  1. [[gnu::weak]] void Reset_Handler(void);
复制代码
其中 [[gnu::weak]] 是C23语法,用于定义弱符号。
然后是函数体:
  1. Reset_Handler:
  2.     bl SystemInit // 调用 SystemInit 函数
  3.     // 从 Flash 中复制数据到 data 段
  4.     // 清零 bss 段
  5.     bl __libc_init_array // 调用 C++ 静态构造函数
  6.     bl main // 调用 main 函数,进入主程序
  7.     bx lr   // 相当于 main 函数中的 return
复制代码
可以改写为C代码:
  1. void data_init(void);
  2. void bss_init(void);
  3. [[noreturn]] // c23 属性,标记函数永不返回
  4. void Reset_Handler(void){
  5.     SystemInit();
  6.     data_init(); // 复制数据到data段
  7.     bss_init(); // 清零bss段
  8.     __libc_init_array(); // 调用C++静态构造函数
  9.     main(); // 进入主程序
  10.     while(true);
  11. }
复制代码
其中 data_init 和 bss_init 是我们自己定义的两个函数,用于初始化 .data 和 .bss 段,其汇编代码如下:
  1. /* Copy the data segment initializers from flash to SRAM */
  2.   ldr r0, =_sdata    // r0 = _sdata;
  3.   ldr r1, =_edata    // r1 = _edata;
  4.   ldr r2, =_sidata   // r2 = _sidata;
  5.   movs r3, #0        // r3 = 0;
  6.   b LoopCopyDataInit // goto LoopCopyDataInit;
  7. CopyDataInit:        // CopyDataInit:
  8.   ldr r4, [r2, r3]   //   r4 = *(uint32_t*)(r2 + r3);
  9.   str r4, [r0, r3]   //   *(uint32_t*)(r0 + r2) = r4;
  10.                      //   // 两句合起来等价于:
  11.                      //   // *(uint32_t*)(r0 + r2) = *(uint32_t*)(r2 + r2);
  12.   adds r3, r3, #4    //   r3 += 4;
  13. LoopCopyDataInit:    // LoopCopyDataInit:
  14.   adds r4, r0, r3    //   r4 = r0 + r3;
  15.   cmp r4, r1         //   cmp res(r4,r1); // 假设有个enum cmp用于存储两个数字比较情况
  16.   bcc CopyDataInit   //   if(cmp == less){ goto CopyDataInit; }
  17. /* Zero fill the bss segment. */
  18.   ldr r2, =_sbss
  19.   ldr r4, =_ebss
  20.   movs r3, #0
  21.   b LoopFillZerobss
  22. FillZerobss:
  23.   str  r3, [r2]
  24.   adds r2, r2, #4
  25. LoopFillZerobss:
  26.   cmp r2, r4
  27.   bcc FillZerobss
复制代码
改写为C代码如下:
  1. extern uint32_t &_sdata;
  2. extern uint32_t &_edata;
  3. extern uint32_t &_sidata;
  4. uint32_t data_start = _sdata;   // data段的起始地址
  5. uint32_t data_end = _edata;     // data段的结束地址
  6. uint32_t source_addr = _sidata; // flash中data的数据的起始地址
  7. uint32_t offset = 0;
  8. // 以 word 为单位,也即4字节为单位,从 flash 中拷贝数据到 ram 中
  9. while(offset< data_end){
  10.     uint32_t *src = (uint32_t *)(source_addr + offset);
  11.     uint32_t *dst = (uint32_t *)(data_start + offset);
  12.     *dst = *src;
  13.     offset += 4; // 指针偏移4字节,也即一个 word 的长度
  14. }
  15. // 清零部分略
复制代码
可以看到,本质上就是把 flash 中的数据拷贝到 ram 中,拷贝的长度为 _edata - _sdata,源地址为 flash 中的 _sidata,目的地址为 ram 中的 _sdata。清零部分同理,只不过是把拷贝改为设置为0.
因此,可以借助C库函数 memcpy 和 memset 来实现 data_init 和 bss_init 函数:
  1. /* Data copy function */
  2. static void data_init(void)
  3. {
  4.     size_t data_size = _edata - _sdata; // get data size
  5.     memcpy(_sdata, _sidata, data_size); // copy data from flash to ram
  6. }
  7. /* BSS zero initialization function */
  8. static void bss_init(void)
  9. {
  10.     size_t bss_size = _ebss - _sbss; // get bss size
  11.     memset(_sbss, 0x00, bss_size);   // clear bss section
  12. }
复制代码
然后是 Default_Handler 函数:
  1. /**
  2. * @brief  This is the code that gets called when the processor receives an
  3. *         unexpected interrupt.  This simply enters an infinite loop, preserving
  4. *         the system state for examination by a debugger.
  5. *
  6. * @param  None
  7. * @retval : None
  8. */
  9.     .section .text.Default_Handler,"ax",%progbits
  10. Default_Handler:
  11. Infinite_Loop:
  12.   b Infinite_Loop
  13.   .size Default_Handler, .-Default_Handler
复制代码
它是中断处理函数的默认实现,当发生未知中断时,它会进入一个无限循环,保持系统状态,等待调试器来查看。C语言实现为:
  1. void Default_Handler(void){
  2.   while(true){}
  3. }
复制代码
最后是中断向量组定义:
  1. /******************************************************************************
  2. *
  3. * The minimal vector table for a Cortex M3.  Note that the proper constructs
  4. * must be placed on this to ensure that it ends up at physical address
  5. * 0x0000.0000.
  6. *
  7. ******************************************************************************/
  8.   .section .isr_vector,"a",%progbits
  9.   .type g_pfnVectors, %object
  10.   .size g_pfnVectors, .-g_pfnVectors
  11. g_pfnVectors:
  12.   .word _estack
  13.   .word Reset_Handler
  14.   // ......
  15.   .word BootRAM          /* @0x108. This is for boot in RAM mode for
  16.                             STM32F10x Medium Density devices. */
复制代码
以及其弱符号定义:
  1.   .weak NMI_Handler
  2.   .thumb_set NMI_Handler,Default_Handler
  3.   .weak HardFault_Handler
  4.   .thumb_set HardFault_Handler,Default_Handler
  5.   .weak MemManage_Handler
  6.   .thumb_set MemManage_Handler,Default_Handler
  7.   // ......
复制代码
改写为C代码:
  1. // 函数声明
  2. [[gnu::weak]] [[gnu::alias("Default_Handler")]] func(void) NMI_Handler;
  3. [[gnu::weak]] [[gnu::alias("Default_Handler")]] func(void) HardFault_Handler;
  4. // ......
  5. [[gnu::weak]] [[gnu::alias("Default_Handler")]] func(void) USBWakeUp_IRQHandler;
  6. // 向量组定义
  7. [[gnu::section(".isr_vector")]]  
  8. const (*const f_pfnVectors[]) = {
  9.   (void(*)void)&_estack,
  10.   Reset_Handler,
  11.   // ...///
  12.   (void(*)void)BootRAM
  13. };
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册