第3章 CHAPTER 3 STM32的开发工具 介绍及安装 为了开发STM32,需要准备代码开发环境和连接硬件用的调试下载工具。下面分别介绍这两类工具。 为了将编写的代码编译成可下载到芯片中的文件,需要利用开发环境对编写的代码进行编译。常见的开发环境有MDK、EWARM、STM32CubeIDE、GNU编译器套件(GNU Compiler Collection,GCC)等。 MDK全称为Microcontroller Development Kit,即微控制器开发套件。它源自Keil公司(后被ARM公司收购),现也称为MDKARM。MDKARM软件为基于CortexM、CortexR4、ARM7、ARM9等内核的处理器设备提供了一个完整的开发环境。 EWARM全称为Embedded Workbench for ARM,是IARSystems 公司为ARM 微处理器开发的一个集成开发环境。其包含项目管理器、编辑器、编译连接工具和支持RTOS的调试工具。在该环境下可以使用C/C++和汇编语言方便地开发嵌入式应用程序。 STM32CubeIDE由意法半导体公司开发,是面向STM32的一体化集成开发环境。该IDE基于Eclipse或GNU C/C++工具链等开源解决方案,包括编译报告功能和高级调试功能。它还额外集成了生态系统中其他工具才有的功能,如来自STM32CubeMX的硬件和软件初始化及代码生成功能。其通过多种增强功能(如数据变量实时观察和特殊寄存器视图)帮助快速调试应用程序,代码编辑、项目构建、板级烧录和调试均集成在一起,可实现无缝、快速的开发周期。 GNU编译器套件是由GNU开发的编程语言编译器。前面介绍的几种开发环境属于集成开发环境,它们把代码编辑界面(文本编辑器)、编译器和调试器整合到一个软件中,使得开发起来较为方便。而GCC只负责其中的一个环节,即编译器功能。因此,其不包含文本编辑界面,并且使用起来需要配合Makefile编译规则说明文件和命令行的各种指令。 为了将开发环境编译生成的文件下载到芯片中并进行调试,还需要调试下载工具将计算机和芯片进行连接。JLINK和STLINK是两种常用的用于调试和编程ARM微控制器的仿真器,它们都可以通过USB接口与计算机连接,实现对目标芯片的内存、寄存器、外设等的访问和控制。 图3.1JLINK JLINK是德国SEGGER公司推出的仿真器,如图3.1所示。它实际上是一个小型USB到JTAG/SWD的转换盒,其与计算机通过USB接口连接,采用的是JTAG协议或SWD协议。JLINK还支持广泛的CPU和体系结构,从8051到大众市场的CortexM,再到CortexA(32位和64位)等高端内核。JLINK支持直接连接SPI闪存,不需要在JLINK和SPI闪存之间使用CPU(通过SPI协议直接通信)。JLINK得到了多个主流集成开发环境的进一步支持,包括SEGGER Embedded Studio、Visual Studio Code、Keil MDK、IAR EWARM等。 STLINK是意法半导体公司为开发STM8/STM32系列MCU而设计的集在线仿真与下载功能于一体的开发工具,支持SWIM、JTAG、SWD共3种模式。最新的STLINK型号为V3,提供了一个虚拟COM端口,允许主机PC通过一个UART与目标微控制器通信,以及桥接接口(SPI、I2C、CAN、GPIO),允许通过引导加载程序对目标进行编程。其支持的开发环境有MDKARM、IAR EWARM、基于GCC的集成开发环境。 ARM Mbed DAPLink是一个开源软件项目,用于在ARM Cortex CPU上运行的应用软件上进行编程和调试。DAP全称为Debug Access Port,即调试访问端口。它是一种开源的调试与编程接口协议,可以被下载使用。该项目正在由ARM、其合作伙伴、众多硬件供应商以及全球开源社区进行持续开发。DAPLink已经取代了mbed CMSISDAP接口固件项目。 本书主要介绍MDK+DAPLink的开发方式,同时使用STM32CubeMX进行初始的工程项目配置和生成。 视频讲解 3.1生成工程模板——STM32CubeMX STM32CubeMX是一种图形化的配置工具,通过分步过程可以非常轻松地配置STM32微控制器和微处理器,以及为ARM CortexM内核或面向ARM CortexA内核的特定Linux设备树生成相应的初始化C代码。在STM32CubeMX诞生以前,需要手工一步一步地在MDK中引入工程文件,并且手工添加外设配置代码。在使用STM32CubeMX以后,我们只需要在软件中选择STM32的相应功能,即可让其自动生成属于MDK的配置好外设的工程模板。 3.1.1STM32CubeMX的安装 下面介绍该软件的安装过程。 (1) 由于STM32CubeMX的使用需要Java开发环境,因此需要先去Java的官方网站上获取Java开发环境的安装包。打开页面后选择下载Java,下载的文件名类似于jre8u401windowsx64.exe。 (2) 打开下载的Java安装包进行安装,如图3.2所示。 图3.2Java安装界面 (3) 从意法半导体公司官方网站上获取STM32CubeMX安装包(“首页”→“工具与软件”→“开发工具”→“STM32软件开发套件”→“STM32配置程序和代码生成器”→STM32CubeMX),如图3.3所示。在页面的下方选择属于不同系统的安装包进行下载(下载前需注册登录)。 图3.3STM32CubeMX下载页面 (4) 运行STM32CubeMX的安装包进行安装。注意: 安装目录不可出现中文。 (5) 安装完成后桌面会出现STM32CubeMX的图标。 3.1.2固件包的安装 不同的芯片需要使用不同的固件来进行配置,因此在配置STM32之前还需要在STM32CubeMX中下载相应的固件包,如图3.4所示。在该软件中,可以通过离线的方式进行固件包的安装,即按照2.3.1节介绍的方法一先从官方网站上下载对应的固件包,然后选择软件菜单栏的Help→Manage embedded software packages命令,在打开的窗口中单击From Local按钮。注意,需要先安装基础包(如1.2.0),再安装扩展包(如1.2.1)。这里推荐采用在线的方法自动进行安装,即2.3.1节介绍的方法二。 图3.4STM32CubeMX中的固件包安装界面 由于后续实验使用STM32U575,这里选择STM32U5的最新固件进行安装。安装完成后,执行Help→Manage embedded software packages命令,在打开的窗口中,对应的固件包版本前面的方框会变为绿色,如图3.5所示。 图3.5固件包安装完成 3.1.3配置并生成代码模板 (1) 打开STM32CubeMX,选择菜单栏的File→New Project命令。 (2) 在弹出窗口的左上角输入想要配置的芯片的型号,在MCUs/MPUs List中双击对应的型号即可打开配置窗口,如图3.6所示。 由于STM32U575支持TrustZone功能,因此会弹出如图3.7所示的对话框,单击OK按钮即可,默认不选择TrustZone功能。其他一些型号不会弹出此对话框。 图3.6芯片型号选择界面 图3.7选择是否启用TrustZone (3) 在配置窗口的Pinout & Configuration标签页中,左侧可以设置各种外设及功能的参数,右侧可以配置每个引脚的工作模式,右下角的放大镜处用来搜索引脚的编号,以便定位引脚,如图3.8所示。 图3.8Pinout & Configuration标签页 在Clock Configuration标签页中,可以配置各个总线上的时钟来源和频率。 在Project Manager标签页中,可以配置工程的相关设置,如目录、代码支持的IDE、固件包版本号、生成的文件列表等。 Tools标签页中是其他的一些工具,如PCC代表Power Consumption Calculator,可用于计算能耗; CAD允许用户快速访问和下载一个或多个设计工具链的原理图符号、PCB示意图和3D CAD模型。 (4) 设置好之后,单击GENERATE CODE按钮即可在对应的目录生成对应IDE使用的工程模板。详细的代码生成步骤及编译、调试、下载方法将会在实验中进行说明。 有关STM32CubeMX的详细使用方法,可单击菜单栏的Help进行了解。 视频讲解 3.2编辑编译工程——MDKARM、STM32CubeIDE 3.2.1MDKARM 下面介绍该软件的安装过程。 (1) 由于最新的MDK不支持Compiler Version 5编译器,而利用STM32CubeMX生成的工程需要使用此编译器,因此需要下载5.37版本以前的MDK。打开Keil官方网站的ARM Product Updates页面,在页面中找到5.36版本的下载链接并单击打开,如图3.9所示。 (2) 在打开的页面中,需要填写相关信息才能下载。 (3) 双击安装程序安装即可。注意,要安装在全英文目录下,同时设置好程序内核的安装位置和固件包的安装位置,如图3.10所示。 图3.9ARM Product Updates页面 图3.10安装程序目录配置界面 图3.11MDK图标 (4) 安装完成后,桌面上会出现软件图标,如图3.11所示。 (5) 打开软件后,需要先选择菜单栏的File→License Management命令进行认证后才能使用该软件的全部功能,否则编译文件时会受到大小等限制。请咨询官方获取注册码。 (6) 软件安装完成后,还需要安装针对特定芯片的固件包。该固件包和STM32CubeMX中安装的固件包不同,它是应用于MDK的,因此需要独立下载安装。打开ARM Keil官方网站,在页面上方找到CMSIS Packs并打开,如图3.12所示。 图3.12ARM Keil官方网站 (7) 由于本书主要以STM32U5为例进行讲解,因此这里按照图3.13进行搜索,然后单击结果中的STM32U5xx_DFP Keil。 图3.13MDK固件包搜索页面 (8) 在Version History中选择一个较老的版本,以便适应老版本的MDK,如图3.14所示。这里选择2.1.0版本,如图3.15所示。 图3.14固件包历史版本页面 图3.15STM32U5xx_DFP 2.1.0固件包下载页面 (9) 打开Keil软件的安装目录,在Keil_v5\UV4文件夹下找到PackInstaller.exe,右击该图标,选择以管理员身份运行。 (10) 由于默认启动后会检查更新,影响后续操作,因此需要先取消选中“启动后自动更新”选项,即取消选中Packs→Check For Updates on Launch,如图3.16所示。取消选中后再以管理员身份运行PackInstaller.exe。 (11) 在Pack Installer中,选择File→Import命令,如图3.17所示。然后在打开的窗口中选择下载的安装包Keil.STM32U5xx_DFP.2.1.0.pack进行安装。 图3.16启动后检查更新选项 图3.17Pack Installer的File菜单 注意,MDK版本要和Packs安装包版本对应。有些老版本的MDK可能会在打开新版本的固件包时产生错误,因此可以在版本历史中下载旧版本的固件包,如1.2.0版本。 (12) 安装成功后,选择相应的芯片,即可在右侧显示固件包已安装,如图3.18所示。 图3.18MDK的固件包安装状态 3.2.2STM32CubeIDE STM32CubeIDE是意法半导体公司官方提供的免费软件开发工具,也是STM32Cube生态系统的一员大将。它基于Eclipse/CDT框架、GCC编译工具链和GDB调试工具,支持添加第三方功能插件。同时,STM32CubeIDE还集成了部分STM32CubeMX和STM32CubeProgrammer的功能,是一个多合一的STM32开发工具,如图3.19所示。 图3.19STM32CubeIDE软件界面 用户只需要STM32CubeIDE这一个工具,就可以完成从芯片选型、项目配置、代码生成到代码编辑、编译、调试和烧录的所有工作。 在开发过程中,用户也可以非常方便地切换到内嵌的STM32CubeMX初始化窗口,添加或者修改之前的外设和中间件配置。不需要在多个工具之间进行切换。 STM32CubeIDE提供的编译和堆栈分析工具为用户提供了关于项目状态和内存使用的有用信息,还提供了很多高级的调试功能帮助用户进行高效调试。 与STM32CubeMX、STM32CubeProgrammer一样,STM32CubeIDE也是一个多平台的STM32开发工具,用户可以在Windows、Linux和macOS操作系统上通过STM32CubeIDE进行软件开发。 下面介绍该软件的安装过程。 (1) 按照3.1.1节的步骤安装Java环境。 (2) 打开意法半导体公司官方网站,依次进入“工具与软件”→“开发工具硬件”→“软件开发工具”→“STM32软件开发套件”→“STM32软件开发套件”→STM32CubeIDE,如图3.20所示。在页面下方选择对应系统的版本下载即可(下载前需注册登录)。 图3.20STM32CubeIDE下载页面 (3) 注意设置安装目录为全英文目录,如图3.21所示。 (4) 选择安装两个仿真器驱动,如图3.22所示。 图3.21STM32CubeIDE安装目录设置 图3.22STM32CubeIDE驱动设置 (5) 安装完成后在桌面上会显示对应的图标。 打开工程后,窗口各部分功能如图3.23所示。 图3.23STM32CubeIDE界面 工具栏如图3.24所示。 图3.24STM32CubeIDE的工具栏 使用图标创建新 C 源代码文件、头文件或新目标,例如,工程、库或存储集(执行主菜单中的File→New命令)。 使用图标编译工程。 使用图标启动调试,或者单击箭头对调试配置进行设置(此功能可通过主菜单中的Run选项启动)。 手电筒图标用于启动各种搜索工具,利用箭头可浏览最近访问的工程区域(对应菜单栏中的Search和Navigate)。 视频讲解 3.3调试下载工具 3.3.1STLINK STLINK硬件主要由两部分组成: STLINK主控板和连接线。STLINK主控板上有一个USB接口、一个20针JTAG/SWD连接口和一个LED指示灯。USB接口用于连接计算机,JTAG/SWD连接口用于连接芯片进行调试和编程。各版本STLINK如图3.25所示。 图3.25各版本STLINK STLINK V1是较老的版本,官方网站上显示已经停产。 STLINK V2是目前比较常见的版本,相比于V1有更高的数据传输速率。 STLINK V3是针对STM8和STM32的新一代模块化在线调试兼编程功能的工具。STLINKV3包含3个版本: STLINKV3SET、STLINKV3MINI、STLINKV3MODS。V3相较于V2有更高的数据传输速率,同时具备更高灵活性和扩展性,满足定制化需求。 为了在计算机上正常使用STLINK,需要为其安装驱动。驱动程序下载页面如图3.26所示。 图3.26STLINK驱动程序下载页面 图3.27设备管理器中的 STLINK设备 下载并解压后,如果系统是64位的,则右击dpinst_amd64.exe后选择以管理员身份运行即可安装。如果系统是32位的,则右击dpinst_x86.exe后选择以管理员身份运行即可安装。 驱动安装完成后,在计算机上插入STLINK,设备管理器中会显示相应的设备,如图3.27所示。 3.3.2DAPLink 图3.28是基于ARM Mbed DAPLink的开源项目开发的一款DAPLink调试下载器,可以看到其上部为USB接口,下部为10针的调试下载引脚。 DAPLink是免驱的,插上设备即可使用,如图3.29所示。 图3.28一款DAPLink 图3.29设备管理器中的DAPLink设备 本书的实验使用DAPLink进行程序的下载和调试,也可以使用STLINK,只需调整MDK中的工程选项即可,如图3.30所示。 图3.30MDK中对调试下载器的选择 3.4串行通信工具 为了在计算机和单片机之间进行串行通信,既需要在单片机侧连接串行通信芯片,也需要在计算机上安装对应芯片的驱动程序。 CH340是一款USB转串口的芯片。通过将CH340部署到开发板上,可以实现计算机与开发板的串行通信。因此,需要在计算机上安装CH340驱动。 打开WCH官方产品页面,在页面下方找到驱动程序,选择对应的系统下载即可,如图3.31所示。这里下载CH341SER.EXE,因为它与CH340驱动兼容。 运行下载的安装程序后,单击“安装”按钮。 安装成功后,当利用USB线连接含有CH340的开发板时,可以看到设备管理器里面能够识别到CH340,如图3.32所示。 图3.31CH340官方驱动下载页面 图3.32设备管理器中识别到的CH340 在开发过程中,可以在计算机上使用串口调试助手软件来与开发板进行串行通信。 3.5STM32硬件开发平台 为了对STM32进行编程并调用各种外设接口、传感器,需要使用特定的硬件开发平台进行学习。本书使用由华清远见研发的FSSTM32U5开发板。FSSTM32U5开发板由底板、核心板、显示屏、资源扩展板组成,如图3.33所示。该开发板可更换STM32F407、STM32F103等型号的核心板,以便学习其他型号的STM32。 图3.33FSSTM32U5开发板 视频讲解 3.6实验: 用STM32CubeMX和MDK创建工程项目并调试 本实验将使用在前面章节中安装好的开发环境进行工程的创建和调试,从而熟悉利用STM32CubeMX和MDK开发STM32的流程,并熟悉相关软件的使用方法。本实验的实验效果是: 在主函数中创建变量后,在主循环中为此变量加1,通过在Debug模式中插入断点并查看执行效果。 3.6.1配置STM32CubeMX工程 (1) 打开STM32CubeMX,选择菜单栏的File→New Project命令新建一个工程。在弹出的页面中搜索对应的型号。本实验使用STM32U575RIT6(如果使用其他型号,则搜索对应型号,然后继续后续步骤即可)。在MCUs/MPUs List中找到对应型号后双击打开,如图3.34所示。 图3.34选择STM32型号 图3.35TrustZone使能选择 (2) 由于本芯片支持TrustZone功能,所以会弹出如图3.35所示页面,选择默认的不使用TrustZone选项,单击OK按钮。 (3) 检查SYS 界面下是否有Debug设置,若有则选择Serial Wire模式,若没有该设置则可以不做设置,如图3.36所示。 这对应于STLINK在MDK中的调试模式,如图3.37所示。 图3.36Debug模式选择 图3.37MDK中的调试接口设置 (4) 由于不使用外设,所以直接进入Project Manager页面设置工程即可。在Project栏目中,需要设置工程名称、工程目录以及使用的Toolchain/IDE。注意,目录中必须为全英文。本实验使用MDKARM进行编译,选择默认的版本号即可,如图3.38所示。 图3.38工程设置页面 (5) 在Code Generator栏目中,选中Copy only the necessary library files单选按钮,即只复制必要的文件,以便节省工程占用空间,提高生成速度,同时选中Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral复选框,为外设分别生成.c和.h文件,如图3.39所示。 图3.39代码生成设置 (6) 单击GENERATE CODE按钮。由于STM32U5系列支持ICACHE(Instruction Cache)指令缓存技术,所以会弹出警告对话框提示没有使用ICACHE。如果不使用高速外设,单击Yes按钮即可,如图3.40所示。 图3.40ICACHE使能提示 (7) 在弹出的对话框中单击Open Project按钮即可在MDK中打开该工程,如图3.41所示。也可以打开工程所在的文件夹,再双击打开*.uvprojx文件。完成此步骤的前提是MDKARM安装成功并安装好了STM32U5的固件包。 图3.41打开工程选择 3.6.2使用MDK打开并编辑工程 (1) 在打开的工程中,左侧为引入工程的文件,如图3.42所示。startup_stm32****xx.s文件为单片机上电后执行的第一段程序,大致分为几个步骤: 初始化堆栈指针(SP)、初始化程序计数器(PC)、初始化中断服务程序(ISR)向量表、跳转到__main。system_stm32u5xx.c文件主要用于系统初始化、系统时钟配置。 (2) 如图3.43所示,单击Options for Target按钮,打开工程配置界面。 图3.42工程文件 图3.43Options for Target按钮所在位置 Device标签页可显示选择的芯片型号和固件包版本。 Target标签页用于设置代码的 ROM 与 RAM 的起始地址、编译器版本等,如图3.44所示。MicroLib是标准C库的裁剪版本,一般用于存储空间非常小的嵌入式应用。注意: 如果要编译的项目引用 C++函数,那么由于C++与MicroLib不兼容,所以此时不使用MicroLib。 图3.44Target标签页 Output标签页用于设置输出的文件名与保存的目录,如图3.45所示。选中Debug Information选项可以生成调试信息,取消选中此选项时,无法打断点调试。选中Create HEX File选项可生成单独烧写的Hex文件。选中Browse Infomation选项可以生成浏览信息,可以在Keil中索引函数或变量的定义、调用等,没有这个信息就无法直接定位函数所在位置; 取消选中该选项会大大加快文件的编译速度。当需要封装模块或打包静态库时,可以选中Create Library单选按钮,该选项与Create Executable互斥,会生成.lib文件而不是完整的.axf文件。该选项可用于提供复用的软件包使用,一般不勾选。选中Create Batch File选项即可在编译后生成.bat格式的编译执行脚本,利用此脚本,可在不打开Keil工程的情况下只执行编译执行脚本即可编译工程。 图3.45Output标签页 Listing标签页可以通过Select Folder for Listings按钮指定.lst文件的存放目录,避免在编译过程中生成的.lst临时文件杂乱无章,如图3.46所示。还可设置是否生成.map等文件。 图3.46Listing标签页 User标签页可以指定编译前和编译后执行的用户程序。选中Beep When Complete选项时在编译完成后扬声器会响一下以进行提示。 C/C++(AC6)标签页用于设置编译C语言时的预定义、编译选项、头文件目录、编译警告等,如图3.47所示。 图3.47C/C++(AC6)标签页 Define中的“USE_HAL_DRIVER,STM32U575xx”是预编译宏,表示使用HAL 库、STM32U575xx系列单片机,对应程序中的预编译判断。如果有其他的预编译宏,则可以用逗号分开。 Optimization可用于设置优化等级。当项目工程较大,想要节省芯片存储空间时,可以考虑提升优化等级。ST的芯片这里有0~3共4个等级可选。需要注意的是,优化等级越高,在调试模式中能加入的断点位置越少。因此,这里选择O0模式,不进行优化。 Include Paths用于指定头文件的搜索路径。 Misc Controls用于设置是否忽略警告。如果在 MDK中需要忽略某一个具体的警告,只需在 Misc Controls中添加 “diag_suppress=” 就可以了,num就是Keil中的警告代码。例如,在工程中需要忽略../Core/Src/main.c(88): warning: #177D: variable "i" was declared but never referenced 这个警告,只需添加 diag_suppress=177即可。 Asm标签页用于设置汇编编译时的选项。 Linker标签页用于配置连接时的选项,如图3.48所示。其中,Scatter File(分散加载文件)可以设置分散加载文件的位置。Scatter File描述内存的布局和分配,指定程序代码、数据和堆栈等的位置和大小,还可以指定在 Flash 存储器中的程序代码如何被分区和排列。当想在外部存储器中保存大量的数据时,可以利用分散加载文件将这些内容加载到外部存储中,避免内部存储不够用。 图3.48Linker标签页 Debug标签页用于设置软件模拟调试(左侧)和硬件调试(右侧),如图3.49所示。在硬件调试中,可以配置使用的调试器。 图3.49Debug标签页 Use: 选择调试器型号为STLink Debugger。 Load Application at Startup: 设置启动位置从启动文件开始加载。若不选中该选项,则在进入调试时,不会重新从初始启动位置开始执行,但需要手动添加.ini文件,把.axf的调试信息放到Keil里,否则进入调试时无法设置断点,也无法追踪到当前程序位置。 Initlalization File: 指定可以包含Debug Commands、Debug Functions、调试器配置和设备初始化命令的文件。 Run to main(): 进入Debug模式后运行到main函数后暂停。如果不选中该选项,则会在启动文件处暂停。 Restore Debug Session Settings: 恢复调试会话设置。选中该选项后可以使用上一次调试过程中设置的Breakpoints、Watch Windows、Memory Display、Toolbox等。 Driver DLL: 驱动动态库文件,Parameter是其参数。 Dialog DLL: 会话框动态库文件,Parameter是其参数。 Utilities标签页用于配置Flash烧写算法和配置如何处理生成的镜像文件。 (3) 双击打开main.c文件,找到int main(void)函数。代码中有很多USER CODE BEGIN和USER CODE END的标记。用户代码需要写在这些标记之内,才能在STM32CubeMX中更改配置后重新生成代码时不被覆盖。在USER CODE BEGIN 1处定义一个变量,然后在主循环中对这个变量+1,如下所示: int main(void) { /* USER CODE BEGIN 1 */ int i=0; /* USER CODE END 1 */ /* MCU Configuration---------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Configure the System Power */ SystemPower_Config(); /* USER CODE BEGIN WHILE */ while (1) { i++; /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } (4) 设置编译优化等级为O0,如图3.50所示。 (5) 修改完成后,单击左上角的Rebuild按钮,如图3.51所示。 图3.50设置编译优化等级 图3.51Rebuild按钮所在位置 (6) 在Build Output区域可以看到编译结果,如图3.52所示。在输出的信息中,Program Size后面的Code代表代码段,存放程序的代码部分; ROdata代表只读数据段,存放程序中定义的常量; RWdata代表读写数据段,存放初始化为非 0 值的全局变量; ZIdata代表0 数据段,存放未初始化的全局变量及初始化为 0 的变量。其后跟的数字代表所占空间大小。 图3.52编译信息 这些信息会在工程目录下的*.map文件中详细说明,如图3.53所示。在该文件中,列举了每个编译后生成的.o文件中不同数据所占的空间大小。 图3.53map文件内容 3.6.3连接开发板调试程序 图3.54开发板与DAPLINK的连接 (1) 将编译后的.hex文件烧写到芯片中。按照图3.54所示将开发板和计算机连接起来。其中,TypeC USB线用于给开发板供电; FSDAPLINK一端通过MiniUSB线连接到计算机,另一端通过排线和转接板连接到核心板上; 核心板采用STM32U575(如果在创建工程时选择了其他型号的STM32,则可以选择对应的核心板)。 如果有STLINK,也可以将STLINK通过此种方式连接到开发板上。注意,要提前安装STLINK驱动并在设备管理器中正确识别到。 (2) 连接好开发板和计算机并保证开发板供电后,在MDK中单击Options for Target按钮打开Debug标签页,依据使用的调试器选择CMSISDAP调试器或者STLINK调试器,如图3.55所示。 图3.55调试器选择 (3) 单击Settings按钮,在打开的页面中可以看到SW Device被正确识别。同时在Flash Download标签页里有相关的Flash烧写算法(若没有,则需要单击Add按钮添加),如图3.56所示。 图3.56Flash烧写算法设置页面 使用DAPLINK时显示信息如图3.57所示。 图3.57使用DAPLINK时的识别信息 使用STLINK时显示信息如图3.58所示。 图3.58使用STLINK时的识别信息 (4) 如图3.59所示,单击Debug按钮,弹出如图3.60所示的界面。 图3.59Debug按钮所在位置 (5) 在如图3.60所示中,左侧为寄存器列表,显示当前各个寄存器的值。右上侧的Disassembly为当前要执行的汇编指令。中间的代码界面由箭头指示下面要执行的代码。在i++这个语句左侧深灰色位置单击,插入一个断点(如果无法插入断点,说明优化级别太高,需要调整优化级别为O0,重新单击Rebuild按钮编译)。右下侧的Call Stack为相关函数、变量的地址、值和类型。 图3.60Debug界面 在如图3.61所示的工具栏中,几个按钮分别为Reset(让程序复位到起始位置)、Run(正常运行,但会在断点处暂停)、Stop(在当前运行的位置处暂停)、Step(单步执行,遇到函数时会跳进函数内)、Step Over(执行一行代码,不会跳进函数内)、Step Out(执行完毕当前函数后返回到上一层函数)、Run to Cursor Line(运行到光标所在行)、Show Next Statement(跳转到程序暂停所在行)。 图3.61Debug模式下的工具栏按钮 (6) 单击两次Step按钮,让程序执行完i=0。可以看到此时箭头位于HAL_Init()函数前,说明即将执行该函数,同时右下角的变量i显示为0,如图3.62所示。 图3.62单击两次Step按钮后程序所在位置 (7) 继续单击Step按钮,程序进入HAL_Init()函数内,如图3.63所示。 图3.63程序进入HAL_Init()函数 (8) 单击Step Out按钮,让程序执行完本函数,并返回上一层函数。此时可以观察到箭头位于即将执行的SystemClock_Config()函数前,如图3.64所示。 图3.64即将执行SystemClock_Config()函数 (9) 单击Step Over按钮,即可执行完毕当前的行所代表的内容,光标移动到下一条代码上。 (10) 单击Run按钮全速运行,会发现程序会在断点处暂停,如图3.65所示。因为还没有执行i++,所以此时变量i的值仍然为0。 图3.65程序在断点处暂停 (11) 继续单击Run按钮,让其在主循环中运行一圈后再次在断点处暂停,会发现变量i的值变为1,如图3.66所示。 图3.66变量i的值变为1 (12) 单击Reset按钮,程序会复位到代码运行的初始位置,如图3.67所示。 图3.67程序复位到代码运行的初始位置 视频讲解 3.7main()函数之前的启动流程 在调试过程中,我们会发现程序从main()函数中开始执行。实际上,在运行main()函数之前,芯片会依据一些程序进行系统的初始化,然后才会调到main()函数处执行。下面解释一下这个过程。 一般来说,STM32处理器复位以后的工作步骤如图3.68所示(注意,此处说明的是典型情况,具体情况需要依据不同架构而定,特别是地址映射和BOOT引脚的配置部分)。 软复位是指通过编程方式在程序中触发的复位方式,如看门狗、寄存器写入等。硬复位是通过物理方式触发的复位方式,通常是通过将复位引脚(例如,NRST)拉低来实现的。无论是软复位还是硬复位,当复位发生时,STM32都会通过BOOT0和BOOT1两个引脚(STM32U5系列只有一个BOOT0引脚)的电平来判断启动程序加载到了哪里,即0x00000000这个地址实际代表哪个地址。 BOOT0=0,BOOT1=X: 从主Flash内存启动,实际地址为0x08000000。这是最常见的配置,用于正常的应用程序启动。 BOOT0=1,BOOT1=1: 从SRAM启动,实际地址为0x20000000这需要在链接时由分散加载文件*.sct分配程序到SRAM。 BOOT0=1,BOOT1=0: 从系统存储器启动,实际地址为0x1FFFF000,这是一段特殊的空间,通常包含出厂时设置的代码,为ISP(In System Program)提供支持。它允许用户通过外部接口(如串行接口)来加载新的固件或进行其他调试和维护操作。 将程序加载到Flash或SRAM时,会执行启动文件startup_stm32****xx.s(通过分散加载文件设置启动文件位于哪里)。在该文件中,首先会初始化堆栈空间,并将栈顶地址分配到最开始的4字节空间0x00000000~0x00000003(如果程序烧进了Flash,则映射到0x08000000~0x08000003)。然后分配每个中断向量的地址。第一个中断向量是Reset_Handler,也就是复位后要执行的程序,它被分配到栈顶地址后面的位置,即0x00000004。在 Reset_Handler 中,会跳到 C 语言编写的初始化代码 SystemInit 中,然后再通过 __main 跳转到另一部分初始化代码,最终进入用户的main()函数。 下面再用如图3.69所示的存储结构图来解释这个过程。该存储结构图为简化版,实际情况需要查看芯片手册的说明。 图3.68STM32处理器复位以后的工作步骤 图3.69STM32的存储结构图 当BOOT0=0,BOOT1=X时,0x00000000映射到0x08000000,于是系统会到此地址寻找栈顶地址是多少。根据分配的栈空间和堆空间大小,系统可能将0x200008D8作为栈顶地址,向下生长0x400字节的地址,紧接着是0x200字节大小的堆空间。所以0x08000000处存放的是栈顶地址0x200008D8。 系统取出栈顶地址后,紧接着在0x08000004处取出程序地址。程序地址位于0x80002E1,于是系统跳转到此处运行程序。 在调试模式中,可以在Memory窗口搜索到对应地址的数据,如图3.70所示。 图3.70调试模式中的Memory窗口 启动代码*.s文件的作用 startup_stm32****xx.s文件中的代码执行于main()函数之前,主要用于设置初始堆栈指针(SP)、设置初始程序计数器(PC)等于Reset_Handler、在向量表中为每个异常/中断设置相应的服务程序地址、跳转到C库中的__main(最终调用main())。下面看一下每部分的实现方式。 在*.s文件中,首先设置了堆栈的空间大小,并开辟了一段连续的空间,分配了堆栈的地址。EQU伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言的#define。AREA命令指示汇编程序汇编一个新的代码段或数据段,它指示汇编器在内存中为该区域分配空间,并可以指定该区域的属性,例如,代码区、数据区、只读区等。 ; Amount of memory (in bytes) allocated for Stack ; Tailor this value to your application needs ; Stack Configuration ; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> ; Stack_SizeEQU0x400;定义栈空间大小为0x400(1K) AREASTACK, NOINIT, READWRITE, ALIGN=3 ;开辟一段可读可写的数据空间,段名为STACK,按照8字节对齐 Stack_Mem SPACEStack_Size;分配Stack_Size大小的连续内存空间 __initial_sp;栈顶地址 ; Heap Configuration ; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; Heap_SizeEQU 0x200;设置堆的大小 AREAHEAP, NOINIT, READWRITE, ALIGN=3 __heap_base ;表示堆的开始地址 Heap_MemSPACE Heap_Size ;分配Heap_Size大小的连续内存空间 __heap_limit;表示堆的结束地址 使用 PRESERVE8 确保了生成的可执行文件中的数据按照 8 字节边界对齐,这符合 ARM CortexM 等处理器的要求,并有助于优化程序的性能和稳定性。使用 THUMB 指令告诉编译器和链接器生成 Thumb 模式下的代码。Thumb 模式是 ARM 架构中的一种指令集,它通过使用更紧凑的指令编码来提高代码密度,并且通常可以提高代码执行效率。Thumb 模式下的指令通常是 16 位宽度,而 ARM 模式下的指令是 32 位宽度。在 STM32 中,通常使用 Thumb 模式来编写启动代码和处理中断服务程序等关键部分,以节省内存空间并提高执行效率。因此,在启动文件中使用 THUMB 指令可以确保生成的代码在 Thumb 模式下运行。需要注意的是,可以通过特定的切换指令在Thumb 模式和ARM模式之间进行转换,因此即使使用 Thumb 模式编写了启动文件,系统也可以在需要时从Thumb模式切换到ARM模式,反之亦然。 PRESERVE8;指定当前文件所占空间按照8字节对齐 THUMB ;表示后面的指令兼容THUMB指令集 然后定义了一个中断服务函数向量表。中断向量表构建了中断源的识别标志,可用来形成相应的中断服务程序的入口地址或存放中断服务程序的首地址。EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。DCD是Data Constant Doubleword的缩写,该指令用于定义一个或多个双字(32位)的数据,并将这些数据初始化到内存中的指定位置。下列代码用DCD为每个中断服务程序分配了4字节的空间,该空间存放对应程序的入口地址。 ; Vector Table Mapped to Address 0 at Reset AREARESET, DATA, READONLY;定义一块数据段,只读,名为RESET EXPORT__Vectors;连续空间的开始地址 EXPORT__Vectors_End ;连续空间的结束地址 EXPORT__Vectors_Size;连续空间的大小 __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler; Reset Handler DCD NMI_Handler; NMI Handler DCD HardFault_Handler; Hard Fault Handler DCD MemManage_Handler; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD SecureFault_Handler; Secure Fault Handler DCD 0; Reserved DCD 0; Reserved DCD 0; Reserved DCD SVC_Handler; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler; SysTick Handler ; External Interrupts DCD WWDG_IRQHandler; Window WatchDog DCD PVD_PVM_IRQHandler; PVD/PVM through EXTI Line detection Interrupt DCD RTC_IRQHandler ; RTC non-secure interrupt DCD RTC_S_IRQHandler ; RTC secure interrupt DCD TAMP_IRQHandler; Tamper non-secure interrupt … 然后定义了一段只读的代码区域。 AREA|.text|, CODE, READONLY ; Reset Handler Reset_HandlerPROC EXPORTReset_Handler [WEAK] IMPORTSystemInit IMPORT__main LDR R0, =SystemInit BLX R0 LDR R0, =__main BXR0 ENDP ; Dummy Exception Handlers (infinite loops which can be modified) NMI_Handler\ PROC EXPORTNMI_Handler [WEAK] B . ENDP HardFault_Handler\ PROC EXPORTHardFault_Handler [WEAK] B . ENDP MemManage_Handler\ PROC EXPORTMemManage_Handler [WEAK] B . ENDP BusFault_Handler\ PROC EXPORTBusFault_Handler[WEAK] B . ENDP … 在这段代码中,Reset_Handler过程是处理处理器复位的代码。在ARM CortexM处理器中,复位后会跳转到该处理器的复位向量处执行。首先,用EXPORT声明了Reset_Handler过程具有全局属性,以便其他文件可以引用它。其次,导入了SystemInit和__main,分别表示系统初始化函数和主函数。通过“LDR R0,=SystemInit”命令将SystemInit函数的地址加载到寄存器R0中,通过BLX R0调用SystemInit函数。再次,通过“LDR R0,=__main”命令将__main函数的地址加载到寄存器R0中。最后,通过BX R0跳转到__main函数执行主程序。 NMI_Handler、HardFault_Handler等过程是处理异常情况的代码,例如,NMI中断、硬件错误等。每个过程都是一个无限循环,其中只包含了一个无条件分支到当前指令地址的指令“B.”。这相当于一个空循环,可以在需要时修改成相应的处理代码。 总的来说,这段代码的作用是在处理器复位时执行系统初始化,并跳转到主程序,同时定义了一些异常处理函数,这些函数目前只是简单的无限循环,可以在实际应用中根据需要进行修改。 最后根据是否使用微型库MicroLib来初始化用户堆栈和堆。如果使用微型库,则声明相关符号以便链接器使用; 如果未使用微型库,则定义初始化堆栈和堆的过程,并声明相应的符号。 ;*********************************************************** ; User Stack and Heap initialization ;*********************************************************** IF:DEF:__MICROLIB EXPORT__initial_sp EXPORT__heap_base EXPORT__heap_limit ELSE IMPORT__use_two_region_memory EXPORT__user_initial_stackheap __user_initial_stackheap PROC LDR R0, =Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem +Heap_Size) LDR R3, = Stack_Mem BXLR ENDP ALIGN ENDIF END