没有IDE的嵌入式编程

2020-10-25 23:05:07

不幸的是,在我接受的大部分编程教育中,我认为语句3和4是相互排斥的。我工作过的每个嵌入式项目都需要使用Keil uVision、Code Composer、Eclipse……。通常的嫌疑犯。这些程序本身没有什么问题,事实上,它们大大降低了嵌入式开发的门槛。它们被广泛使用是有原因的!

也就是说,当我开始一个项目时,当我在向导中单击,选择项目模板,并看到工作区中弹出一堆神秘的文件和目录时,我总是有点沮丧。这些东西都做了什么?为什么我看不见发生了什么事?这些样板代码在我的项目中做了什么,所有这些复杂性真的有必要吗?当我向队友表达这种挫折感时,我经常会得到这样的回复:嗯,如果你愿意,你可以阅读数据表,手动编写所有的注册表。请便!&34;球队有时也会尖刻。

因此,当我开始研究FreePulse时,我开始研究是否有更好的方法。就在我放弃希望之前,我偶然看到了这篇精彩的博客文章,详细描述了作者是如何为STM32F4发现板上的一个项目构建Makefile的。当我开始使用相同的芯片重新设计FreePulse时,我决定加入进来,试着完全放弃Eclipse。我对这个结果非常满意,希望这篇博客文章能指导其他想要独立于IDE开发嵌入式平台的人。

我将对make做一个简要的概述,详细介绍如何使用make来执行大多数有用的IDE编译功能,然后简单介绍如何将其集成到一个非常流畅的开发工作流中,只使用VIM和命令行即可。如果你觉得你已经很好地掌握了Make的工作原理,那就随便跳过吧!

当开始任何编码项目时,我要做的第一件事就是尝试确定Makefile中程序的布局。来自自由软件基金会的人:

GNU make是一个控制从程序的源文件生成程序的可执行文件和其他非源文件的工具。1个。

Make是相当棒的。虽然我不打算对make所能做的一切做一个详尽的教程(部分原因是那样会花费太长时间,部分原因是我还在不断地学习更多内容),但我会把它作为勾勒嵌入式程序结构的工具。

#源文件(所有*.c或*.cpp文件)SRCS=path/to/my/first/source/file.c SRCS+=path/to/my/Second/source/file.c#...etc#包含您的头文件(所有*.h文件)的目录INC_DIRS=first/library/with/Inc_Dir+=Second/library/with/Inc#...etc include=$(addprefix-i,$(INC_DIRS)#设置CFLAGS=-WALL CFLAGS+=-STD=c++11#...etc#链接器文件(所有*.ld文件)LFLAGS=-Tfirstfile.ld-Tond dfile.ld#Tell Make如何将*.c文件编译为*.o文件%.o:%所需的任何编译器选项。C GCC-c-o$@$<;$(CFLAGS)#最后,告诉make如何构建整个项目最终_binary.elf:$(SRCS)GCC$(INCLUDE)$(CFLAGS$(LFLAGS)$^-o$@

哇哦。虽然这看起来似乎很难理解,但我保证它实际上非常容易理解!要分解它,请执行以下操作:

我们声明由.c或.cpp文件扩展名表示的源文件。它们包含制作我们出色的应用程序的所有代码,还包含设备库所需的AnySource文件。

我们声明包含所有包含文件的文件夹,用.h文件扩展名表示。Include文件通过保存函数声明让我们单独的C/C++文件使用彼此的函数,您可以通过#include;ome efile.h";指令告诉编译器您想要什么函数。注意(也很容易忘记)您的不同源文件不会自动知道彼此包含什么,这一点非常重要。如果它未在头文件中声明,而头文件未包含在目标文件中,则它将不会编译。末尾的addprefix函数做的正是您所期望的,因为我们的编译器需要知道我们前面的include目录前面的小-i标志来查找我们的内容。

我们声明任何编译器选项或标志,它们是生成将保存在微控制器存储器中的最终二进制文件所必需的。稍后我将详细介绍你应该在这篇文章中使用哪些旗帜。

