【嵌入式】使用链接脚本从零创建STM32代码工程

​​​

推荐一款 求职面试、刷题学习 的神器:👉*点击跳转* ,快来看看吧!


Use Linker Script File to build an STM32 project from scratch

链接脚本文件,用于控制链接过程,关于编译和链接,可以参考相关文档。以stm32在Linux环境下编译作为示例,项目链接git库地址:

tangquan/test.ld_file.proghttps://gitee.com/tq797/test.ld_file.prog

各种原因,视频参考教程的地址在文件startup_stm32f401xc.c里面。Makefile语法参考文章:

【Linux开发】一个小规模工程的Makefile模板_欢迎光临-CSDN博客本Makefile适用于小规模软件工程。修改SOURCE_PATH变量设置源码路径,修改BINARY_PATH变量设置输出文件的路径,OBJECT_PATH变量为中间.o文件的存放目录,INCLUDE_PATH变量为头文件包含路径,TARGET变量为输出文件名。Makefile会输出依赖关系文件“xxx.o.d”,以便根据依赖关系只进行必要的编译工作。# GNU的make工作时的执行步骤如下...https://blog.csdn.net/tq384998430/article/details/100087283?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163489021416780274187536%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163489021416780274187536&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-3-100087283.pc_v2_rank_blog_default&utm_term=makefile&spm=1018.2226.3001.4450

ARM处理器的裸机代码编译可以使用armcc编译器(MDK,收费)或者gcc编译器(免费)。我使用的是arm-none-eabi-gcc,百度一下就能下载。交叉编译器的命名规则比较迷,这里arm表示处理器架构,none表示未指定制造商,eabi表示遵循嵌入式应用二进制接口标准,未指定操作系统。链接脚本的语法参考视频教程。

Makefile:

PROJECT				= bare_metal_arm
TARGET				= $(PROJECT)
TARGET_ELF			= $(PROJECT).elf
TARGET_BIN			= $(PROJECT).bin
TARGET_HEX			= $(PROJECT).hex
TARGET_MAP			= $(PROJECT).map

MACH				= cortex-m4

ROOT_DIR			= .
OBJ_DIR				= $(ROOT_DIR)/obj
OUT_DIR				= $(ROOT_DIR)/out

SOURCE_PATH			?= $(ROOT_DIR)
OBJECT_PATH			?= $(OBJ_DIR)
INCLUDE_PATH 		:= 

LINKER_SCRIPT		= $(ROOT_DIR)/link.ld
LINKER_SPECS		= nano.specs

