Linux下开发STM32:使用gcc-arm-none-eabi工具链编译生成bin、hex文件

2023-11-23 04:20

本文主要是介绍Linux下开发STM32:使用gcc-arm-none-eabi工具链编译生成bin、hex文件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.为什么不是gcc

在开发stm32的时候,编译工具链要使用gcc-arm-none-eabi,为什么不是gcc呢?这就要说到linux下的交叉编译了,因为我们要在PC机上编译出可以运行在ARM上的程序,使用gcc编译出的是在PC上运行的程序,所以我们要使用gcc-arm-none-eabi进行交叉编译~

2.gcc-arm-none-eabi toolchain 介绍及安装

gcc-arm-none-eabi是一个开源的ARM开发工具链,适用于Arm Cortex-M和Coretex-A系列处理器,包括GNU编译器(GCC),以及GDB,可用于Windows,Linux,MacOS上的交叉编译。

640?wx_fmt=png

在此我们从[ARM官方下载链接](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads)选择合适的版本下载(这里我选择Linux64):

640?wx_fmt=png

解压下来是tar.ba2格式包,使用命令tar -jxf <要解压的文件>解压到我们要安装的目录:

640?wx_fmt=png

为了以后使用方便,将文件夹重命名:

640?wx_fmt=png

它下面的bin目录就是我们要使用的编译工具链:

640?wx_fmt=png

share目录的doc下包含了大量的使用帮助文档,可以先略读一二,特别是readme.txt

640?wx_fmt=png

接下来我们要将bin目录添加到环境变量,这样可以直接在命令行输入要使用的工具名,然后系统就可以找到该工具,在此我们仅为当前用户添加环境变量,使用vim ~/.bashrc编辑当前用户配置文件,在最后添加export PATH=$PATH:/home/mculover666/gcc-arm-none-eabi/bin

640?wx_fmt=png

然后使用命令source ~/.bashrc更新系统路径,使添加的环境变量立即生效:

640?wx_fmt=png

然后输入命令arm-none,然后按三下Tab(一定不要输入全部),检查系统是否可以自动补全:

640?wx_fmt=png

如果系统可以提示,说明环境变量配置成功,可以开心的使用arm-none-eabi工具链啦~

3.从裸机工程开始

3.1.硬件说明

这里我使用的是野火霸道开发板,板载芯片为STM32F103ZET6,下载器使用e-link,这个下载器使用CMSIS-DAP下载程序,同时并带有一个串口,非常好用~

640?wx_fmt=png

板载RGB-LED的原理图如图所示:

640?wx_fmt=png

3.2.新建空的裸机工程

首先新建一个文件夹mkdir 00-template-reg用来存放整个工程,然后整个工程包含三个文件:

  • startup_stm32f10x_hd.s:从固件库中拷贝,注意不是arm文件夹下的,因为truestudio使用的是gcc编译器,所以我们选择truestudio文件夹下的启动文件;

  • stm32f10x.h:空文件;

  • main.c:代码如下:

#include "stm32f10x.h"int main()
{/* 开启GPIOB时钟 */*(unsigned int*)(0x40021000+0x18) |= 1&lt;&lt;3;/* 配置PB0为推挽输出 */*(unsigned int*)(0x40010c00+0x00) |= 1&lt;&lt;(4*0);/* PB0输出低电平,点亮绿色LED */*(unsigned int*)(0x40010c00+0x0c) &amp;= ~(1&lt;&lt;0);while(1);
}
void SystemInit(void)
{}

4.编译

接下来就是激动人心的编译步骤了~编译的时候有两种文件,一种是汇编启动文件,一种是c源文件,接下来分别编译:

参数说明
-mthumb表明使用的指令集(必需)
-mcpu=cortex-m3表明芯片内核(必需)
-g产生调试信息

4.1.启动文件编译

