在VS Code软件中安装插件
在VS Code软件中安装Arm Assembly和LinkerScript插件,可以让startup_stm32f407xx.s和STM32F407ZGTx_FLASH.ld文件的代码进行高亮显示。
STM32F407x_FLASH.ld
存储区域地址信息的定义
栈段的地址空间位于RAM区域的末尾,且由高地址往低地址生长
_estack代表栈段的首地址,其地址为RAM区域的首地址加上RAM区域的长度,即RAM区域的末地址
_Min_Heap_Size指的是设置的堆的大小,_Min_Stack_Size指的是设置的栈的大小
(可以由自己设置)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
MEMORY:对存储区域进行设置
定义信息包括区域的首地址ORIGIN、大小LENGTH、读写可执行权限(x可执行,r可读,w可写)
STM32F407系列,存储区域分为1M的FLASH区域和192KB的SRAM区域(包含64KB的CCM data RAM)
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
各个程序段在存储区域的排布
SECTIONS:设置程序中各个段放在指定的地址空间,每个段都按照先后顺序依次在地址空间排布
SECTIONS设置的各个段
——.isr_vector:包含了中断向量表信息,在.s启动文件中定义,放置在FLASH区域的首地址
——.text:代码段,存放程序代码和其他数据,紧跟在.isr_vector段后面
——.rodata:只读数据段,,存放常量数据,紧跟在.text段后面
——.data:初始化数据段,存放已初始化且非零的全局变量,紧跟在.rodata段后面,但在程序运行时被加载到RAM区域的首地址
——.bss:未初始化数据段,存放已初始化为零或者没有初始化的全局变量,紧跟在.data段后面
——.user_heap_stack:位于.bss段后面,工具链自动检查堆栈大小是否溢出RAM区域空间
SECTIONS涉及的变量
——_etext:.text段的末尾地址
——_sdata:.data段的首地址
——_edata:.data段的末尾地址
——_sbss:.bss段的首地址
——_ebss:.bss段的末尾地址
——_sidata:.data段被加载到RAM区域的首地址
/* 定义各个程序段 */
SECTIONS
{
/* 启动代码 */
.isr_vector :
{
. = ALIGN(4); /* .代表当前地址 ALIGN(4)代表4字节地址对齐*/
KEEP(*(.isr_vector)) /* KEEP指示链接器保留指定的段,*代表匹配所有输入段,(.isr_vector)是要匹配的段名 */
. = ALIGN(4);
} >FLASH /* >代表存储到哪个区域 */
/* 程序代码和其他数据 */
.text :
{
. = ALIGN(4);
*(.text) /* 匹配.text段,不使用KEEP链接器指令,可能被优化掉 */
*(.text*) /* 匹配.text*段,这里的*代表通配符 */
*(.glue_7) /* 匹配.glue_7段,.glue_7段是从ARM模式切换到Thumb模式的代码 */
*(.glue_7t) /* 匹配.glue_7t段,.glue_7t段是从Thumb模式切换到ARM模式的代码 */
*(.eh_frame) /* 匹配.eh_frame段,.eh_frame段通常包含异常处理所需的信息 */
KEEP(*(.init)) /* 匹配.init段,.init段包含程序初始化代码 */
KEEP(*(.fini)) /* 匹配.fini段,.fini段包含程序终止代码 */
. = ALIGN(4);
_etext = .; /* 定义一个全局符号表示.text段的末尾地址 */
} >FLASH
/* 常量数据 */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* 匹配.rodata段 */
*(.rodata*) /* 匹配.rodata*段 */
. = ALIGN(4);
} >FLASH
}
_sidata = LOADADDR(.data); /* _sidata指的是.data段被加载到RAM区域后的首地址 */
/* 初始化数据 */
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data) /* 匹配.data段 */
*(.data*) /* 匹配.data*段 */
. = ALIGN(4);
_edata = .;
} >RAM AT> FLASH /* 表示该段存储在FLASH区域,但运行时被加载到RAM区域,该加载操作是在.s启动文件中执行的 */
_siccmram = LOADADDR(.ccmram);
.ccmram :
{
. = ALIGN(4);
_sccmram = .;
*(.ccmram)
*(.ccmram*)
. = ALIGN(4);
_eccmram = .;
} >CCMRAM AT> FLASH
/* 未初始化数据 */
.bss :
{
. = ALIGN(4);
_sbss = .;
__bss_start = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
__bss_end = _ebss;
} >RAM
/* 用于检查堆栈大小是否溢出RAM区域空间 */
.user_heap_stack :
{
. = ALIGN(8);
PROVIDE (end = .);
provide (_end = .);
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
程序运行的入口
/* Entry Point */
ENTRY(Reset_Handler)
整个文件重点关注的地方
——想要分配的堆栈空间大小(_Min_Heap_Size和_Min_Stack_Size),
对此我的理解是:当设置的_Min_Heap_Size和_Min_Stack_Size的值小于程序代码中使用的堆栈空间大小,编译工具链会报错或警告;所以要合理设置_Min_Heap_Size和_Min_Stack_Size的值;过大也可能会超出RAM的容量
——芯片内部存储空间(MEMORY),不同的芯片型号则需要修改
——可以把.data段加载到CCMRAM区域,但需要修改启动代码关于从FLASH区域加载数据到RAM区域的代码
——修改复位处理函数名称(Reset_Handler,如果你想这么做的话,但还是不要的好!)
startup_stm32f407xx.s
配置汇编环境
;告诉汇编器使用统一的语法规则
.syntax unified
;指定目标处理器为Cortex-M4
.cpu cortex-m4
;指定浮点运算单元为SoftVFP
.fpu softvfp
;指定使用thumb指令集
.thumb
Reset_Handler复位处理函数
——将FLASH区域的.data数据段拷贝到RAM区域
——将RAM区域的.bss数据段清零
——调用时钟系统初始化函数和其他一系列初始化函数
——跳转到用户main函数
/* 创建一个代码段(.text)的子节,名为Reset_Handler */
.section .text.Reset_Handler
/* 声明Reset_Handler为弱符号 */
.weak Reset_Handler
/* 声明Reset_Handler为函数类型 */
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* 设置堆栈指针 */
/* 将数据段初始化程序从Flash复制到SRAM */
ldr r0, =_sdata /* 将_sdata的值存入r0 */
ldr r1, =_edata /* 将_edata的值存入r1 */
ldr r2, =_sidata /* 将_sidata的值存入r2 */
movs r3, #0 /* 将立即数0存入r3 */
b LoopCopyDataInit /* 跳转到LoopCopyDataInit */
CopyDataInit:
ldr r4, [r2,r3] /* (r2的值加上r3的值)对应内存地址存放的值存入r4 */
str r4, [r0,r3] /* r4的值存入(r0的值加上r3的值)对应内存地址 */
adds r3, r3, #4 /* 将r3的值加4存入r3 */
LoopCopyDataInit:
adds r4, r0, r3 /* 将r0的值和r3的值相加存入r4 */
cmp r4, r1 /* 比较r4和r1的值,改变CPSR寄存器的标志位 */
bcc CopyDataInit /* 当r4比r1小时,发生跳转 */
/* 零填充bss段 */
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b LoopFillZerobss
FillZerobss:
str r3, [r2]
adds r2, r2, #4
LoopFillZerobss:
cmp r2, r4
bcc FillZerobss
/* 调用时钟系统初始化函数 */
bl SystemInit
/* 调用静态构造函数 */
bl __libc_init_array
/* 调用应用程序的入口 */
bl main
/* bl指令将返回地址保存到lr寄存器中,然后跳转到指定的地址执行代码 */
/* bx指令跳转到保存到lr寄存器中的返回地址,继续执行原来的代码 */
bx lr
.size Reset_Handler, .-Reset_Handler
死循环Default_Handler函数
/* 创建一个代码段(.text)的子节Default_Handler,"a"表示该段是可分配的,"x"表示该段是可执行的,%progbits表示该段包含程序代码或数据 */
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
中断向量表以及默认中断处理函数
中断向量表
——中断向量表定义在.isr_vector段,保存着各个异常和外部中断处理函数的首地址
/* 创建一个.isr_vector的段,"a"表示该段是可分配的,%progbits表示该段包含程序代码或数据 */
.section .isr_vector,"a",%progbits
/* 声明ptfVectors为对象类型 */
.type ptfVectors, %object
ptfVectors:
.word _estack
.word Reset_Handler /* Reset */
.word NMI_Handler /* Non maskable interrupt */
.word HardFault_Handler /* All class of fault */
.word MemManage_Handler /* Memory management */
.word BusFault_Handler /* Pre-fetch fault,memory access fault */
.word UsageFault_Handler /* Undefined instruction or illegal state */
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler /* System service call via SWI instruction */
.word DebugMon_Handler /* Debug Monitor */
.word 0
.word PendSV_Handler /* Pendable request for system service */
.word SysTick_Handler /* System tick timer */
/* 外部中断 */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_IRQHandler /* PVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word CAN1_TX_IRQHandler /* CAN1 TX */
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
.word USART3_IRQHandler /* USART3 */
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
.word FSMC_IRQHandler /* FSMC */
.word SDIO_IRQHandler /* SDIO */
.word TIM5_IRQHandler /* TIM5 */
.word SPI3_IRQHandler /* SPI3 */
.word UART4_IRQHandler /* UART4 */
.word UART5_IRQHandler /* UART5 */
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
.word TIM7_IRQHandler /* TIM7 */
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
.word ETH_IRQHandler /* Ethernet */
.word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */
.word CAN2_TX_IRQHandler /* CAN2 TX */
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
.word OTG_FS_IRQHandler /* USB OTG FS */
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
.word USART6_IRQHandler /* USART6 */
.word I2C3_EV_IRQHandler /* I2C3 event */
.word I2C3_ER_IRQHandler /* I2C3 error */
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
.word OTG_HS_IRQHandler /* USB OTG HS */
.word DCMI_IRQHandler /* DCMI */
.word 0 /* CRYP crypto */
.word HASH_RNG_IRQHandler /* Hash and Rng */
.word FPU_IRQHandler /* FPU */
异常和外部中断的默认中断处理函数
——用户可以自己定义异常和外部中断的处理函数,若不定义,则执行处理函数时会执行这里的弱函数
——这里的弱函数实际调用的都是Default_Handler,而Default_Handler是一个死循环
/* .thumb_set symbol,value 用于为value取一个别名 */
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Stream0_IRQHandler
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
.weak DMA1_Stream1_IRQHandler
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
.weak DMA1_Stream2_IRQHandler
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
.weak DMA1_Stream3_IRQHandler
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
.weak DMA1_Stream4_IRQHandler
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
.weak DMA1_Stream5_IRQHandler
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
.weak DMA1_Stream6_IRQHandler
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
.weak ADC_IRQHandler
.thumb_set ADC_IRQHandler,Default_Handler
.weak CAN1_TX_IRQHandler
.thumb_set CAN1_TX_IRQHandler,Default_Handler
.weak CAN1_RX0_IRQHandler
.thumb_set CAN1_RX0_IRQHandler,Default_Handler
.weak CAN1_RX1_IRQHandler
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
.weak CAN1_SCE_IRQHandler
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM9_IRQHandler
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler
.weak TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_TIM11_IRQHandler
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler
.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak OTG_FS_WKUP_IRQHandler
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler
.weak TIM8_BRK_TIM12_IRQHandler
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
.weak TIM8_UP_TIM13_IRQHandler
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
.weak TIM8_TRG_COM_TIM14_IRQHandler
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
.weak TIM8_CC_IRQHandler
.thumb_set TIM8_CC_IRQHandler,Default_Handler
.weak DMA1_Stream7_IRQHandler
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
.weak FSMC_IRQHandler
.thumb_set FSMC_IRQHandler,Default_Handler
.weak SDIO_IRQHandler
.thumb_set SDIO_IRQHandler,Default_Handler
.weak TIM5_IRQHandler
.thumb_set TIM5_IRQHandler,Default_Handler
.weak SPI3_IRQHandler
.thumb_set SPI3_IRQHandler,Default_Handler
.weak UART4_IRQHandler
.thumb_set UART4_IRQHandler,Default_Handler
.weak UART5_IRQHandler
.thumb_set UART5_IRQHandler,Default_Handler
.weak TIM6_DAC_IRQHandler
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
.weak TIM7_IRQHandler
.thumb_set TIM7_IRQHandler,Default_Handler
.weak DMA2_Stream0_IRQHandler
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
.weak DMA2_Stream1_IRQHandler
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
.weak DMA2_Stream2_IRQHandler
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
.weak DMA2_Stream3_IRQHandler
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
.weak DMA2_Stream4_IRQHandler
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
.weak ETH_IRQHandler
.thumb_set ETH_IRQHandler,Default_Handler
.weak ETH_WKUP_IRQHandler
.thumb_set ETH_WKUP_IRQHandler,Default_Handler
.weak CAN2_TX_IRQHandler
.thumb_set CAN2_TX_IRQHandler,Default_Handler
.weak CAN2_RX0_IRQHandler
.thumb_set CAN2_RX0_IRQHandler,Default_Handler
.weak CAN2_RX1_IRQHandler
.thumb_set CAN2_RX1_IRQHandler,Default_Handler
.weak CAN2_SCE_IRQHandler
.thumb_set CAN2_SCE_IRQHandler,Default_Handler
.weak OTG_FS_IRQHandler
.thumb_set OTG_FS_IRQHandler,Default_Handler
.weak DMA2_Stream5_IRQHandler
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
.weak DMA2_Stream6_IRQHandler
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
.weak DMA2_Stream7_IRQHandler
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
.weak USART6_IRQHandler
.thumb_set USART6_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak OTG_HS_EP1_OUT_IRQHandler
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
.weak OTG_HS_EP1_IN_IRQHandler
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
.weak OTG_HS_WKUP_IRQHandler
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
.weak OTG_HS_IRQHandler
.thumb_set OTG_HS_IRQHandler,Default_Handler
.weak DCMI_IRQHandler
.thumb_set DCMI_IRQHandler,Default_Handler
.weak HASH_RNG_IRQHandler
.thumb_set HASH_RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
整个文件重点关注的地方
——针对不同的芯片型号,汇编环境需要调整
——针对不同的芯片型号,中断向量表和对应中断处理函数也需要修改
——文件中将中断向量表ptfVectors和默认中断处理函数Default_Handler定义为全局符号,使得在C源文件中可见和可用
——在复位处理函数运行过程中还会跳转__libc_init_array这样一个特殊的符号,用于在 C/C++ 程序的初始化阶段执行一系列的函数。在链接过程中,链接器会收集所有带有 attribute((constructor)) 属性的函数,并将它们放入一个特殊的段(通常是 .init_array 段)。然后,在程序启动时,__libc_init_array 会被调用,以执行这些初始化函数。
一些感想
为什么要看这两个文件
下面的图片是用STM32CUBEMX生成的STM32的工程目录,我其实有个想法:构建这样的一个STM32的工程目录,每个文件都经过我的复刻(而不是直接拷贝),我认为这能帮助我更好地理解和学习某款芯片,所以我试着去看每个文件。在这个工程目录下,除了.mxproject和.ioc文件(STM32CUBEMX所需的工程文件,我不打算去看),剩下就是一个.s启动文件、一个.ld链接器脚本、一个Makefile文件和所有的c语言源文件。
强烈建议不要去看.s文件、.ld文件和Makefile文件的每个语句!!!!
(如果你是高手,那当我没说!)以我现有的开发经验和这几天的体会来说,看这几个文件确实花费我很大精力,特别是去看每个语句,我感觉我也有些钻牛角尖了,所以建议把这些文件拿去直接使用(.s文件可以看看),若使用Keil开发,也压根不需要接触到.ld文件和Makefile文件是吧?!
看完这两个文件的收获
说实话,我也不敢说我现在彻底理解了整个工程的编译和链接流程,我只是尽力去看懂和理解这个过程完成的一些事情。但是我起码
——写出了这篇博客;
——复刻了startup_stm32f407xx.s和STM32F407ZGTx_FLASH.ld这两个文件,在里面也做了中文注释;
——了解各个程序段在存储区域的排布情况和MCU上电启动执行程序的流程;
——完成了我的想法(构建一个STM32的工程目录)的一部分;
——知道了如何修改设置的堆栈大小和 attribute((constructor)) (这个内容可能有用,但我暂时没用过);
——我或许能构建一个GD32F470ZGT6芯片的工程目录,而使用VS Code和ARM-GCC编译工具链来开发,这个以后再说吧
后续
Makefile文件我还在看,等我觉得看的差不多我就也会写一篇博客分享我的心得和看法;然后后面就是根据数据手册来复刻出固件库,这个我尽力而为。