文章目录
前篇
Linux下使用VSCode配置STM32的集成开发环境
解读startup_stm32f407xx.s和STM32F407x_FLASH.ld文件
解读STM32CubeMX生成的Makefile文件
一、所需的源文件
首先新建一个工程目录,把Makefile中编译用到的源文件先拷贝到工程目录下,具体工程目录的设置可以自行设置,但我这里就模仿STM32CubeMX创建的工程目录创建属于自己的工程目录。
二、所需的头文件
再把Makefile中编译用到的头文件先拷贝到工程目录下,根据头文件所在路径以及源文件所需的头文件选择所需的头文件
main.c ——》main.h
stm32f4xx_hal_msp.c——》main.h
stm32f4xx_it.c——》main.h、stm32f4xx_it.h
system_stm32f4xx.c——》stm32f4xx.h
stm32f4xx_hal.c——》stm32f4xx_hal.h
stm32f4xx_hal_cortex.c——》stm32f4xx_hal.h
stm32f4xx_hal_dma.c——》stm32f4xx_hal.h
stm32f4xx_hal_dma_ex.c——》stm32f4xx_hal.h
stm32f4xx_hal_exti.c——》stm32f4xx_hal.h
stm32f4xx_hal_flash.c——》stm32f4xx_hal.h
stm32f4xx_hal_flash_ex.c——》stm32f4xx_hal.h
stm32f4xx_hal_flash_ramfunc.c——》stm32f4xx_hal.h
stm32f4xx_hal_gpio.c——》stm32f4xx_hal.h
stm32f4xx_hal_pwr.c——》stm32f4xx_hal.h
stm32f4xx_ha_pwr_ex.c——》stm32f4xx_hal.h
stm32f4xx_hal_rcc.c——》stm32f4xx_hal.h
stm32f4xx_hal_rcc_ex.c——》stm32f4xx_hal.h
stm32f4xx_hal_tim.c——》stm32f4xx_hal.h
stm32f4xx_hal_tim_ex.c——》stm32f4xx_hal.h
main.h——》stm32f4xx_hal.h
stm32f4xx_it.h——》None
stm32f4xx.h——》stm32f407xx.h、stm32f4xx_hal.h(因为定义了STM32F407xx和USE_HAL_DRIVER)
stm32f4xx_hal.h——》stm32f4xx_hal_conf.h
stm32f407xx.h——》core_m4.h、system_stm32f4xx.h、stdint.h
stm32f407_hal_conf.h——》stm32f4xx_hal_cortex.h、stm32f4xx_hal_dma.h、stm32f4xx_hal_dma_ex.h、stm32f4xx_hal_exti.h、stm32f4xx_hal_flash.h、stm32f4xx_hal_flash_ex.h、stm32f4xx_hal_flash_ramfunc.h、stm32f4xx_hal_gpio.h、stm32f4xx_hal_gpio_ex.h、stm32f4xx_hal_pwr.h、stm32f4xx_hal_pwr_ex.h、stm32f4xx_hal_rcc.h、stm32f4xx_hal_rcc_ex.h
core_m4.h——》cmsis_version.h、cmsis_compiler.h、mpu_armv7.h、stdint.h
system_stm32f4xx.h——》None
stm32f4xx_hal_def.h——》stm32f4xx.h、Legacy/stm32_hal_legacy.h、stddef.h
cmsis_version.h——》None
mpu_armv7.h——》None
cmsis_compiler.h——》cmsis_gcc.h、stdint.h
stm32_hal_legacy.h——》None
cmsis_gcc.h——》None
stdint.h和stddef.h头文件
在ARM GCC中,标准库通常位于<toolchain_install_path>/lib/gcc/arm-none-eabi/version/include目录下。其中<toolchain_install_path>是ARM GCC编译工具链的安装路径,version是GCC的版本号。
在编译过程中,ARM GCC会搜索标准库的路径,找到stdint.h和stddef.h文件并将其包含到编译环境中,以便编译器能够正确地识别和使用这些标准库头文件中的定义和类型。
三、配置c_cpp_properties.json文件
c_cpp_properties.json文件
——这里需要注意c_cpp_properites.json文件只是方便VS Code软件对整个工程进行检查分析,但不会影响工程的编译链接过程(编译链接过程不需要使用到这个文件);
——不对c_cpp_properites.json文件进行设置,打开源文件会报错;
——设置好c_cpp_properites.json文件后,打开每个源文件就不会报错;
——设置好c_cpp_properites.json文件后,也能跳转到stddef.h和stdint.h文件
(注意defines、compilerPath和intelliSenseMode字段的设置)
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"USE_HAL_DRIVER",
"STM32F407xx"
],
"compilerPath": "/usr/local/arm/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gcc",
"cStandard": "c11",
"cppStandard": "c++98",
"intelliSenseMode": "gcc-arm"
}
],
"version": 4
}
workspace.json文件
把整个文件夹保存为工作区后,会生成一个workspace.json文件
{
"folders": [
{
"path": ".."
}
],
"settings": {
"files.associations": {
"stddef.h": "c",
"stdint.h": "c",
}
}
}
到这一步时的工程目录
四、添加.s启动文件
/* 告诉汇编器使用统一的语法规则 */
.syntax unified
/* 指定目标处理器为Cortex-M4 */
.cpu cortex-m4
/* 指定浮点运算单元为SoftVFP */
.fpu softvfp
/* 指定使用thumb指令集 */
.thumb
/* 定义全局符号ptfVectors */
.global ptfVectors
/* 定义全局符号Default_Handler */
.global Default_Handler
/* .word是ARM汇编语言的一个伪指令,用于声明一个32位的数据项 */
/* .data section的初始化值的起始地址,在链接脚本中定义 */
.word _sidata
/* .data section的起始地址,在链接脚本中定义 */
.word _sdata
/* .data section的结束地址,在链接脚本中定义 */
.word _edata
/* .bss section的起始地址,在链接脚本中定义 */
.word _sbss
/* .bss section的结束地址,在链接脚本中定义 */
.word _ebss
/* 创建一个代码段(.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
/* 创建一个代码段(.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的段,"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 */
/* .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这个符号的大小就是从ptfVectors符号的位置到.所指向的位置之间的字节数 */
/* .首先指向ptfVectors符号的位置,随着数据的写入,.的位置向后移动 */
.size ptfVectors, .-ptfVectors
五、添加.ld链接脚本
/* 程序运行的入口点 */
ENTRY(Reset_Handler)
/* 用户模式栈的最高地址 */
/* _estack在启动文件中定义 */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* RAM的末尾地址 */
_Min_Heap_Size = 0x200; /* 需要的堆空间大小 */
_Min_Stack_Size = 0x400; /* 需要的栈空间大小 */
/* 指定内存区域 */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
}
/* 定义各个程序段 */
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
/* 将所有名称以 .ARM.extab 或 .gnu.linkonce.armextab. 开头的输入段
合并到一个名为 .ARM.extab 的输出段,并将该输出段放置在 FLASH 内存区域。*/
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*) /* .ARM.extab*和.gnu.linkonce.armextab.*段是与ARM架构的异常处理表相关的代码和数据 */
} >FLASH
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);/* PROVIDE_HIDDEN用于定义一个符号并将该符号标记为隐藏的 */
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >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
/* 将从libc.a、libm.a、libgcc.a标准库中移除的对象文件放入/DISCARD/段,链接器链接过程会忽略这个段 */
/DISCARD/ :
{
libc.a (*)
libm.a (*)
libgcc.a (*)
}
/* 将.ARM.attributes段放在地址为0的位置 */
.ARM.attributes 0 :
{
*(.ARM.attributes)
}
}
/*
_estack
_etext
_sidata
_sdata
_edata
_sbss
_ebss
.isr_vector
.text
.rodata
.data
.ccmram
.bss
.user_heap_stack
*/
六、添加Makefile文件
1.修改TARGET变量
2.修改.c源文件和.s启动文件路径
3.修改头文件路径
4.修改.ld链接脚本路径
######################################
# target
######################################
# TARGET = 工程文件夹名
TARGET = Proof
#######################################
# build path
#######################################
# BUILD_DIR = 编译过程生成的文件存放的文件夹名
BUILD_DIR = build
######################################
# sources
######################################
# C sources
## "\"代表行继续;多个值之间需要使用空格或其他空白字符作为分隔符
C_SOURCES = \
Core/Src/main.c \
Core/Src/stm32f4xx_it.c \
Core/Src/stm32f4xx_hal_msp.c \
Core/Src/system_stm32f4xx.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c \
# ASM sources
ASM_SOURCES = \
startup.s
#######################################
# binaries
#######################################
# GCC工具链所在路径
# 1.可以通过GCC_PATH变量在make命令中定义,命令格式:make GCC_PATH=xxx
# 2.可以将其添加到PATH环境变量中
PREFIX = arm-none-eabi-
ifdef GCC_PATH
# 编译器
CC = $(GCC_PATH)/$(PREFIX)gcc
# 汇编器
## -x assembler-with-cpp选项,指示编译器先执行预处理,并将预处理后的代码进行汇编
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
# 从目标文件复制内容到另一个文件,如.elf文件生成.hex和.bin文件
CP = $(GCC_PATH)/$(PREFIX)objcopy
# 报告目标文件的大小
SZ = $(GCC_PATH)/$(PERFIX)size
else
# 编译器
CC = $(PREFIX)gcc
# 汇编器
AS = $(PREFIX)gcc -x assembler-with-cpp
# 从目标文件复制内容到另一个文件,如.elf文件生成.hex和.bin文件
CP = $(PREFIX)objcopy
# 报告目标文件的大小
SZ = $(PERFIX)size
endif
HEX = $(CP) -O ihex
## -S选项,移除符号表和调试信息,从而减小文件大小
BIN = $(CP) -O binary -S
#######################################
# FLAGS
#######################################
# cpu
CPU = -mcpu=cortex-m4
# instruction-set
INSTRUCTION-SET = -mthumb
# fpu
FPU = -mfpu=fpv4-sp-d16
# float-abi
FLOAT-ABI = -mfloat-abi=hard
# mcu
MCU = $(CPU) $(INSTRUCTION-SET) $(FPU) $(FLOAT-ABI)
# ASM defines
ASM_DEFS =
# C defines
C_DEFS = \
-DUSE_HAL_DRIVER \
-DSTM32F407xx
# ASM includes
ASM_INCLUDES =
# C includes
C_INCLUDES = \
-ICore/Inc \
-IDrivers/STM32F4xx_HAL_Driver/Inc \
-IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy \
-IDrivers/CMSIS
# 是否要生成调试文件的标志
DEBUG = 1
# -O(即-O0)代表关闭编译器所有对代码的优化
OPT = -O
# compile flags
## -Wall选项,用于启用所有警告
## -fdata-sections/-ffunction-sections选项,将未使用的数据段、函数段放置在单独的数据段、函数段,在链接过程若未被使用,链接器可以将它们从可执行文件中删除
CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
ifeq ($(DEBUG),1)
# -g:用于告诉编译器生成调试信息
# -gdwarf-2:用于指定生成调试信息的格式,DWARF是一种用于表示调试信息的标准格式
# -gdwarf-2表示使用DWARF-2版本的格式来生成调试信息
CFLAGS += -g -gdwarf-2
endif
# Generate dependency information
# -MMD:告诉编译器生成目标文件(.o文件)的同时,生成一个与之对应的依赖文件(.d文件)。依赖文件包含了目标文件所依赖的头文件信息
# -MP:告诉编译器为每个头文件生成一个伪目标规则。这样,即使某个头文件被删除了,make也不会因为找不到头文件而停止
# -MF:用于指定生成的依赖文件的名称
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
# assemble flags
# -Wa:表示将后续参数传递给汇编器进行处理
# -a:让汇编器生成汇编和源代码混合的文件
# -ad:让汇编器生成带有本地符号表的汇编代码文件
# -alms:指定生成带有扩展信息的汇编代码文件,并保存到指定路径
ASFLAGS = -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst))
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = FLASH_SRAM.ld
# libraries
# -lc:链接C标准库
# -lm:链接数学库
# -lnosys:不链接操作系统相关功能的库
LIBS = -lc -lm -lnosys
LIBDIR =
# -specs=nano.specs:告诉链接器使用nano版本的规范文件,这些文件定义了链接器如何处理程序的内存布局和运行时功能
# -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref 生成一个目标文件映射(map)文件,并通过 -Wl,--cref 选项生成有关交叉引用信息的报告。
# -Wl,--gc-sections 通过此选项告诉链接器进行无用代码剪裁,以减小最终可执行文件的大小。
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
#######################################
# build the application
#######################################
# list of C program objects
# addprefix函数
# 用法:$(addprefix <prefix>,<names...>)
# 功能:把前缀<prefix>加到<names>中的每个字符串前面
# 返回:加过前缀的文件名序列
# 示例:$(addprefix src/,foo bar) 返回值是src/foo src/bar
# notdir函数
# 用法:$(nordir <names...>)
# 功能:从文件名序列<names>中取出非目录部分
# 返回:文件名序列<names>的非目录部分
# 示例:$(notdir src/foo.c hacks) 返回值是foo.c hacks
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
# vpath特殊变量
# 功能:给make工具指定文件的搜索路径
# sort函数
# 用法:$(sort <list>)
# 功能:给字符串<list>中的单词排序,并去掉相同的单词
# 返回:排序后的字符串
# 示例:$(sort foo bar lost) 返回值是bar foo lost
# dir函数
# 用法:$(dir <names...>)
# 功能:从文件名序列<names>中取出目录部分
# 返回:文件名序列<names>的目录部分
# 示例:$(dir src/foo.c hacks) 返回值是src/ ./
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
$(SZ) $@
# | $(BUILD_DIR) 表示当构建目标文件 $(BUILD_DIR)/%.o 时,$(BUILD_DIR) 是一个延迟依赖项。
# 这意味着在构建目标文件之前,会首先检查构建目录 $(BUILD_DIR) 是否存在,但构建目录的更新不会触发重新构建目标文件。这在确保构建目录存在的同时,避免了不必要的重新编译。
# 例如,如果 $(BUILD_DIR)/%.o: %.c Makefile 规则中只指定了 .c 文件和 Makefile 作为依赖项,当 .c 文件和 Makefile 更新时都会触发重新编译对应的目标文件。
# 而通过添加 | $(BUILD_DIR) 来延迟对构建目录的依赖性(只检查是否存在),可以确保当构建目录不存在或需要创建新的目录时才进行操作,并避免了不必要的重新编译。
# $(CC)是一个变量,表示编译器的路径
# -c是编译器选项,表示只进行编译而不进行链接
# $(CFLAGS)是传递给编译器的
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
$(CC) -c $(CFLAGS) $(ASFLAGS) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR):
mkdir $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(BIN) $< $@
# #######################################
# # clean up
# #######################################
clean:
-rm -rf $(BUILD_DIR)
#######################################
# dependencies
#######################################
# 这句命令是在 makefile 中用于包含所有生成的依赖文件。以便 make 工具能够正确地处理依赖关系。
# $(wildcard $(BUILD_DIR)/*.d):这是一个 makefile 的函数,用于匹配 $(BUILD_DIR) 目录下所有的 .d 文件。$(BUILD_DIR) 是一个变量,通常用于指定生成的目标文件和依赖文件的目录。
# -include:这是一个 makefile 的指令,用于包含指定的依赖文件。加上 - 前缀表示即使这些依赖文件不存在,也不会导致 make 失败。
-include $(wildcard $(BUILD_DIR)/*.d)
七、编译、烧录、调试
1.编译
在当前文件夹下新建终端,输入make
仔细比对原先工程和此工程的编译结果text、data、bss、dec和hex的数值会发现有细微差别,是因为此工程我设置了关闭编译器所有对代码的优化!!!!
2.烧录
首先烧录器连上STM32,另外单独打开一个终端,输入以下指令:
openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg
回到VS Code的内置终端,分别输入:
program hex文件的绝对路径
reset
exit
3.调试
在VS Code内置终端,分别输入,可以进行调试
arm-none-eabi-gdb elf文件的路径
target remote localhost:3333
八、配置tasks.json和launch.json文件
1.tasks.json文件
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build",
"command": "/usr/bin/make",
"args": [],
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "build",
"isDefault": true
},
"detail": "编译器: make",
"problemMatcher": []
},
]
}
2.launch.json文件(修改程序可执行文件路径)
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name":"ARM Debug",
"cwd":"${workspaceFolder}",
"request":"launch",
"type":"cppdbg",
"miDebuggerPath": "/usr/local/arm/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gdb",
"program":"${workspaceFolder}/build/Proof.elf",
"targetArchitecture": "arm",
"setupCommands": [
{
"text":"file '${workspaceFolder}/build/Proof.elf'"
},
{
"text":"target remote localhost:3333"
},
{
"text":"monitor reset"
},
{
"text":"monitor halt"
},
{
"text":"load"
},
]
},
]
}
八、填坑
1.在.s启动文件里,汇编器不支持分号作为注释符号
错误写法
;告诉汇编器使用统一的语法规则
.syntax unified
;指定目标处理器为Cortex-M4
.cpu cortex-m4
;指定浮点运算单元为SoftVFP
.fpu softvfp
;指定使用thumb指令集
.thumb
;定义全局符号ptfVectors
.global ptfVectors
;定义全局符号Default_Handler
.global Default_Handler
正确写法
/* 告诉汇编器使用统一的语法规则 */
.syntax unified
/* 指定目标处理器为Cortex-M4 */
.cpu cortex-m4
/* 指定浮点运算单元为SoftVFP */
.fpu softvfp
/* 指定使用thumb指令集 */
.thumb
/* 定义全局符号ptfVectors */
.global ptfVectors
/* 定义全局符号Default_Handler */
.global Default_Handler
最后
到此完成了整个工程的构建,代码是可以成功烧录的;我也把.s文件、.ld文件和Makefile文件的内容都写在上面了,需要这个工程的下载链接的话私信我!