Ronan Blog

罗华东的博客 | 向前每多走一步,热爱和勇气就会多一分。

手动搭建 stm32 标准外设库+Makefile工程

2024-09-03 2 min read Docs Ronan

本教程以 stm32f407 为例,其他 stm32 芯片配置方式大同小异。

1.下载官方标准外设库

进入 ST 官网的嵌入式软件板块 ,根据自身板子型号选择,有F0-F4多种型号。

之后根据提示选择下载即可,如果没有意外的话,应该会获得一个类似 en.stsw-stm32065_v1-9-0.zip(这是 F4 的标准库) 的压缩包。

2.创建工程

2.1建立工程结构

1.解压从官网下载的标准外设库 en.stsw-stm32065_v1-9-0.zip ,获得一个 STM32F4xx_DSP_StdPeriph_Lib_V1.9.0 目录

2.新建一个my_project目录,并在该目录下创建四个子目录:driverincludelibsrc

3.将标准固件库目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/CMSIS/Include文件夹,以及固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/TrueSTUDIO文件夹,全部拷贝到my_project/cmsis文件夹下

4.将标准固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/STM32F4xx_StdPeriph_Driver文件夹下的incsrc文件夹全部拷贝移植到lib文件夹下

5.将标准固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include文件夹目录下的stm32f4xx.hsystem_stm32f4xx.h文件拷贝到include文件夹下

6.将标准固件文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Project/STM32F4xx_StdPeriph_Templates目录下的main.cstm32f4xx_it.csystem_stm32f4xx.c拷贝移植到src文件夹下,stm32f4xx_conf.hstm32f4xx_it.h拷贝到include文件夹中,其中main.c文件是STM32工程文件的主函数程序,(main.c 也可以不拷贝,自行创建即可)

7.将标准固件库文件目录.../STM32F4xx_DSP_StdPeriph_Lib_V1.9.0/Project/STM32F4xx_StdPeriph_Templates/TrueSTUDIO/STM32F40_41xxx文件夹下的STM32F417IG_FLASH.ld拷贝到my_project的工程根目录下,并重命名为stm32_flash.ld

2.2编写 Makefile

到这里我们需要自行手动编写一个 Makefile…

逗你玩呢,我已经写好了,请慢用:


CROSS_COMPILE = arm-none-eabi-

# 将源文件放在这里 (*.c)
SRCDIR=./src
LIBDIR=./lib/src

SRC = ${wildcard ${LIBDIR}/*.c} \
	  ${wildcard ${SRCDIR}/*.c}

# 将以该名称生成二进制文件 (.elf, .bin, .hex)
PROJECT_NAME=out

# 编译器设置。仅编辑 CFLAGS 以包含其他头文件。
CC = $(CROSS_COMPILE)gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)gcc
OBJCOPY = ${CROSS_COMPILE}objcopy

# compiler flags 编译器标志
CFLAGS = -g -O2 -Wall -Tstm32_flash.ld
CFLAGS += -DUSE_STDPERIPH_DRIVER
CFLAGS += -D STM32F40_41xxx
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -I .
CFLAGS += -specs=nosys.specs

# 包含 STM 库中的文件
CFLAGS += -I./cmsis/Include
CFLAGS += -I./include
CFLAGS += -I./lib/inc

SRC += ./cmsis/TrueSTUDIO/startup_stm32f40_41xxx.s

# 将 SRC 中每个 .c 文件的扩展名替换为 .o,生成对应的目标文件。例如,如果 SRC 中有一个文件 main.c,它会生成 main.o,以便后续编译和链接时使用这些目标文件。
OBJS = $(SRC:.c=.o)

vpath %.c ./lib/src \
vpath %.c ./src \

.PHONY: proj

all:proj

proj:$(PROJECT_NAME).elf

$(PROJECT_NAME).elf: $(SRC)
	$(CC) $(CFLAGS) $^ -o $@
	$(OBJCOPY) -O ihex $(PROJECT_NAME).elf $(PROJECT_NAME).hex
	$(OBJCOPY) -O binary $(PROJECT_NAME).elf $(PROJECT_NAME).bin

clean:
	rm -f *.o $(PROJECT_NAME).elf $(PROJECT_NAME).hex $(PROJECT_NAME).bin

flash:proj
	st-flash write $(PROJECT_NAME).bin Ox8000000
	STM32_Programmer_CLI -c port=SWD -w $(PROJECT_NAME).hex 

将以上内容复制并保存为Makefile,将其放到my_project工程根目录下

3.编译前准备(重要!!!)

完成上面的步骤,恭喜你!已经成功了3/4…

但在正式编译之前,你需要进行以下配置:

1.将my_project/lib/src目录下的stm32f4xx\_fmc.c文件删除

2.将my_project/src目录下的stm32f4xx\_it.c文件第25行main.h头文件引用(#include “main.h”)注释(或删除)137行的延时函数调用(TimingDelay_Decrement();)注释(或删除)

到这里你就可以通过make来成功编译构建项目啦~~~

也许有时候遭遇问题

也可能你完成了上面所有的设置,但是在进行make时依然遇到了错误,这时候你就可以通过下面的方法解决:

  • my_project目录下的stm32\_flash.ld链接文件的75行(也就是} >FLASH这一行的上方)添加_exit = .;,否则编译会报错(注意:这是由于交叉编译器版本的问题

4.去除警告(可选)

直到你兴致勃勃且成功地运行了make命令,可惜结果对于稍有洁癖的你来说简直不能容忍,因为出现了一堆警告信息:

/Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(libc_a-closer.o): in function `_close_r':
closer.c:(.text._close_r+0xc): warning: _close is not implemented and will always fail
/Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(libc_a-lseekr.o): in function `_lseek_r':
lseekr.c:(.text._lseek_r+0x14): warning: _lseek is not implemented and will always fail
/Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(libc_a-readr.o): in function `_read_r':
readr.c:(.text._read_r+0x14): warning: _read is not implemented and will always fail
/Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libg.a(libc_a-writer.o): in function `_write_r':
writer.c:(.text._write_r+0x14): warning: _write is not implemented and will always fail
/Applications/ArmGNUToolchain/13.3.rel1/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld: warning: out.elf has a LOAD segment with RWX permissions
arm-none-eabi-objcopy -O ihex out.elf out.hex
arm-none-eabi-objcopy -O binary out.elf out.bin

所以,有没有解决办法呢?当然有:

my_project/src目录下新建一个syscalls.c文件:

#include <sys/types.h>  // 包含必要的类型定义

int _close(int file) {
    return -1;
}

int _lseek(int file, int ptr, int dir) {
    return -1;
}

int _read(int file, char *ptr, int len) {
    return 0;
}

int _write(int file, char *ptr, int len) {
    return len;
}

caddr_t _sbrk(int incr) {
    extern char _end;  // 链接器脚本中定义的堆的起始位置
    static char *heap_end;
    char *prev_heap_end;

    if (heap_end == 0) {
        heap_end = &_end;
    }

    prev_heap_end = heap_end;
    heap_end += incr;

    return (caddr_t)prev_heap_end;
}

再次执行 make 即可。