Linux下STM32的开发环境配置和烧录指令

STM32 Linux

This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.

最近做海雾的预报比较无聊,总得找个东西打发时间,于是翻出了大概两年前嫖的别人的板子。当时他们的创业园办公室要被收回了,于是我就去捡垃圾,看看有什么好东西。然后就搞到了这块STM32F103VET6野火指南者板子,上面还有一块屏幕,挺香的。

翻了一下野火官方的教学视频,发现是用的Keil开发的,但是我的Arch没法运行Keil,所以又花了两天时间弄清楚如何在Arch上开发,编译,烧录STM32的程序。

安装软件

  • arm-none-eabi-gcc:编译器
  • cmake:编译程序
  • VS Code:写代码
  • OpenOCD:烧录程序
  • stm32cubemx:生成板子的启动脚本、链接脚本,初始化项目,提供HAL库文件

以上软件除了stm32cubemx需要从AUR安装,其他的都可以直接用pacman安装。(VS Codearch4edu中可以找到)

设置udev

使用USB将板子连接到电脑上以后,在/dev下并不会出现对应的设备符号,我们需要手动设置udev规则。

首先运行以下命令让udev监控usb的更改

1
udevadm monitor

然后插上调试器的USB线,这个时候会出现许多日志

image-20231108112341246

然后随便复制一个比较长的路径,例如/devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11.1/1-11.1:1.1,然后运行下面的命令查看对应设备的信息,udev会一并把父设备也列出来。

1
sudo udevadm info -a --path=/sys/devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11.1/1-11.1:1.1

/devices/xxxx路径添加到/sys后面

在输出的信息中寻找到含有"KeilSoftware"字样对应的设备,这个设备就是调试器。记录下设备的路径/devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11.1

image-20231108113034111

重新运行命令查看该设备的信息,并记录下KERNELSUBSYSTEMATTR{idProduct}ATTR{idVendor}

1
sudo udevadm info -a --path=/sys/devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11.1

然后我们就可以编写udev的规则了。

1
sudo vim /etc/udev/rules.d/stm32f103vet6.rules
1
KERNEL=="1-11.1",SUBSYSTEMS=="usb",ATTRS{idVendor}=="c251",ATTRS{idProduct}=="f001",MODE:="0777",SYMLINK+="STM32F103VET6"

其中SUBSYSTEMS对应SUBSYSTEM的值,ATTRS{idVendor}对应ATTR{idVendor}的值,ATTRS{idProduct}对应ATTR{idProduct}的值。MODE是设备的权限,设为0777则普通用户即可访问。SYMLINK是出现在/dev中的设备的名称,这里我设置为STM32F103VET6,方便识别。

将规则保存,然后拔出调试器的USB线重新插入,这个时候就应该可以在/dev下找到对应的设备了。

image-20231108113913588

初始化项目

首先我们需要使用stm32cubemx这个程序初始化项目,它给给我们准备所需要的HAL库文件,启动文件,链接文件,CMSIS-DAP头文件等。

打开stm32cudemx首先你需要登陆一下,否则是无法下载文件的

然后点击新建项目。首先输入自己板子的型号,然后双击选择对应的板子

image-20231108115041323

设置时钟源

我用的这个板子推荐频率是72MHz。为了能够之后不再设置,所以预先在stm32cubemx里面设置一下,后面就不用手动再设置时钟了。

点击System Core -> RCCHigh Speed Clock (HSE)选择Crystal/Ceramic Resonator,然后在Clock Configuration页面设置时钟源、分频和倍频。

image-20231113163343237

设置GPIO引脚

如果使用按键、LED灯的话,是需要初始化对应的GPIO引脚的。可以选择在stm32cubemx中一并预先设置好,或者生成代码以后自己再设置,反正都差不多。这里不再多说。

项目设置

然后在Project Manager里面设置项目的名称之类的。Toolchain / IDE可以选Makefile,后面可以根据Makefile编写CMake的规则。

image-20231108115401755

Code Generator这里可以选仅复制必要的库文件,这样库文件量会少很多。

image-20231108120317877

其他的默认就可以,然后点击GENERATE CODE生成代码。生成完了以后就可以把stm32cubemx关掉了。

调整项目结构

打开项目所在目录,默认的目录结构我不太喜欢,所以自己调整了一下。

默认的文件结构如下

image-20231108120452911

更改后的结构(其实改得也不多)

image-20231108120824153

编写CMakeLists.txt

因为我想用cmake来帮助编译,所以需要编写一个CMakeLists.txt文件。这里我就直接贴内容了。只需要将46和53行那里设置源文件和头文件的路径,以及39行设置宏定义来选择板子型号,15行MCU型号的设置修改一下应该就可以用于其他型号的板子了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
cmake_minimum_required(VERSION 3.20)

# tell cmake not to compile executable when test project, because it will fail in cross-compiling
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")

project(STM32LED)

