Linux下的STM32简易开发环境搭建(Makefile)

废话一下:不得不说,Windows上的Keil MDK软件确实很强大,从设备支持,到代码编辑,再到仿真器调试,硬件开发一条龙都可以搞定,现在还在活跃更新。不过Linux在编译方面能力这么强,怎么不会支持基于ARM Cortex M系列内核的STM32系列主控呢?这不,最近还真折腾上了,使用记事本开发STM32硬件!

不过别一听到“记事本”就退缩啦!在另一篇博客还是会介绍使用Eclipse开发STM32的!届时,图形化编程、调试,还是会很方便的!本文旨在加深对交叉编译的理解、巩固Makefile文件的编写、熟悉STM32硬件开发底层原理,为以后嵌入式系统的开发做准备。

环境准备

软件下载

1、交叉编译工具链下载

https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3/RC1.1/gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2?revision=c34d758a-be0c-476e-a2de-af8c6e16a8a2?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2019-q3-update
(如果链接失效,可进入 arm.com -> Resources -> ARM Developer Site (developer.arm.com) -> Tools and Software -> Open Source Software -> Developer Tools -> GNU Toolchain -> GNU RM -> Downloads 下载最新版本)

工具链下载页面

2、STM32标准外设库下载

https://www.st.com/content/ccc/resource/technical/software/firmware/48/ab/e5/17/0d/79/43/74/stsw-stm32054.zip/files/stsw-stm32054.zip/jcr:content/translations/en.stsw-stm32054.zip
(如果链接失效,可进入 st.com -> Tools & Software -> Embedded Software -> STM32 Embedded Software -> STM32 Standard Peripheral Libraries -> 对应型号F1/F4…)

st.com STM32标准外设库下载页面

软件准备

1、安装必需软件包

首先要确保系统有交叉编译的一些必要软件包。具体需要什么软件包可参考OpenWRT的交叉编译系统需求:
https://openwrt.org/docs/guide-developer/build-system/install-buildsystem

比如对于Ubuntu用户,需要执行:(摘自该网站)

sudo apt-get install subversion build-essential libncurses5-dev zlib1g-dev gawk git ccache gettext libssl-dev xsltproc zip

对于Archlinux用户,需要执行:(同样摘自该网站)

sudo pacman -S --needed asciidoc bash bc binutils bzip2 fastjar flex git gcc util-linux gawk intltool zlib make cdrkit ncurses openssl patch perl-extutils-makemaker rsync unzip wget gettext libxslt boost libusb bin86 sharutils b43-fwcutter findutils time

2、解压工具链

将下载的交叉编译工具链解压缩,路径随意。Makefile可以设置工具链位置。

进入bin文件夹,该文件夹下面的文件就是我们要用到的编译器啦

3、解压STM32标准外设库

将下载的外设库解压,位置随意,Makefile可以设置include路径。

下载得到的两个文件夹,一个工具链,一个外设库

4、设置这两个文件夹为只读

iostreamstdio.h这些文件,我们一般不能修改。修改库文件往往会造成意想不到的后果,为了避免库文件被不小心修改,强烈建议将这些文件设置为只读。

cd ~/Downloads/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src
chmod 555 *
cd ~/Downloads/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/inc
chmod 555 *
cd ~/Downloads/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport
chmod 555 *
cd ~/Downloads/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x
chmod 555 *

不过注意:尽量不要将包含库文件的文件夹设置成只读的了,这样可能会造成编译器无法在目录中生成.o文件,导致编译失败

搭建工程

上面我们就完成了环境的搭建。下面就让我们新建一个项目文件夹吧。

目录结构

我在主文件夹创建了一个名为“LinuxSTM32”的文件夹,然后在里面创建了以下文件/文件夹,其目录架构如下:

LinuxSTM32
 |- Libraries  # 存放自己编写的库
 |   |- init.h  # 自己写的初始化库
 |   |- init.c
 |   |- led.h  # 自己写的LED库
 |   |- led.c
 |
 |- Main # 存放自己编写的主程序
 |   |- main.c  # 自己写的主程序
 |
 |- Devices # 存放设备相关文件
 |   |- STM32F103C8  # 待会儿会介绍这些文件从哪而来
 |       |- stm32f10x.h
 |       |- stm32f10x_conf.h
 |       |- system_stm32f10x.h
 |       |- system_stm32f10x.c
 |       |- startup_stm32f10x_md.s
 |
 |- Objects # 存放编译过程中产生的文件
 |- bin # 存放目标文件
 |   |- target.hex  #将会产生
 |
 |- Makefile # Makefile文件