SOURCES				:= $(foreach tmp,$(SOURCE_PATH),$(wildcard $(tmp)/*.c $(tmp)/*.cpp))
OBJECTS				:= $(addsuffix .o,$(addprefix $(OBJECT_PATH)/,$(basename $(notdir $(SOURCES)))))
DEPENDS				:= $(addsuffix .d,$(OBJECTS))

# $(info SOURCES: $(SOURCES))
# $(info OBJECTS: $(OBJECTS))
# $(info DEPENDS: $(DEPENDS))



CC := arm-none-eabi-gcc
# LD := arm-none-eabi-ld
LD := arm-none-eabi-gcc        # use gcc as the linker which can automatically find the dependent libraries
OBJCOPY := arm-none-eabi-objcopy

CFLAGS := -c -mcpu=$(MACH) -mthumb -std=gnu11 -Wall -O0
# CFLAGS += -ffunction-sections
LDFLAG := -mcpu=$(MACH) -mthumb -T link.ld -Wl,-Map=$(OUT_DIR)/$(TARGET_MAP) --specs=$(LINKER_SPECS) -mfloat-abi=soft
# LDFLAG += -Wl,--gc-sections
# LDFLAG += -L/c/gcc-arm-none-eabi-10.3/arm-none-eabi/lib
# LDFLAG += -L/c/gcc-arm-none-eabi-10.3/lib/gcc/arm-none-eabi/10.3.1




all : $(TARGET_ELF)
	$(info Build "$(PROJECT)" finished)

$(TARGET_ELF): $(OBJECTS)
#	$(info LD $@)
	$(LD) $(LDFLAG) -o $(OUT_DIR)/$@ $^
	$(OBJCOPY) -O binary -R .note -R .comment -S $(OUT_DIR)/$@ $(OUT_DIR)/$(TARGET_BIN)

-include $(DEPENDS)

$(foreach tmp,$(SOURCES),\
	$(eval $(addprefix $(OBJECT_PATH)/,$(addsuffix .o,$(basename $(notdir $(tmp))))):; \
				${CC} ${CFLAGS} $(INCLUDE_PATH) \
				-MMD -MF $(OBJECT_PATH)/$(basename $(notdir $(tmp))).o.d \
				-c -o $(OBJECT_PATH)/$(basename $(notdir $(tmp))).o $(tmp)))



.PHONY : clean setenv
clean:
	rm *.o ${TARGET}.* obj/* out/* -rf

linker script file:

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20005000;    /* end of 20K RAM */

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x100; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
    FLASH(rx):ORIGIN =0x08000000,LENGTH =64K
    SRAM(rwx):ORIGIN =0x20000000,LENGTH =20K
}

/* Define output sections */
SECTIONS
{
    /* The startup code goes first into FLASH */
    .isr_vector :
    {
        . = ALIGN(4);
        KEEP(*(.isr_vector)) /* Startup code */
        . = ALIGN(4);
    } >FLASH

    .text :
    {
        . = ALIGN(4);

        *(.text)           /* .text sections (code) */
        *(.text*)          /* .text* sections (code) */
        *(.rodata)         /* .rodata sections (constants, strings, etc.) */
        *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
        *(.glue_7)         /* glue arm to thumb code */
        *(.glue_7t)        /* glue thumb to arm code */

        KEEP (*(.init))
        KEEP (*(.fini))

        . = ALIGN(4);

        _etext = .;
        _exit = _etext;
    }> FLASH

    _la_data = LOADADDR(.data);
    
    .data :
    {
        . = ALIGN(4);

        _sdata = .;
        *(.data)           /* .data sections */
        *(.data*)          /* .data* sections */

        . = ALIGN(4);
        _edata = .;
    }> SRAM AT> FLASH

    .bss :
    {
        _sbss = .;
        __bss_start__ = _sbss;
        *(.bss)
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
        __bss_end__ = _ebss;
    }> SRAM

    PROVIDE ( end = _ebss );    /* head starts after the .bss section */
    PROVIDE ( _end = _ebss );

    /* User_heap_stack section, used to check that there is enough RAM left */
    ._user_heap_stack :
    {
        . = ALIGN(4);
        . = . + _Min_Heap_Size;
        . = . + _Min_Stack_Size;
        . = ALIGN(4);
    } >SRAM

    /* Remove information from the standard libraries */
    /DISCARD/ :
    {
        libc.a ( * )
        libm.a ( * )
        libgcc.a ( * )
    }

}

编译出来的文件:

关于elf文件和bin文件的区别参考前面的文章:

【计算机】ELF文件和BIN文件_欢迎光临-CSDN博客_bin文件和elf文件原文:http://blog.chinaunix.net/uid-24148050-id-362928.html文件的内容:1. BIN文件是 raw binary 文件,这种文件只包含机器码。BIN文件中的所有数据都是机器可以执行的指令码、指令参数或者常量数据,不包含其它调试信息。单片机中烧写的都是bin文件。2. ELF文件除了机器码外,还包含其它额外的信息,如段的加载地址,运行地址,重定位表,符号表等。ELF文件相当于是对bin文件的一个包装和解释,不仅仅包含程序的机器码,还包含加载.https://blog.csdn.net/tq384998430/article/details/115246520

elf文件可以用于仿真调试,bin文件就可以下载到MCU中去,segger j-flash有linux版本:

SEGGER - The Embedded Experts - Downloads - J-Link / J-Tracehttps://www.segger.com/downloads/jlink/使用JFlash命令可以打开UI界面,和windows上的j-flash是一样的:

 下载运行,使用的是STM32F401CCU,调试器为JLink使用SWD接口。使用openocd调试软件,参考文章:

https://support.particle.io/hc/en-us/articles/360039251414-JTAG-and-SWD-Guidehttps://support.particle.io/hc/en-us/articles/360039251414-JTAG-and-SWD-Guide执行下面指令连接上芯片:

openocd.exe -f interface/jlink.cfg -c "transport select swd" -f target/stm32f4x.cfg

注意使用 -c "transport select swd" 选择端口为SWD,不然连接不上。连接上芯片之后openocd会创建一个服务,使用gdb或者telnet可以连接上openocd,这里我使用gdb。重新打开一个Terminal:

1、打开GDB调试器:
$ arm-none-eabi-gdb

2、连接至openocd服务:
(gdb) target remote 127.0.0.1:3333

3、打开之前生成的elf文件(注意编译的时候需要加上-g选项添加调试信息)
(gdb) file a.elf

4、下载至芯片
(gdb) load a.elf

5、continue执行程序
(gdb) c
 


 推荐一款 求职面试、刷题学习 的神器:👉*点击跳转* ,快来看看吧!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值