启动文件一般是由汇编写成,此处需要注意的是,汇编文件的格式有.S.s之分:

  • 大写S:表明文件中含有预处理指令(比如#define),需要先进行处理

  • 小写s:表明文件不需要处理,可以直接编译;

之前我们添加的启动文件是小写.s,所以直接进行编译,另外说一下,如果使用的是.S文件,那么需要带上-x assembler-with-cpp参数。

接下来说明一些汇编文件gcc编译器使用的参数:

参数说明
-x assembler-with-cpp先对文件进行预处理
-Wa,option向汇编器Assembler传递参数

注:可以向汇编器传递的参数:

参数说明
-W或--no-warn关闭所有告警
--fatal-warnings将所有的警告提示为错误
--warn正常提示告警信息

所以,接下来我们可以使用如下的参数组合来编译启动文件(不进行预处理,并且正常提示告警信息):

arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -g -Wa,--warn -o startup_stm32f10x_hd.o startup_stm32f10x_hd.s
640?wx_fmt=png

4.2.C文件编译

因为main.c中没有特殊的东西,只是两个函数,所以简单的编译一下就可以了:

参数描述
-Wall允许输出所有警告
arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m3 -g -Wall -o main.o main.c

5.链接

链接重要的部分有两点:链接文件传递给链接器的参数

640?wx_fmt=png

其中 stm32_flash.ld是针对于STM32F103ZE的链接文件,如果是别的芯片,需要进行修改,将它复制到我们的工程中去:
640?wx_fmt=png

然后就要让链接器开始根据 stm32_flash.ld这个文件对 startup_stm32f10x_hd.omain.o这两个文件开始链接,生成包含了调试信息的elf文件,同时,我们还需要给链接器传递一些参数:

参数描述
-T指定链接文件


arm-none-eabi-gcc -o test.elf main.o startup_stm32f10x_hd.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

6.生成bin文件和hex文件

利用arm-none-eabi-objcopy工具可以将elf文件转化为适合于单片机的bin文件和hex文件,其中参数-O(大写o)用于指定输出文件的格式(默认是bin格式)

arm-none-eabi-objcopy test.elf test.bin
arm-none-eabi-objcopy test.elf -Oihex test.hex

7.编写一个makefile雏形

TARGET=test
CC=arm-none-eabi-gcc
OBJCOPY=arm-none-eabi-objcopy
RM=rm -f
CORE=3
CPUFLAGS=-mthumb -mcpu=cortex-m$(CORE)
LDFLAGS = -T stm32_flash.ld -Wl,-cref,-u,Reset_Handler -Wl,-Map=$(TARGET).map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80 -Wl,--start-group -lc -lm -Wl,--end-group
CFLAGS=-g -o
$(TARGET):startup_stm32f10x_hd.o main.o$(CC) $^ $(CPUFLAGS) $(LDFLAGS) $(CFLAGS) $(TARGET).elf
startup_stm32f10x_hd.o:startup_stm32f10x_hd.s$(CC) -c $^ $(CPUFLAGS) $(CFLAGS) $@
main.o:main.c$(CC) -c $^ $(CPUFLAGS) $(CFLAGS) $@bin:$(OBJCOPY) $(TARGET).elf $(TARGET).bin
hex:$(OBJCOPY) $(TARGET).elf -Oihex $(TARGET).hex
clean:$(RM) *.o $(TARGET).*
  • 使用命令make编译生成elf文件;

  • 使用命令make bin将elf文件转化生成bin文件;

  • 使用命令make hex将elf文件转化生成hex文件;

  • 使用命令make clean即可清除掉所有编译产生的文件。

本文转自:Mculover666

推荐阅读:

给初入职场朋友的8条走心建议

从TrueSTUDIO迁移到STM32CubeIDE只需要简单几步

关注公众号『strongerHuang』,在底部菜单中查看更多精彩内容!

640?wx_fmt=jpeg

长按识别图中二维码关注

这篇关于Linux下开发STM32:使用gcc-arm-none-eabi工具链编译生成bin、hex文件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/415294

相关文章

VScode连接远程Linux服务器环境配置图文教程

《VScode连接远程Linux服务器环境配置图文教程》:本文主要介绍如何安装和配置VSCode,包括安装步骤、环境配置(如汉化包、远程SSH连接)、语言包安装(如C/C++插件)等,文中给出了详... 目录一、安装vscode二、环境配置1.中文汉化包2.安装remote-ssh,用于远程连接2.1安装2

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

golang1.23版本之前 Timer Reset方法无法正确使用

《golang1.23版本之前TimerReset方法无法正确使用》在Go1.23之前,使用`time.Reset`函数时需要先调用`Stop`并明确从timer的channel中抽取出东西,以避... 目录golang1.23 之前 Reset ​到底有什么问题golang1.23 之前到底应该如何正确的