主要源代码

init.h源代码:

#ifndef __init_H
#define __init_H

void InitGPIOC( void );
void delay_ms( u32 );

#endif

init.c源代码:

#include "init.h"
#include "stm32f10x.h"
#include "pindef.h"

void InitGPIOC( void ) {
    
    GPIO_InitTypeDef GPIO_InitStructure;
	
	SystemInit();
	
	RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	
	GPIO_Init(LED_GPIO_GRP, &GPIO_InitStructure);
}

void delay_ms(u32 i) {
	u32 temp;
	SysTick->LOAD = 9000*i;
	SysTick->CTRL = 0x01;
	SysTick->VAL = 0;
	do {
		temp = SysTick->CTRL;
	}
	while((temp&0x01)&&(!(temp&(1<<16))));
	SysTick->CTRL = 0;
	SysTick->VAL = 0;
}

led.h源代码:

#ifndef __led_H

#define __led_H

void Light( void );
void Mute( void );

#endif

led.c源代码:

#include "led.h"
#include "stm32f10x.h"
#include "pindef.h"

void Light( void ) {
    
    GPIO_SetBits(LED_GPIO_GRP, LED_GPIO_PIN);
}

void Mute( void ) {
    
    GPIO_ResetBits(LED_GPIO_GRP, LED_GPIO_PIN);
}

main.c源代码:

#include "init.h"
#include "led.h"

int main( void ) {
    
    InitGPIOC();
    
    while(1) {
        Light();
        delay_ms(1500);
        Mute();
        delay_ms(1500);
    }
}

pindef.h源代码:

#ifndef __pindef_H

#define __pindef_H

#define LED_GPIO_GRP GPIOC
#define LED_GPIO_PIN GPIO_Pin_13
#define LED_GPIO_CLK RCC_APB2Periph_GPIOC

#endif

复制必要文件

1、进入解压的固件库,进入文件夹:STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/(不同设备不同路径,根据实际情况修改),
stm32f10x.hsystem_stm32f10x.csystem_stm32f10x.h、以及根据自己设备情况(小容量、中容量、大容量等)选择startup/TRUEstudio中的文件(STM32F103ZE选hd、STM32F103C8选md,注意不是arm文件夹下的,是TRUEstudio的),这几个文件,复制到我们项目文件夹、Devices目录下(参考上面目录结构)

2、进入解压的固件库,进入文件夹:
STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/
stm32f10x_conf.h复制到项目文件夹、Devices目录下(参考上面目录结构)

编辑开关文件

根据需要注释Devices目录下stm32f10x_conf.h文件中的包含关系。本例中我们只需要使用GPIO功能,所以只需保留stm32f10x_gpio.hstm32f10x_rcc.hmisc.h

开始编译

手动编译测试

关于交叉编译

交叉编译,即在一个平台的处理器上编译生成另外一种处理器可执行的代码。我们现在就要从x86_64平台编译生成ARM Cortex M3平台代码。

手动编译

我们先手动编译试试,看能否产生目标文件。

1、编译源文件

首先进入Libraries文件夹:

cd ./Libraries

然后设置C_INCLUDE_PATH临时环境变量,将标准外设库的头文件文件夹、CMSIS核心接口文件夹、设备文件夹、库文件夹添加进去:

export C_INCLUDE_PATH=\
/xxxxxx/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport:\
/xxxxxx/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/inc:\
/xxxxxx/LinuxSTM32/Devices/STM32F103C8:\
/xxxxxx/LinuxSTM32/Libraries