我们声明我们需要使用的所有链接器文件(由.ld文件扩展名指示)。链接器文件实际上是由自由软件基金会(Free Software Foundation)的人为另一个程序编写的,恰当地称为ld(现在文件扩展名是有意义的)。LD是由GCC或g++这两个主要的C/C++编译器自动调用的,因此我们不必担心细节问题。不过,本质上说,ld帮助编译器告诉编译器,当我们的程序最终编译时,它是如何对齐的。稍后会详细介绍这一点。

这段Makefile代码的最后两个块是魔术发生的地方。这些是规则,make将用来编译您的程序。规则的语法为:

第一条规则是make中的一种特殊规则,称为模式规则。这条规则告诉make,如果我们想从源文件(即依赖关系=%.c)生成目标文件(即target=%.o),我们应该使用下面由四个空格缩进指定的命令。但是,上面示例中给出的命令究竟是什么呢?

正如我们前面提到的,GCC是一名编译器;-c标志指定我们要编译源文件,-o标志说明下一个参数将是输出文件的名称。那个名字就是..。$@?

事实证明,关于make,我们还需要学习最后一件小事:自动变量。您可以阅读有关Make提供给您的所有不同自动变量的文档页面,但我们将重点介绍这里刚刚使用的两个变量:

$<;表示冒号右侧的第一个依赖项。在本例中,由于只列出了一个依赖项,因此$<;==%.c。

因此,在做了一些转换之后,我们看到模式规则告诉我们,对于我们想要转换为对象文件(%.o)的每个源文件(%.c),它应该调用我们的编译器(GCC),告诉它使用我们指定的一些编译器选项($(CFLAGS))将源文件($<;)编译(-c)到输出文件(-o$@)。还不错!你已经在去[摸索][摸索文章]的路上了。

我们几乎涵盖了这条规则中的所有内容,除了$^变量,它只是另一个自动变量。$^代表依赖项列表中的每个文件,而$<;只代表第一个文件。记住这一点的一种俗气的方法是看箭头的方向!有了最后一点知识,我们可以自信地解释最后这条规则:

获取我们所有的源文件($(SRCS)),并将它们转换为最终的输出二进制文件,名为finalbinary.elf。为此,请调用我们的编译器(GCC),告诉它我们的包含目录在哪里($(INCLUDE)),指定我们的编译器选项($(CFLAGS))和链接器文件($(LFLAGS)),最后给它所有的源文件($^),并告诉它我们希望输出是什么(-o$@)。

由于微控制器编程的一些独特方面,我们需要在一些事情上更明确一些。这就是Liviu的博客文章真正给了我一些至关重要的指导的地方;同时,这也是我们可以看到构建Makefile如何让我们窥探当我们点击IDE上的编译和运行按钮时会发生什么的神秘之处。

首先,我们需要指定我们将使用哪种编译器。虽然您的计算机上可能安装了GCC或g++,但它们是为在您的计算机体系结构上编译程序而构建的。我们需要GCC的一个特殊版本,称为交叉编译器,这样我们就可以跨体系结构编译成ARMarchitecture。你可以从这个镜像站点下载它,或者在软件包管理器中寻找它,比如HOMEBREW(OSX)、APT-GET(Ubuntu)、Pacman(适用于Arch Linux),或者与您的发行版类似的包管理器。

一旦安装了交叉编译器工具集,程序可能会有很长的名称(例如,arm-one-eabi-g++)。在你的Makefile中打字会很麻烦,所以你可以制作变量来保存你工具的路径。我使用TOOLS_DIR保存交叉编译器工具集中可执行文件目录的路径,然后指定我将使用的工具:

接下来,我们需要指定帮助我们编译程序的CFLAGS。Liviu的Makefile非常有用,因为它对标志进行了逻辑分组,因此我们可以更好地了解正在发生的事情。我不会给出每个标志的确切描述(您可以在链接的文档中查找),但我会给出每个组的概述:

Cflags=-ggdb设置调试选项,以便编译器以我们稍后将使用的调试程序gdb可以理解的格式发送信息。