# set compile tool chains
# compiler
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
# output bin file
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
# get bin size
set(CMAKE_SIZE arm-none-eabi-size)
# MCU setting
set(MCU_FLAGS "-mcpu=cortex-m3 -mthumb")

# debug or release?
if (CMAKE_BUILD_TYPE MATCHES Debug)
set(DBG_FLAGS "-g -gdwarf-2 -Og")
elseif(CMAKE_BUILD_TYPE MATCHES Release)
set(DBG_FLAGS "-O2")
endif()

# gcc flags
set(CMAKE_C_FLAGS "${MCU_FLAGS} -Wall -fdata-sections -ffunction-sections ${DBG_FLAGS}")
# asm flags
set(CMAKE_ASM_FLAGS "${MCU_FLAGS} -x assembler-with-cpp ${DBG_FLAGS}")
# link scripts
set(LINKER_SCRIPT ${PROJECT_SOURCE_DIR}/Drivers/linkscript/STM32F103VETx_FLASH.ld)
# set link options
set(CMAKE_EXE_LINKER_FLAGS "${MCU_FLAGS} -T${LINKER_SCRIPT} -specs=nosys.specs -lc -lm -lnosys \
-Wl,-Map=${PROJECT_NAME}.map,--cref -Wl,--gc-sections")

# print current source directory
message("cmake current source directory: ${CMAKE_CURRENT_SOURCE_DIR}")

# macro definition, select device type
# in some project, use STM32F103xE depending on the headers
add_definitions(-DUSE_HAL_DRIVER -DSTM32F103xE)
# add_definitions(-DUSE_HAL_DRIVER -DSTM32F10X_HD)

# enable asm
enable_language(ASM)

# find source file
FILE(GLOB_RECURSE SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.c)
FILE(GLOB_RECURSE HAL_LIST ${PROJECT_SOURCE_DIR}/Drivers/STM32F1xx_HAL_Driver/Src/*.c)

set(MAIN_SRC ${PROJECT_SOURCE_DIR}/main.c)
set(STARTUP_SRC ${PROJECT_SOURCE_DIR}/Drivers/startup/startup_stm32f103xe.s)

# set include directory
include_directories(${PROJECT_SOURCE_DIR}/src/include ${PROJECT_SOURCE_DIR}/Drivers/STM32F1xx_HAL_Driver/Inc
${PROJECT_SOURCE_DIR}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy
${PROJECT_SOURCE_DIR}/Drivers/CMSIS/Include
${PROJECT_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32F1xx/Include)

# generate elf file
add_executable(${PROJECT_NAME}.elf ${STARTUP_SRC} ${SRC_LIST} ${HAL_LIST} ${MAIN_SRC})

target_link_libraries(${PROJECT_NAME}.elf nosys c m)

# generate hex and bin file
add_custom_command(TARGET "${PROJECT_NAME}.elf" POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex "${PROJECT_NAME}.elf" "$${PROJECT_NAME}.hex"
COMMAND ${CMAKE_OBJCOPY} "${PROJECT_NAME}.elf" "${PROJECT_NAME}.bin"
)

烧录

中间编写代码的过程我就不做介绍了。假设你写好了一个流水灯的程序,并且已经编译得到了elf文件,那么下一步我们需要将其烧录进单片机中。我们使用OpenOCD进行烧录,命令如下

1
openocd -f /usr/share/openocd/scripts/interface/cmsis-dap.cfg -c "transport select swd" -f Drivers/openocdcfg/stm32f1x.cfg -c "init" -c "reset halt" -c "sleep 100" -c "wait_halt 2" -c "flash write_image erase build/STM32LED.elf" -c "sleep 100" -c "reset run" -c shutdown

其中

  • -f /usr/share/openocd/scripts/interface/cmsis-dap.cfg:由于我用的调试器使用CMSIS-DAP标准,所以配置文件选择cmsis-dap.cfg
  • -c "transport select swd":使用SWD模式进行数据传输
  • -f Drivers/openocdcfg/stm32f1x.cfg:使用stm32f1x类型芯片的配置文件。此文件是我从/usr/share/openocd/scripts/target下复制过来的
  • -c "init" :初始化
  • -c "reset halt":执行硬件复位,将所有已定义的目标重置,并立即停止目标的运行
  • -c "sleep 100"OpenOCD在状态恢复之前等待至少100毫秒
  • -c "wait_halt 2"OpenOCD等待目标停止的时间为2毫秒
  • -c "flash write_image erase build/STM32LED.elf":擦出flash扇区并将build/STM32LED.elf文件烧录到其中
  • -c "reset run":执行硬件复位,将所有已定义的目标重置,并开始目标的运行
  • -c shutdown:关闭OpenOCD

可以将以上的命令写进一个shell脚本中,方便后续重复运行。然后我发现烧录的时候,烧录bin文件是有问题的,烧录不进去,但是烧录elf文件就是正常的,这个问题比较奇怪,还没有找到原因。

Author: Syize

Permalink: https://blog.syize.cn/2023/11/08/linux-stm32-development/

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Syizeのblog

Comments