设置参数,开始逐个编译:(注意包含stm32f10x_conf.h

/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 --include /xxxxxx/LinuxSTM32/Devices/STM32F103C8/stm32f10x_conf.h -o led.o led.c
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 --include /xxxxxx/LinuxSTM32/Devices/STM32F103C8/stm32f10x_conf.h -o init.o init.c

可以看到生成了两个.o文件:

进入Main文件夹,继续编译:

cd ../Main
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 --include /xxxxxx/LinuxSTM32/Devices/STM32F103C8/stm32f10x_conf.h -o main.o main.c

2、编译库文件

进入设备文件夹,编译启动汇编文件:

cd ../Devices/STM32F103C8
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -g -Wa,--warn -o startup_stm32f10x_md.o startup_stm32f10x_md.s

进入外设库文件夹,继续执行编译:

cd /xxxxxx/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -o stm32f10x_gpio.o stm32f10x_gpio.c
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -o stm32f10x_rcc.o stm32f10x_rcc.c
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -o misc.o misc.c

进入设备文件夹,继续编译:

cd /xxxxxx/LinuxSTM32/Devices/STM32F103C8
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -o system_stm32f10x.o system_stm32f10x.c

进入CMSIS库文件夹,继续编译:

cd /xxxxxx/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -o core_cm3.o core_cm3.c

此时可能会出现错误:

发生这种情况可根据https://gist.github.com/timbrom/1942280修改core_cm3.c文件第736、753行处:

再次编译,编译成功。

3、连接.o文件

现在我们还差一个引导链接器连接的文件。从外设库STM32F10x_StdPeriph_Lib_V3.5.0/Project/STM32F10x_StdPeriph_Template/TrueSTUDIO/STM3210E-EVAL/复制stm32_flash.ld到Objects文件夹,然后根据自己设备容量情况编辑:

// STM32F103C8

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20002000;    /* 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
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 8K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
}
// STM32F103ZE

// 直接用该文件即可,无需修改

然后将刚刚生成的.o文件全部移动到Objects文件夹,别漏了哦!

开始连接:

cd /xxxxxx/LinuxSTM32/Objects
/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc core_cm3.o init.o led.o main.o misc.o startup_stm32f10x_md.o stm32f10x_gpio.o stm32f10x_rcc.o system_stm32f10x.o -mthumb -mcpu=cortex-m3 -T stm32_flash.ld -specs=nosys.specs -static -Wl,-cref,-u,Reset_Handler -Wl,-Map=test.map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80 -Wl,--start-group -lc -lm -Wl,--end-group -o target.elf

然后就可以看到,当前目录下多了一个target.elf文件。

4、生成HEX文件

/xxxxxx/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-objcopy target.elf -O ihex final.hex

如果要生成bin文件,可执行:

arm-none-eabi-objcopy target.elf final.bin

5、烧录

首先连接串口

可以使用stm32flash在Linux上给STM32系列单片机烧录程序:

sudo stm32flash -w /xxxxxx/LinuxSTM32/Objects/final.hex -v -g 0 /dev/ttyUSB0
烧录成功

6、效果

可以看到,板载LED有规律地闪烁,实验成功。

写Makefile实现自动编译

Makefile文件

Makefile无非将手动的繁琐操作变成自动的了,使用户能通过编辑Makefile中一些主要参数(如工程路径、编译器路径、外设库路径),快速构建出所需的目标代码。这里我写了一个Makefile文件:(写这个文件花了我一整天啊啊啊,稍后会将代码上传至GitHub,欢迎commit!)

# Makefile for STM32F10x

# User Definitions 修改这四行就行了

project_name = STM32F10x_project
device_vol = md
compiler_root = /home/ans/Downloads/gcc-arm-none-eabi-8-2019-q3-update
StdPeriph_root = /home/ans/Downloads/STM32F10x_StdPeriph_Lib_V3.5.0

USER_INCLUDE = export C_INCLUDE_PATH=./src:./src/*/:./src/*/*/

# Automatic Defintitions
target = $(project_name)

device_vol_upper = $(shell echo $(device_vol) | tr a-z A-Z)

USERCODE_SRC := $(wildcard ./src/*.c ./src/*/*.c ./src/*/*/*.c)
USERCODE_OBJ := $(addprefix ./obj/, $(notdir $(USERCODE_SRC:%.c=%.o)))

STDPERIPH_SRC := $(wildcard $(StdPeriph_root)/Libraries/STM32F10x_StdPeriph_Driver/src/*.c)
STDPERIPH_OBJ := $(addprefix ./obj/, $(notdir $(STDPERIPH_SRC:%.c=%.o)))

CMSIS_SRC = $(StdPeriph_root)/Libraries/CMSIS/CM3/CoreSupport/core_cm3.c
CMSIS_OBJ = ./obj/core_cm3.o

SYSTEM_SRC = $(StdPeriph_root)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
SYSTEM_OBJ = ./obj/system_stm32f10x.o

STARTUP_SRC = $(StdPeriph_root)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/TrueSTUDIO/startup_stm32f10x_$(device_vol).s
STARTUP_OBJ = ./obj/startup_stm32f10x_$(device_vol).o

FLASH_SRC = ./lib/devices/$(device_vol)/stm32_flash.ld

# Binarary files
ELF_FILE = ./bin/$(project_name).elf
BIN_FILE = ./bin/$(project_name).bin
HEX_FILE = ./bin/$(project_name).hex

########## Compile Configurations ##########
ARMCC = $(compiler_root)/bin/arm-none-eabi-gcc
ARMCC_INCLUDE_DIRS = \
	-I $(StdPeriph_root)/Libraries/CMSIS/CM3/CoreSupport\
	-I $(StdPeriph_root)/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x\
	-I $(StdPeriph_root)/Libraries/STM32F10x_StdPeriph_Driver/inc\
	-I ./lib/devices/*/\
	-I ./src\
	-I ./src/*/\
	-I ./src/*/*/
ARMCC_FLAGS = -mthumb -mcpu=cortex-m3

########## Scripts ##########
.PHONY: all elf bin hex clean

all: $(BIN_FILE) $(HEX_FILE)
	@echo "Successfully built all"

$(BIN_FILE): $(ELF_FILE)
	@echo "Operation not supported"
	-mkdir ./bin
	#$(compiler_root)/bin/arm-none-eabi-objcopy $(ELF_FILE) $(BIN_FILE)
	@echo "Successfully built $(target).bin"

$(HEX_FILE): $(ELF_FILE)
	@echo "Building $(target).hex"
	$(compiler_root)/bin/arm-none-eabi-objcopy $(ELF_FILE) -O ihex $(HEX_FILE)
	@echo "Successfully built $(target).hex"

$(ELF_FILE): \
	$(USERCODE_OBJ) \
	$(STDPERIPH_OBJ) \
	$(CMSIS_OBJ) \
	$(SYSTEM_OBJ) \
	$(STARTUP_OBJ)
	
	@echo "Linking files"
	-mkdir ./bin
	$(ARMCC) $(ARMCC_FLAGS) -specs=nosys.specs -static -Wl,-cref,-u,Reset_Handler -Wl,-Map=test.map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80 -Wl,--start-group -lc -lm -Wl,--end-group -T $(FLASH_SRC) $(wildcard ./obj/*.o) -o $(ELF_FILE)
	@echo "Successfully linked files to $(target).elf"

obj/%.o: ./src/%.c
	@echo "Building usercode"
	-mkdir ./obj
	$(USER_INCLUDE)
	$(ARMCC) -c $(ARMCC_FLAGS) $(ARMCC_INCLUDE_DIRS) -D STM32F10X_$(device_vol_upper) -D USE_STDPERIPH_DRIVER -o $@ $<
obj/%.o: ./src/*/%.c
	@echo "Building usercode"
	-mkdir ./obj
	$(USER_INCLUDE)
	$(ARMCC) -c $(ARMCC_FLAGS) $(ARMCC_INCLUDE_DIRS) -D STM32F10X_$(device_vol_upper) -D USE_STDPERIPH_DRIVER -o $@ $<
obj/%.o: ./src/*/*/%.c
	@echo "Building usercode"
	-mkdir ./obj
	$(USER_INCLUDE)
	$(ARMCC) -c $(ARMCC_FLAGS) $(ARMCC_INCLUDE_DIRS) -D STM32F10X_$(device_vol_upper) -D USE_STDPERIPH_DRIVER -o $@ $<

obj/%.o: $(StdPeriph_root)/Libraries/STM32F10x_StdPeriph_Driver/src/%.c
	@echo "Building StdPeriph"
	-mkdir ./obj
	$(USER_INCLUDE)
	$(ARMCC) -c $(ARMCC_FLAGS) $(ARMCC_INCLUDE_DIRS) -D STM32F10X_$(device_vol_upper) -D USE_STDPERIPH_DRIVER -o $@ $<
	@echo "Successfully built StdPeriph"

$(CMSIS_OBJ): $(CMSIS_SRC)
	@echo "Building CMSIS"
	-mkdir ./obj
	$(ARMCC) -c $(ARMCC_FLAGS) -o $(CMSIS_OBJ) $(CMSIS_SRC)
	@echo "Successfully built CMSIS"

$(SYSTEM_OBJ): $(SYSTEM_SRC)
	@echo "Building system"
	-mkdir ./obj
	$(USER_INCLUDE)
	$(ARMCC) -c $(ARMCC_FLAGS) $(ARMCC_INCLUDE_DIRS) -D STM32F10X_$(device_vol_upper) -D USE_STDPERIPH_DRIVER -o $(SYSTEM_OBJ) $(SYSTEM_SRC)
	@echo "Successfully built system"

$(STARTUP_OBJ): $(STARTUP_SRC)
	@echo "Building startup"
	-mkdir ./obj
	$(ARMCC) -c $(ARMCC_FLAGS) -g -Wa,--warn -o $(STARTUP_OBJ) $(STARTUP_SRC)
	@echo "Successfully built startup"

elf: $(ELF_FILE)

bin: $(BIN_FILE)

hex: $(HEX_FILE)

clean: 
	rm -f -R ./bin ./obj

用法

建立目录结构:

STM32_Project_Template
|-- lib
|   |-- devices
|       |-- md
|       |   |-- stm32f10x_conf.h
|       |   |-- stm32_flash.ld
|       |
|       |-- hd
|
|-- src
|   |-- lib
|   |   |-- led.h
|   |   |-- led.c
|   |   |-- init.h
|   |   |-- init.c
|   |   |-- or other files.c/.h
|   |
|   |-- main.c
|   |-- stm32f10x_it.c
|   |-- or other files.c/.h
|   
|-- Makefile

即建立一个工程文件夹,在该文件夹中放入Makefile文件,然后在该目录下建立src文件夹,存放用户代码,再建立lib文件夹,用来存放硬件相关配置。在src目录下可再建立文件夹。比如可建立lib文件夹用来存放自己写的库文件。Makefile会自动编译src目录下三级目录以内的所有源文件。(这点可以优化)

关于stm32f10x_conf.hstm32_flash.ld在哪里找,请参阅前文。

然后cd进入该工程目录,执行make即可生成烧录文件(hex文件)。make all等价于makemake elf只会建立elf文件,make hex会根据elf文件建立hex文件。

总结

在x86_64平台上生成另外一种处理器的代码,就是交叉编译。关于STM32系列代码的编译,在Windows操作系统,GUI界面的Keil,只要点击“编译”按钮,就会自己生成脚本,然后根据脚本编译,简单又方便,但也淡化了这一方面的知识,让人只停留在“会用”的层面,而不能很好地理解在底层到底发生了什么事情、执行了什么步骤。

而通过手动编译整个STM32工程,我们可以更加清晰地认识编译的原理、头文件与源文件的区别。通过亲手编写Makefile文件,将操作步骤“流水线”化,对我们handle大型工程的能力也是有帮助的。

现在是2019年8月。在各种SDK、API、库的帮助下。编写程序,已经是一件很简单的事了。要实现什么功能,调用库就好了。无可否认的是,这样做确实能提高工作效率,能将任务完成得又快又好。但是,这些库也是人开发出来的啊,如果现在人们只是热衷于表面的编写程序,而不去研究底层原理的话,说不定某一天,底层这个地基开始松动,不能支撑整栋大楼的时候,我们整个计算机世界,会不会也摇摇欲坠?

计算机的底层开发,应该永远是最重要的。

还令我担心的是,现在小学生的编程教育,无非只是非常游戏化的。他们所谓的“机器人大赛”,只是在屏幕上拖动方块,形成逻辑而已。可以说,这仅仅是一个“逻辑教育”,而不是“编程教育”。确实,编程需要逻辑思维,但是“逻辑”也仅仅是“编程”的一部分。要写出一个好程序,不仅仅需要逻辑,还需要一颗冷静的心、坚强的毅力、还要学会应对挫折。如果学生们从小就树立“编程只是把库函数凑在一起”的观念,即使他们长大后将库用得如何活灵活现,但是这也就使研究底层的人越来越少了。这对于整个计算机界,恐怕是一个末日。

所以,编程非儿戏,我们需要关注底层。这还不够,我们还应敬仰前人。我们应该感谢为我们提供各种库、各种工具、以及传授以我们知识的前人。STM32的标准外设库是谁开发的?C语言是谁发明的?Make又是谁创造的?你是如何学会这些知识的?唯有敬仰前人、敬仰知识,我们才能走得更远。

参考资料

【STM32开发环境】Linux下开发stm32(一) | 使用gcc-arm-none-eabi工具链编译
https://blog.csdn.net/Mculover666/article/details/84888539

【嵌入式】基于makefile的STM32编译方法探索
https://www.jianshu.com/p/7f77a5486a26

 teyann/stm32_flash.ld
https://gist.github.com/teyann/38a8766f0c905fccad61

stm32在linux下开发(一)
https://blog.csdn.net/yanchanchu9519/article/details/79485463

STM32 gcc编译环境搭建
http://www.zhongruitech.com/294956586.html

ST官网
https://www.st.com/

ARM官网
https://www.arm.com

巨人的肩膀

Linux之父:Linus Torvalds

发表评论