完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛|
一、前言
嵌入式开发,必然离不开各自开发平台下的工具链,通常情况下,工具链都被封装为一个完整的IDE,供开发者使用。深度封装的IDE可以让开发者快速上手,避免新手一开始就接触复杂繁琐的底层原理,从而专注于代码本身。实际开发过程中,IDE也确实能胜任开发过程中大部分需求,起到它应用的作用。 在某些特殊场景下,一旦需要自定义开发流程,或对工程进行深入改动。就要求开发者了解IDE的底层原理及代码编译流程。本文是作者探索开发工具链过程中的总结,实际探索过程进行了多个平台和芯片的尝试,在本文中,以流行的STM32平台为例进行记录,文中提及的工具技术,每一个都值得单独开篇详述,限于篇幅,无法在本文中深入研究,以后有机会再探索并单独撰文记录。本文先对编译以及调试流程作了简介,帮助读者先构建基本的知识框架,以便理解后面搭建IDE过程中的每个步骤及其目的,如果读者已对编译及调试流程很熟悉,可以直接跳到搭建IDE部分,参考本文搭建自己的IDE。作者水平有限,如有错漏,烦请指正。 二、编译器组成与编译流程 2.1编译流程概述 C语言是一门高级语言,其高级语言的描述其实是相对于汇编语言以及机器语言来的。目标代码必须在特定平台上执行才有意义,不同平台架构及指令集的不同注定了在C语言下同样的动作,最终必须转换为目标平台CPU所能识别的特定二进制序列才能正常运行,即:“可执行文件”。从编写完成的C代码到最终的可执行文件所经历的过程,即是被我们泛化的概念:“编译”。而实际上,“编译”仅是转换过程中的一部分。 真实的编译过程,包含了预编译->编译->汇编->链接四个部分,每个部分执行后,均有对应的输出文件,这些中间文件在编译过程中是必需,但最终不被目标平台所使用,因此,通常会在生成可执行文件后被清除。这四个部分所使用的工具,被打包为一个程序集,根据不同的输入参数,执行不同的命令以完成编译过程的各个阶段任务,该程序集,即:“编译器”,实际上,现在的编译器,不仅包含了编译过程本身的工具,还包含着调试以及镜像转换等一系列工具集。针对ARM平台,常见的编译器有ARMCC(Keil)、IAR(IAR)、GCC For ARM(除前两者外的IDE使用)、LLVM(暂未大规模使用)。本文的主角是GCC For ARM,目前主要由ARM在维护及更新,基于GNU开源工具链,主要用于ARM平台的交叉编译。 2.2 Gcc For Arm编译器 在此给出Gcc For Arm的官网下载链接: gcc-arm-none-eabi-10-2020-q4-major-win32.zip 以上编译器压缩包解压后,进入其根目录,将看到以下内容: 上图展示了ARM提供的GCC For Arm工具链中的所有工具,其中标红的是在嵌入式开发过程中常用的几个,下面对几个常用工具逐一讲解,在讲解之前,请对整个编译过程有大概了解: 如上图所示,C代码编译的过程及每个阶段都将产生对应的中间文件,中间文件的格式根据目标平台的不同和编译器的不同会有所区别,但在其所属阶段产生的文件内容,实际上并无本质差异。 有了以上宏观了解后,可以逐个将编译器的工具与编译流程对应: 1.arm-none-eabi-gcc.exe 该文件是编译器的主体,其作用范围涵盖编译流程中的预编译、编译、汇编、链接四个阶段,后文将逐一对以上过程展开讲解。 2.arm-none-eabi-ld.exe 该文件是链接器主体,其作用是将编译器汇编后输出的一系列*.o目标文件合并以及重定位,最终生成目标平台的可执行文件(*.elf)。 3.arm-none-eabi-gdb.exe 该文件是gdb调试服务的可执行文件,其作用是加载由链接器产生的可执行文件(*.elf)至目标芯片的RAM或者Flash,并具有运行、调试、增删断点、读取变量、修改变量等常见调试功能。其作用范围,覆盖嵌入式开发的调试阶段。 4.arm-none-eabi-objcopy.exe 该文件是镜像拷贝程序的主体,由于链接阶段所产生的*.elf目标文件,不止包含了程序的运行地址和数据,还包含了大量信息供调试使用,而程序烧录时,仅需*.flash的地址及对应地址的数据,因此需要将这些信息,从*.elf中提取出来,转换为常见的Flash镜像格式,如:*.hex、*.S19、*.Bin。可以简单理解为:*.elf文件调试时使用,*.hex烧录时使用,关于HEX文件的格式,可以参考作者之前的文章:深入理解工具链-Hex文件详解。 5.arm-none-eabi-objdump.exe 该文件是反汇编程序的主体,某些特殊情况下,C代码层面的逐行调试并不能满足我们的调试需求,需要进一步知道其汇编语言层面的动作。此时,我们可以使用反汇编程序,将目标平台的二进制机器码转换为汇编代码,以便调试过程中确定问题,反汇编后的格式,不同平台稍有区别,通常为*.lst格式,也有*.lss和直接使用*.txt格式的,但其大都为文本文件,均可使用记事本直接打开。 6.arm-none-eabi-size.exe 该文件主要用于打印当前程序的存储占用情况,如:.data段、.bss段、*.text段大小。通过调用该工具,开发者可以准确知道程序占用的硬件存储资源分布情况,通常,.data段、.bss段大小之和即为所占RAM空间大小;.data段、.text段大小之和为所占Flash空间的大小。IDE编译完成后所打印的大小信息,本质上是调用该工具实现。 2.3预编译 有别于初学C语言时所有C代码均在一个*.C文件中编写,大型工程为了便于分层与模块解耦,将不同模块的代码封装在不同的*.C文件中。为了让其他模块或上层代码使用本模块中的宏定义、全局变量或函数,常规做法是将模块宏定义以及变量及函数声明放在对应的*.H文件中,*.H文件在被使用的模块中通过#include预编译指令包含。因此,预编译阶段主要的工作,即:将*.H文件的内容,在每一个包含了该*.H文件的*.C文件中按照预编译指令展开。除了#include的展开操作,还有其他的一些预编译指令,但本质上,预编译阶段的内容,都在文本编辑层面,大部分操作,均为复制、粘贴、替换等。经过预编译阶段后,编译器所产生的中间文件格式为*.i。 假设,当前工程下,包含main.c 、main.h、clock.c、clock.h共4个文件,那么,经过预编译阶段,将输出main.i、clock.i这2个文件。 以上过程,由编译器执行预编译命令完成: 执行上述命令后,在源文件目录下,将生成对应的.i文件: 每个*.i的内容 = *.c + *.h 在上述命令中,arm-none-eabi-gcc为编译指令,编译指令后,紧跟指令参数,“-E”表示仅执行预编译动作; “-o”代表执行结果输出到文件 “-o”后紧跟输出文件名。 为了在任意工作目录都能使用该命令,需要提前将该命令所在路径加入环境变量“PATH”中,对于Windows平台,其加入方法如下:
下面是界面示意,后文中用到的工具,若需要加入环境变量“Path”的,其目的和添加方法都一样,后文中将不再重复讲解添加路径操作。 2.4编译 经过预编译阶段后,每一个*.C均已包含了本文件的代码逻辑以及其他文件的函数及变量声明。此时,每个*.i经过编译器继续执行编译动作。编译这一过程,本质上是将C代码这一高级语言转换为汇编代码,和预编译过程一样,其执行后,每个*.i都将生成对应的*.s文件。 编译命令如下所示: 其命令格式与预编译类似,输入参数由“-E”变为了“-S”,该参数表示编译器仅执行编译操作,不进行汇编与链接。执行该命令后,输出目录下将出现*.s的汇编代码文件,其内容与处理器架构直接关联,不同架构处理器具有不同汇编指令,示例生成的main.s内容如下: 2.5汇编 经过前面部分的讲解,我们可以合理的猜测,汇编阶段即是将汇编代码转换为目标平台的机器码。和我们猜测的一样,汇编阶段所作的操作,就是为每个*.s文件执行汇编,将其翻译为机器码。但有个需要注意的地方是,此处的*.s文件,不仅包含了每个*.c逐步骤生成的*.s,还包含了该芯片的启动代码:startup.s。启动代码一般是由芯片厂商编写,在执行main函数前执行的初始化代码。其中包括了诸如:堆栈空间分配、初始化中断向量表、RAM初始化、看门狗初始化、时钟初始化、以及跳转main函数等操作,每个处理器在启动代码部分的操作有细微差异,但其目的,都是为了初始化硬件,为main函数运行提供合适的运行环境。其中,堆栈分配自然不必多说,对于嵌入式代码,通常堆空间可以不分配,但栈空间的分配是必须的;RAM初始化主要对全局变量进行重定向及初始化,对于C代码来说,有初始值的全局变量将被分配到.data段,无初始值以及初始值为0的全局变量将被分配到.bss段,因此,初始化代码需要将有初值的全局变量其初值从Flash复制到RAM,将无初值以及初值为0的全局变量赋值为0,其中,初始化.data段这部分即被称为“重定向”。关于启动代码,内容与硬件直接相关,其实可以单独撰文详述,有时间作者将在别的文章中详细讲解。 回到主题:汇编阶段,会为需要汇编的每个*.s文件,都生成对应的*.o文件,不同编译器和目标平台所生成的文件后缀可能不一致,例如:IAR平台下,MP430单片机在这部分扩展名为*.r43,AVR单片机的扩展名为*.r82。但其文件内容,都是相似的,包含了C代码中所有的默认段(*.data、*.bss、*.common、*.text)和用户自定义段。 汇编命令如下所示: -c参数表示编译器仅执行编译与汇编动作,不进行链接。 以上示例不仅汇编了main.s以及clock.s,还汇编了STM32F407单片机的启动代码,此时,输出的*.o文件不再是文本文件,我们已无法使用记事本打开查看,但每个*.o文件,都包含了代码执行所必须的段以及符号表。所谓符号表,即是C代码中全局变量的变量名,函数名,以及汇编代码中的程序段标号,它们将在链接阶段,被分别合并到最终可执行文件对应的段以及符号表中。这部分内容,读者有兴趣可以参考神作:《深入理解计算机系统》第七章,链接部分。此时我们将这些*.o文件称为:目标文件,它们的运行地址,均以0开始,真实运行地址将在链接阶段被重新指定,因此,它们更完整的名称应该是:可重定向目标文件。 2.6链接 在前面的阶段中,编译器均是对单个文件的处理,其生成的*.o目标文件中,也仅包含本文件的符号,但程序要想运行,一定要明确每个变量和函数的运行地址,而此时,*.o文件中的地址,都是以0开始,显然,直接合并会导致程序的变量和函数地址重叠,这样的程序无法运行,因此,需要链接器来进行最终的统筹工作,为*.o中的每个符号分配合适的地址。 在这个过程中,我们除了可以让源工程的目标文件(*.o)参与之外,还可以让其他可重定向目标文件参与。例如别的*.o文件、*.a、*.dll、*.lib等,它们,都属于可重定向目标文件,其内部的数据,已经符合目标处理器的指令集规则可以直接执行。链接器则需要进一步让他们有序的在存储器中排列,保障运行时程序有序执行,除此之外,部分数据(如代码段.text)我们将其指定链接到NOR-Flash中,即:从Flash中取指令,加载到CPU执行;另一部分(如数据段.data、.bss)将其运行地址链接到RAM中,以便在运行过程中改变数据。试想另一种情况,将全局变量链接到Flash是否可行呢? 显然,逻辑上是可行的,但是,程序运行时,读操作没有问题,写操作一定会异常,因为Flash的写操作,需要Flash驱动的参与,CPU的地址总线读写指令,显然不具备这样的能力。为了让全局变量运行时,能正常读写,我们将其运行地址重定向到RAM中。由于RAM的掉电丢数据特性,在程序main函数运行前,需要将.data段的数据(有初值全局变量的初值)复制到对应的RAM地址中,这部分工作不由链接器完成,而是由启动代码来执行,此时,连接器的重定向和启动代码的初始化RAM动作,有了因果关系。由于链接过程通常在非目标机器上完成,显然链接器只能做到指定运行地址,而无法搬移数据。这整个过程,我们称之为链接,而实际上,链接器还做了重定向工作。 链接是个复杂的过程,如果通过敲命令行进行链接,文件数量和参数少还好,一旦参数变多,那么,工作量将极其庞大,且容易出错。 于是,我们引入了链接脚本的概念,链接脚本中,将链接过程中需要指定的地址、存储分布、各个段的大小及起始地址,加载地址和运行地址,都做了约束。在链接时,我们仅需加载链接脚本文件,即可让链接器根据我们的约束,执行链接操作。关于链接脚本的编写和使用,读者可以根据需要扩展学习,在示例中,仅用于演示编译流程,并未使用链接脚本,以后有机会作者将在其他文章中详述链接脚本的语法格式。 链接命令我们可以使用继续使用arm-none-eabi-gcc指令,也可以使用arm-none-eabi-ld指令,均能生成可执行文件。二者的不同是,gcc指令可以输入*.c文件,直接执行到链接步骤,生成可执行文件,而ld指令,其输入只能是可重定向的目标文件。 链接指令如下所示: 上述两个指令均能生成elf可执行文件。 2.7生成HEX镜像 大多数情况下,都需要生成特定格式的Flash镜像文件(.hex、.s19、.bin),以便进行烧录及BootLoader升级。该需求可以使用arm-none-eabi-objcopy命令完成。 操作指令如下: 通过执行arm-none-eabi-objcopy指令,我们成功生成了hex文件。 2.8通过Makefile编译代码 文章至此,C代码编译的整个过程,基本介绍完毕,但在此处,还要特别引入MakeFile,尽管有了方便的编译工具帮助我们实现代码编译。源文件和编译参数的输入仍然是一件头疼的事,因此,Makefile的作用,即是将调用编译器编译的过程自动化,通过Makfile文件中约定编译参数、输入和输出,再执行Make命令,解析Makefile文件,帮助我们自动编译和构建可执行文件。 针对前文的示例,我们可以编写简单的Makefile,使用Make指令,解析Makefile,一个命令完成所有编译操作,生成*.elf可执行文件、*.hex文件、*.map文件,反汇编文件。 Makefile的语法不在本文讨论范围内,因此,不要求读者自己写Makefile,仅需会用Makefile完成编译即可。作者将在其他文章中深入探索Makefile语法,并总结记录成文。 本文的示例仅包含四个源文件,为其编写Makefile相对简单,在源文件目录下新建文本文件,去除扩展名,将其重命名为Makefile,输入如下内容,注意第2行和第3行需要以TAB键开头,否则无法将其识别为makefile文件: Makefile文件需要由make指令调用,make工具很多,其主要作用是解析makefile文件并执行命令,作者电脑里安装了多个make工具,有x86平台的mingw32-make.exe,该工具安装x86平台Gcc编译器后即可用,另外,还有mingw附带的make.exe,无论使用哪一个,将其路径加入环境变量后,均可解析Makefile文件进行编译。 分别使用mingw32-make.exe和make.exe编译结果如下: 实测两个工具均可以成功解析Makefile,并编译生成elf和hex文件,make工具的获取和安装将在后文中讲到。 同链接脚本一样,Makefile的编写是件非常考验技术人员的事,本文中的Makefile仅适合演示简单工程,并不适合大型工程使用,但Makefile的使用方法是一样的。自己动手编写启动代码、Makefile和链接脚本需要对编译器和目标平台足够熟悉,并且需要持续学习,有兴趣的读者可以深入探索。目前常规的开发流程下,其实启动代码、Makefiel和链接脚本并不需要自己编写,芯片厂商一般都会提供,只需要会用即可,某些场景下,需要进行简单的修改,仅此而已,是否需要深入学习,完全取决于兴趣及工程需要。 三、调试流程 常规的调试需求包含烧录加载程序、增删断点、查看修改变量等操作,除了Keil和IAR这样的封闭IDE外,大部分基于eclipse的集成开发环境大都支持开源调试工具链,这样能以最快的速度支持自家芯片,并与市面上大部分仿真器兼容。而GDB调试器和OpenOcd是其中的代表。本文的最终目标:自己搭建IDE,同样依赖于这两项强大的调试工具。下面将对GDB调试器和OpenOcd调试服务逐个进行介绍。 3.1 Openocd调试工具 Openocd是一套开源的调试工具,目前在很多IDE中流行,跨平台支持着众多目标芯片的仿真和调试,如果说GDB是调试的人机接口,那么Openocd则是GDB调试器与不同芯片之间的接口,其负责将调试指令下发至仿真器驱动直接和目标芯片进行交互,从而实现GDB所希望达到的命令效果。因此OpenOcd充当着GDB调试器和不同目标芯片之间的翻译器。Openocd的代码完全开源,其官网地址为:http://openocd.org/getting-openocd/。 官方并不直接提供其可执行文件,因为OpenOcd支持了太多硬件仿真器,通常不是所有仿真器都需要,可以根据需要自行下载并编译出合适的版本。当前最新版本为0.11,官网虽然不直接给出可执行文件,但在其主页推荐了4个编译维护OpenOcd的网站,它们都有提供可以直接使用的可执行文件程序包。 本文没有使用这四个中的任何一个,而是在VisualGDB的主页找到了支持广泛且自己测试下来更好用的编译版本,这个版本支持的仿真器更多,兼容性更强,在此附上下载地址:openocd-20210625.7z。 下载完成后,同样需要将Openocd的可执行文件所在路径加入环境变量以便使用。添加方法已在前文提及,不在此重复。 Openoce需要加载对应的配置文件才能正常使用,通常需要加载接口配置文件以及目标配置文件,接口配置文件描述了所使用的仿真器类型,如常用的CMSIS-DAP、ST-LINK、JLINK等,在Openocd的interface目录下均能找到对应的配置文件;而所谓目标配置文件描述了需要调试的目标芯片,在Openocd的target目录下,列出了openocd所支持的众多芯片的配置文件。 在开始调试前,需要建立Openocd与硬件的连接,并在后台为GDB调试器开放调试端口,一般在硬件连接成功后,即可打开默认的调试端口:3333。 建立Openocd与目标芯片的连接使用如下命令: 出现上图的提示信息即表示成功打开调试端口,此时,可以进行下一步,使用GDB调试器进行芯片程序的加载,运行与调试。 3.2 GDB调试器 在前面介绍GCC For Arm时,简单介绍过,对Arm平台调试,通常需要借助arm-none-eabi-gdb工具,该工具提供调试过程中,增删断点,调试目标等一系列操作的交互接口。GDB调试器其全称是:The GNU Project Debugger,从名称可以看出,其来源是大名鼎鼎的GNU项目,GDB一开始用于Linux平台的C代码调试,随着版本的迭代,现在则被Windows平台下的嵌入式开发IDE大量使用。arm-none-eabi-gdb是GDB的Arm平台版本,目前在众多ARM开发环境中被使用,以STM32为例,除Keil和IAR之外,近年来官方大力推广的TrueStudio和CubeIDE,二者都是基于eclipse的集成开发环境,都使用arm-none-eabi-gcc作为编译器,并集成了arm-none-eabi-gdb调试工具。 注:命令行下使用gdb务必先将gdb所在路径加入环境变量 下面对GDB的常用命令进行介绍: 1.启动gdb调试工具并加载elf文件 arm-none-eabi-gdb file_name.elf 2.连接到Openocd的调试端口(默认为:3333) target remote localhost:3333 3.烧录程序 load 4.main函数处增加断点 b main 5.删除所有断点 delet 6.打印变量值 p 变量名 7.设置变量值 set 变量名 = value 8.继续执行程序 continue 下面附上部分STM32F4通过GDB命令行工具调试的记录: 四、自己搭建IDE 通过上文的讲解,读者应对整个代码编译及调试流程有了一定了解,清楚了IDE所需的工具都有哪些。最后需要做的是:将这些工具组合起来,让命令行工具链以可视化方式与开发者交互。,最终,能在自己搭建的IDE上实现代码编辑、编译、烧录下载、调试,IDE的搭建就算完成。 4.1准备工作 在开始搭建IDE前,我们需要先列举一下搭建IDE所需的准备工作,主要是下载IDE所需的各部分替代工具,以及对它们进行配置。大部分内容已在前文中进行详述,并给出工具的下载链接,为了读者阅读方便,所有用到的工具,将在本节再次汇总其获取和配置方法。 下列工具,已链接了超链接,点击即可直接跳转下载。
前面提到,工具链都准备齐全了以后,自己搭建IDE还有三大障碍:启动代码、Makefile、链接脚本。以上三个文件都由开发者手动完成的话,工作量实在不小,实际上,如果都能自己写这三个文件了,其实IDE也没有必要了。 因此,开发STM32不需要自己会写这三个东西,通常芯片官方都会有提供,需要注意的是,对于STM32标准库而言,官方给出的标准库中,其启动代码和链接脚本,分别适配了Keil、IAR、以及GCC,如果使用标准库,提取时需要注意区分。若使用标准库,必须自己编写Makefile,因为Makefile文件的内容与工程文件结构直接关联,官方无法给出固定的Makefile。其实,面对这种情况,也不是无解,还可以通过Cmake工具,编写Cmake文件,自动生成Makefile。关于Cmake这部分内容,作者没有研究,有空研究再开篇单独说。 STM32近年来,大力推广HAL库及其代码配置工具STM32CubeMx,不少人用过,初始化配置自动生成代码确实方便。仔细观察其生成选项,除了可以生成Keil工程、IAR工程、TrueStudio工程、STM32CudeIDE工程,还可以生成Makefile工程。是的,生成的Makefile工程,已经同时包含了我们需要的启动代码、链接脚本、以及Makefile,这就是我们想要的。 CubeMx只用来生成工程,编译过程不需要它,因此,无需将其加入环境变量,下面省略其安装过程直接以图文方式讲解生成步骤,第一次使用需要安装库,可能会稍慢。 1.选择目标芯片,本文以STM32F407开发板为硬件平台,配置编写简单的点灯示例作为演示 2.配置晶振和LED引脚,作者开发板LED引脚为PF9与PF10,将其配置为GPIO_Output,配置PH0及PH1为晶振时钟输入引脚。 3.配置RCC选择时钟源,有外部晶振时,根据需要选择即可。 4.配置Debug调试方式,根据实际情况选择,不能不使能Debug,否则会导致初次烧录完成后无法通过Debug烧录。 5.根据需要配置GPIO引脚别名,笔者此处将其配置为:“LED0”、“LED1”。 6.配置系统时钟,注意选择时钟源、锁相环及时钟频率,填写时钟频率后,CubeMx会根据时钟源自动计算锁相环增益。 7.配置生成Makefile工程,生成后启动文件及链接脚本会自动包含在生成的工程中。 8.其他配置,作者此处勾选了将外设初始化单独放在各自的.C和.H文件中,否则,所有初始化代码都会直接在main.c中定义,实在别扭,此处读者可以根据个人习惯决定。 9.生成工程代码。 生成的工程目录内容如下,可以看到,除了HAL库和初始化配置代码外,启动代码、Makefile、链接脚本都在在里面: 4.3安装Vscode及插件 Vscode轻量而强大,原本只是一个文本编辑器,但其丰富强大的插件库,可以适应众多应用场景:各种形式的文本编辑、代码管理、语法高亮、智能补全、文件解析对比等,被众多开发者和普通用户喜爱。在笔者电脑上,VScode已经成为装机必备的万能工具,替代了电脑中原先的多个软件,真正的在朝着All IN ONE的方向发展,这次,被笔者用来作为ARM平台的嵌入式开发IDE,它完美诠释了小而强大:编辑体验碾压Keil和IAR,UI界面碾压Eclipse,加上其他插件,可以做到一个软件搞定大部分开发需求 安装过程此处省略,要想使用Vscode进行嵌入式开发,需要安装两个组件: C/C++插件、Cortex Debug,前者用以分析C代码,语法高亮和智能补全,后者用来配置调试工具以及调试代码。 Vscode插件安装方法很简单,在其扩展商店中直接搜索,安装即可: 安装C/C++插件: 安装Cortex Debug插件: 以上两个插件安装完毕后,Vscode基本可用了。 其他插件,读者可以自己选择性的安装: Intel HEX format,可解析HEX文件 Hex Editor,可编辑二进制文件 Mapfile Syntax,可解析.map文件 LinkerScript,可解析链接脚本.ld文件 4.4安装配置GCC For Arm编译器 GCC For Arm在前文花了大量篇幅讲述,本文给出的链接下载后是压缩包,因此,解压即可使用,无需安装。 为了让GCC在任意目录下均可用,笔者将GCC For Arm工具链路径加入环境变量,添加方法在2.3节有详细说明,请参照前文。 为验证arm-none-eabi-gcc工具是否正确加入环境变量,可以打开powershell控制台,在控制台输入arm-none-eabi-gcc,若出现如下提示,即表示GCC For Arm已经全局可用。 4.5通过Msys2获取Make工具 本节主要目的是获取使用Makfile编译所必须的Make工具,由于历史原因,GCC原本是linux平台下的编译套件,Make工具也集成在其中,因此需要安装x86平台的GCC编译器以获取Make工具,x86平台的GCC,其包名称为:mingw-w64。 GCC下载渠道很多,但笔者这里通过msys2安装,msys2是一个包管理工具,正如其官方介绍:“MSYS2 is a collection of tools and libraries providing you with an easy-to-use environment for building, installing and running native Windows software”。Windows平台下的很多开源工具一开始都在linux下流行,因此,其很多依赖库都是linux移植而来,Msys2就是这样一个工具,可以方便的下载这些依赖,以支持开发者在Windows平台开发和运行原生应用。本文中,我们仅用它安装x86平台GCC以获取Make工具,实际上,开发过程中的很多依赖,都可以用它下载,下面开始安装步骤。
2.打开Msys,运行命令:pacman -Syu,更新数据库。 3.运行命令:pacman -S mingw-w64-x86_64-toolchain,安装GCC工具链,出现提示时,回车全部安装即可,当前的Msys2已添加中科大和清华镜像源,下载安装很快。 安装完成后,进入Msys2安装目录,GCC的bin目录下的mingw32-make.exe即是我们需要的make工具。 4.将mingw32-make.exe所在目录加入环境变量,在控制台输入命令:mingw32-make,出现如下提示即表示加入全局变量成功。 5.提取make工具至GCC For Arm,这一步是可选的,不是必须,如有需要,可将mingw32-make.exe复制到arm-none-eabi-gcc所在目录,更名为make.exe。此时,make也全局可用。 4.6配置OpenOCD 经过以上步骤,生成的工程可以正常编译,并生成烧录文件,而调试功能,除了依赖于arm-none-eabi-gdb,还需要配置OpenOcd以适配仿真器,建立目标芯片,仿真器以及PC的连接。 本文给出的OpenOcd版本为0.11,解压即可使用,为了开发方便,需要将openocd.exe的路径加入环境变量,方法请参照2.3节。 通常调试目标芯片都使用确定的仿真器,因此,为了使用方便,可以直接从Openocd的目录中,将接口配置文件和目标配置文件复制到STM32的工程目录,以便使用:
4.7使用Make编译STM32工程 在4.2中生成的工程已经包含了Makefile,至此,编译器和make工具均已就绪,我们可以尝试在命令行下编译STM32工程。
2.执行mingw32-make或make指令,指令后跟-j参数,可启动并行编译,充分利用处理器多核性能,加快编译速度 可以看到,Make编译完成后,控制台打印了程序大小信息,生成了elf文件,以及hex和bin文件以及一堆lst反汇编文件,这些文件以及编译的中间文件,都存放在工程下自动新建的build目录。其中:
GDB调试的前提是OpenOcd调试工具已经正确完成硬件连接,并开启调试端口,因此,调试前需要运行openpcd命令,连接目标芯片,且调试过程中,Openocd服务需要持续运行。 操作步骤如下: 1.连接开发板,上电,确保仿真器正确连接。 2.打开调试端口,在工程目录下打开powershell,由于已经将接口配置文件和目标配置文件复制到工程目录,直接使用目录下的配置文件即可,执行指令:openocd.exe -f .stlink-v2.cfg -f .stm32f4x.cfg.执行结果如下,打开3333调试端口成功: 3.开启gdb调试器,加载上一节中编译生成的elf文件,由于Openocd需要持续运行,我们新开一个powershell终端,执行如下指令(elf文件名根据实际情况填写): arm-none-eabi-gdb.exe .buildSTM32_LED.elf 终端将打印如下内容: 4.连接至3333调试端口 执行target指令即可连接至openocd开启的3333调试端口,成功连接后,gdb提示产生了复位中断,一切正常。 5.烧录elf文件至目标芯片 在GDB工具中输入指令:load,即可将启动gdb时加载的elf文件烧录至目标芯片。烧录成功的打印内容如下,其他调试指令读者可参照3.2节自行尝试: 五、利用Vscode将命令行工具图形化 5.1让Vscode正确识别工程 VScode本质上只是一个文本编辑器,如果要使其自动识别工程下的C语言源文件,需要对C/C++插件进行正确的配置才能获得我们想要的效果: 1.源文件搜索路径 有些工程的源文件并不在工程目录下,可能在别的SDK路径下,此时如果不添加源文件路径,C/C++插件无法找到源文件并正确跳转。(本文示例工程不存在SDK路径,所有用到的源文件均在工程目录下,因此添加工程路径即可) 2.预编译宏定义 许多厂商提供的库都支持自家多款处理器,通常使用不同的预编译宏定义来进行条件编译以适配不同处理器,因此,需要在工程中添加预编译宏定义才能正确编译和高亮。(对于本文示例来说,编译部分由Makefile决定,因此,Makefile中已经添加了预编译宏,编译不会出错。然而,C/C++显然它不会去读取Makefile内容获取工程应加的预编译宏,无法高亮显示正确的代码,因此预编译宏应由我们手动添加) 3.编译器路径 插件要想参与编译,必然要获取编译器的路径,在VScode的C/C++插件中,编译器路径必须完整到编译器的可执行文件。 4.C语言标准 不同的C语言标准下,支持的语法会稍有区别,因此,必须让C/C++插件知道工程的源文件代码以哪一个版本的C语言标准提供识别支持(如果不知道请填C99,大部分人所学的C语言版本普遍为C99,使用GNU工具链则填写GNU99)。 5.高亮感知模式 这部分在该插件下提供了众多可选项,目的是为了更好的适配目标平台进行代码高亮。(本示例中,选用“gnu11”) 以上内容的配置,网上大部分教程通常直接让读者新建c_cpp_properties.json文件配置,该方法适合对Vscode有一定了解的读者,对新手并不友好。其实,Vscode本身提供了图形化的配置界面。 选择图形化配置界面后,将本节提到的几个参数作如下配置: 此处要特别注意预编译宏的添加,具体添加什么内容,可从Makefile中的C_DEFS字段获知。 做完以上更改后,工程目录夹下将会新增.vscode文件夹,目录下新增了c_cpp_properties.json配置文件,打开该文件,可以看到,里面已经有了自定义的ARM配置。 删除并不需要的默认Win32配置后,c_cpp_properties.json中的配置即为以后该工程的默认配置: 5.2在Vscode下使用Make编译工程 上一节我们通过配置C/C++插件,让代码编辑和语法高亮,变量函数跳转这些功能正常使用,体验甚至超过Keil、IAR、Eclipse等主流IDE,本节我们尝试新建build任务,让Vscode可以在图形界面下编译工程。新建build任务笔者暂未找到图形化配置方法,但可以参考Vscode的build任务模板进行配置。 1.创建build任务模板,操作如下图所示: 2.配置build任务和clean任务,最终配置如下 3.尝试执行build命令编译工程 在Vscode界面下按Ctrl+Shfit+B可以唤出任务列表,可以看到task.json中配置的两个任务,一个build,一个clean。 执行build任务后,可以看到,其效果与在命令行下执行make -j或者mingw32-make -j的效果一致,都在build目录下生成了.elf与.hex文件,其本质是编译Makefile中的all伪目标,因此,make -j all才是命令的完整写法,我们使用了简化写法。 4.尝试执行clean命令清除编译输出文件 某些情况下,需要清除所有输出文件,重新进行全新编译而不是增量编译,此时,需要编译Makefile中的clean伪目标,查看Makefile可以知道,其真实操作是在powershell下执行了rm指令,删除中间文件,只不过由make工具来帮助完成解析执行。 执行clean任务后,build目录下的所有文件都将被清除,读者可以自行尝试。 5.3在Vscode下烧录调试 经过上一节的内容,我们已经可以在Vscode下正常编辑编译代码,本节我们将通过Cortex Debug插件,在Vscode下实现正常的烧录,调试。 通过前面的介绍,我们知道,调试需要先开启Opencod调试服务器端口,再使用gdb调试器连接至调试端口,有了这些基础,才能正常调试。而Vscode下的Cortex Debug插件则在GDB调试器的基础上做了深度集成,支持众多调试服务器。如果抽象一点,可以这样理解:Cortex Debug = GDB + GDB Server,其中,GDB Server可以是Openocd GDB Server、J-Link GDB Server、ST-LINK GDB Server、pyOCD GDB Server等的任意一种,本文使用的是Openocd GDB Server。 配置Cortex Debug的步骤如下: 1.新建调试配置模板 2.复制SVD文件至工程目录 由于Cortex Debug支持加载SVD文件,因此,需要自己找到目标芯片的SVD文件,拷贝至工程根目录,供Cortex Debug加载使用。SVD是芯片的资源描述文件,相当于数据化的芯片手册,有了SVD文件的支持,调试时可以查看芯片寄存器和外设的状态。 SVD文件可以在芯片的支持包中找到,具体到本文示例,作者是在Keil的包路径中将所需SVD文件提取出来,其路径为:xxKeilPacksKeilSTM32F4xx_DFP2.13.0CMSISSVD。其中包含了STM32系列F4系列所有芯片的SVD文件: 本文示例所用芯片为STM32F407,因此将STM32F40x.svd文件拷贝至工程目录下: 3.配置Openocd调试服务器 步骤1后,.vscode文件夹下将新建launch.json文件,我们需要对其默认内容稍作修改: 4.写个简单的流水灯 修改CoreSrcmain.c,在GPIO初始化函数MX_GPIO_Init()下,添加如下两行代码,使LED0与LED1的初始状态相反: HAL_GPIO_WritePin(GPIOF,LED0_Pin,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOF,LED1_Pin,GPIO_PIN_RESET); 修改CoreSrcmain.c,在While主循环中添加如下两行代码,使LED状态50ms反转一次。 HAL_GPIO_TogglePin(GPIOF,LED0_Pin|LED1_Pin); HAL_Delay(50); 5.调试运行流水灯 在Vscode界面按下F5,或者点击Debug按钮,后台将执行编译任务,若编译无误,将自动启动Openocd服务,烧录调试,开始运行时会先进入复位中断,点击运行即可正常运行,可以在运行过程中随时打断点,查看寄存器和变量,单步运行。 编辑界面如下,输入时可以智能提示补全: 调试界面如下: 六、总结 至此,本文的最终目的已经达到了,目标虽然是自己搭建集成开发环境,但实际是为了深入理解工具链、编译流程、调试原理。很多技术细节限于篇幅无法更加深入的探索,而Makefile、启动文件、链接脚本、Openocd、编译器参数、每一个都值得深究,本文算是挖坑文,希望以后有空继续在深入理解工具链的系列文章中补全,再会! |
|
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
4140 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
3228 浏览 1 评论
2753 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
2181 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
14984 浏览 2 评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
3087浏览 4评论
stm32f4下spi+dma读取数据不对是什么原因导致的?
1896浏览 3评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
2066浏览 3评论
1979浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
2168浏览 3评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-2 13:34 , Processed in 0.891907 second(s), Total 72, Slave 55 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
2016