CFLAGS+=-O0告诉编译器不要对代码执行优化,这对于使用调试程序gdb很重要。您可以稍后增加这个数字,但只有在进行彻底测试并准备停止使用gdb时才能这样做。

CFLAGS+=-WALL-WEXTRA-WARRAY-BILDES全部设置警告选项,以便当您的代码不满足某些条件时,编译器会对您大喊大叫。非常适合在运行前捕获错误!

CFLAGS+=-mLittle-endian-mhumb-mcpu=corest-m4-mhumb-interwork都告诉编译器机器相关的选项。这是关于ARM处理器的信息(例如,Little endian、Thumb ISA、corlet-m4cpu等)。

CFLAGS+=-mflol-abi=hard-mfpu=fpv4-sp-d16告诉编译器更多与机器相关的选项,但这些选项特别涉及处理偏移点编号。

CFLAGS+=-Felide-structors-std=c++0x都指定了C++语言选项。这些是编译器将强制执行的关于C++的规则,允许它有选择地允许或限制某些语言功能是否将编译。乍一看,-Felide-structorsflag的作用并不明显;但是,在C++中使用Copy构造函数时,它允许优化内存(有关更多信息,请阅读文档)。

现在我们已经理顺了CFLAGS,当我们需要配置项目时,我们可以将重点转向让我们的生活变得更容易。我们要做的第一件事是指定我们的SRC_DIR,很大程度上与我们指定INC_DIR的方式相同。这样做的好处是,然后我们可以将以下命令添加到Makefile:

这些命令将告诉make在这些源目录中查找在当前目录中找不到的AnySource文件。现在,当我们指定SRCS变量时,我们不必键入每个源文件的完整路径。相反,我们可以只列出不带路径的文件名:

最后,我们将把所有这些知识很好地利用起来,并整理出更多的规则。既然我们有了这个很棒的Makefile结构,那么制定新规则就轻而易举了:

.PHONY:$(PROJ_NAME)$(PROJ_NAME):$(PROJ_NAME)。精灵%.O:%。C$(Cc)-c-o$@$<;$(CFLAGS)$(PROJ_NAME).elf:$(SRCS)$(CC)$(INCLUDE)$(DEFS)$(CFLAGS)$(LFLAGS)$^-o$@$(OBJCOPY)-O ihex$(PROJ_NAME).elf$(PROJ_NAME).hex$(OBJCOPY)-O BINARY$(PROJ_NAME).elf$(PROJ_NAME).bin CLEAN:RM-f*.O$(PROJ_NAME).elf$(PROJ_NAME)。).hex$(PROJ_NAME).bin FLASH:ST-FLASH WRITE$(PROJ_NAME).bin 0x8000000。

太棒了!现在我们有了一套强大的制定规则。让我们看看我们现在能做些什么:

Make:仅调用make本身就会触发文件中的第一个规则。在本例中,它只调用我们的项目名称,这取决于最终的二进制.elf文件。Make如何创建该文件?它查找规则以查找它,并且在规则3处找到了匹配项!因此,我们的程序二进制文件是使用前面指定的编译器选项和文件构建的。

清理:这将从我们的工作区中删除所有相关的输出文件。这对于确保您在重新生成项目时有一个干净的开始非常有用。

制作闪存:这将调用外部程序st-flash将扩展名为.bin的.elf文件的副本闪存到微控制器中。0x8000000是STM32F4特定的地址,用户代码应该加载到该地址。

使用这些工具,我们已经创建了一个主Makefile,它完成了获取源代码、包括路径和ARM配置的所有繁重工作。现在我们可以专注于编码了!查看FreePulserepository,获取我们刚才概述的Makefile的真实示例。

在我的下一篇博客文章中,我将介绍如何使用VIM和You Complete自动完成引擎和语法检查器来构建一个轻便而强大的编码工作空间。在那篇文章的后面,我还将介绍如何在STM32F4上使用USART接口构建一个简单的命令行控制台记录器。

希望这些帖子能作为如何开始独立于IDE的嵌入式开发项目的有用参考!您可以随时查看FreePulse存储库,查看如何在项目中实现此功能的代码示例。