首页 > 解决方案 > arm-none-eabi-gcc 与 nucleo L432KC 板

问题描述

我正在寻找一个程序来从 linux 终端编译和上传我的 STM32L432KC 核板代码,就像我在 atmega328p 上使用的程序一样

我有点喜欢使用 vim 和 gdb 调试器,我很高兴为我的 avr atmega328p 和 avr-gcc 和 avra 组装了一段时间但现在我想继续深入研究嵌入式系统,所以我买了我的核板文档页面

所以我只需要一个像上面那样的小教程来编译、链接和刷写代码,而不需要安装任何 IDE

标签: gccarmthumbnucleo

解决方案


STM32 芯片都是基于 cortex-m 的(他们从 ARM 购买的内核)。到目前为止,它们都支持 cortex-m0 指令集(armv6-m)。您可以按照 ST 文档查看它与 arm 网站 infocenter.arm.com 上的技术参考手册的 cortex-m 核心,并在其中说明了哪种架构(armv6-m armv7-m armv8-m ...)和在那里你可以了解指令集和架构。没有最低限度的文件,您不应该开始这段旅程。ARM TRM 和 ARM ARM 用于内核和架构。和 ST 的参考手册(不是他们的程序员手册)和 ST 的数据表。

cortex-ms 从向量表启动,在架构参考手册 (ARM) 中有描述。第一个字被加载到堆栈指针中,第二个字是复位向量,它被定义为要求 lsbit 为 1(表示这是一个 thumb 函数地址)。你可以阅读其余的内容。做一个足够好的最小例子。

我使用过的所有 STM32 芯片(我使用过很多)都支持基于 0x08000000 的用户闪存和 0x20000000 的 SRAM,一些较新的 nucleo 板附带的固件将坚持正确的 0x08000000 地址在向量表(一小部分也支持更快的内存地址 0x00200000)。ARM 文档基本上会说 0x00000000 或表示 VTOR 事物,但实际上它通常是 0x00000000 作为逻辑在复位时查找向量表的地址。给这只猫剥皮的各种方法,但 ST 选择将一定百分比的 flash 镜像到 0x00000000。

因此,一个非常简单的示例可以帮助您入门。

引导程序,flash.s

.thumb

.thumb_func
.global _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop

.thumb_func
reset:
    bl notmain
    b loop
.thumb_func
loop:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

主/入口 C 代码 notmain.c

extern void bounce ( unsigned int );
unsigned int x;
void notmain ( void )
{   
    unsigned int ra;
    
    x=5;
    for(ra=0;;ra++) bounce(ra);
}

包括 gnu 工具在内的一些工具会看到 main() 关键字并将某些内容添加到二进制文件中,这有时是我们不想要的。有些工具链比其他工具链差。这是一个简单的解决方案来打败它。YMMV。

链接描述文件 flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
    .bss    : { *(.bss*)    } > ram
}

虽然链接器中存在命令行 -Ttext= 和 -Tdata= 等,但我建议不要使用它们,除非在懒惰时偶尔会出现 Stack Overflow 答案。命令行选项有一些令人不快的问题,包括一个长期存在的错误,他们仍然没有修复(这些工具神奇地与错误的结果一起工作,所以我猜没人关心)。

建造

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy -O binary notmain.elf notmain.bin

现在我的代码被设计为不关心 arm-none-eabi- 与 arm-linux-gnueabi- 之类的东西,过去 10 年或 15 年的 arm-whatever-whatever 应该可以工作。我只是将编译器用作编译器,将链接器用作链接器。尽可能接近零的工具链特定魔法。

始终检查输出以查看向量表是否看起来不错 notmain.list (我使用了反汇编程序,例如该工具尝试反汇编向量,只需忽略那里的反汇编)。

notmain.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000011    stmdaeq r0, {r0, r4}
 8000008:   08000017    stmdaeq r0, {r0, r1, r2, r4}
 800000c:   08000017    stmdaeq r0, {r0, r1, r2, r4}

08000010 <reset>:
 8000010:   f000 f804   bl  800001c <notmain>
 8000014:   e7ff        b.n 8000016 <loop>

08000016 <loop>:
 8000016:   e7fe        b.n 8000016 <loop>

08000018 <bounce>:
 8000018:   4770        bx  lr
    ...

0800001c <notmain>:
 800001c:   2205        movs    r2, #5
 800001e:   b510        push    {r4, lr}
 8000020:   2400        movs    r4, #0
 8000022:   4b03        ldr r3, [pc, #12]   ; (8000030 <notmain+0x14>)
 8000024:   601a        str r2, [r3, #0]
 8000026:   0020        movs    r0, r4
 8000028:   f7ff fff6   bl  8000018 <bounce>
 800002c:   3401        adds    r4, #1
 800002e:   e7fa        b.n 8000026 <notmain+0xa>
 8000030:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <x>:
20000000:   00000000    andeq   r0, r0, r0

地址空间是正确的 0x08000000

08000000 <_start>:
 8000000:   20001000
 8000004:   08000011
 8000008:   08000017
 800000c:   08000017

向量的地址是地址 ORRED 与 1,这是正确的。现在并不是所有的 STM32 都有 0x1000 字节的内存,所以有时你把它做得更小。

仅当您不依赖 .data 被初始化且 .bss 被归零时,最小链接器脚本和最小引导程序才有效。

08000010 <reset>:
 8000010:   f000 f804   bl  800001c <notmain>
 8000014:   e7ff        b.n 8000016 <loop>

对于这个例子,它工作得很好。就像其他人一样,您可以随心所欲地将链接描述文件过度复杂化。或者你可以保持简单。作为裸机,您已经想要牺牲 C 库,那么如果您必须初始化全局变量运行时怎么办?在写入之前不应该读取内存,因此 .bss 也不需要为零。

可以使用此解决方案来解决您第一次闪烁 LED 程序(裸机的你好世界)的微不足道的延迟。

for(ra=0;;ra++) bounce(ra);

因为反弹不在 notmain.c 的优化域中,编译器不会尝试将其作为死代码删除

for(ra=0;ra<100000;ra++) continue;

如果试图以这种方式进行延迟。在您最初闪烁后,led 开始以 POLLING 方式使用定时器,在精通处理器、芯片和外围设备并弄清楚中断如何以轮询方式为该芯片工作之前,不要靠近中断。如果您不这样做,那么我们将在 SO 再次遇到您的问题/问题。

如果您实际查看 CMSIS 标头,它们会非常难看,并且 CMSIS 部分采用了统一语法,因为 ARM 的另一个好主意已经坏了。抓住机会,就像使用他们的图书馆抓住机会一样。(只要看看源代码,你就会明白我的意思)。

NUCLEO 板是一个很好的起点,因为在目标 mcu 中“编程”闪存所需要做的就是复制 notmain.bin 二进制文件。cp notmain.bin /media/user/something 和调试 mcu 创建虚拟拇指驱动器,当您插入电路板时,您会看到安装的,获取文件并通过 SWD 对目标 mcu 进行编程并为您释放重置。

然后你可以自由地使用 openocd 和 flash write_image erase notmain.elf 在目标 mcu 或 GDB 中写入 flash,如果你敢的话,等等。但是简单的拖放或命令行复制将起作用。

我发现在编码会话的过程中,有时写入不起作用,设备已满或类似的事情。拔下并重新插入 NUCLEO 板,这将修复它。

这些天很难找到,所以从 st 中寻找 stsw-link007,一个版本会从 ST 中提取自己的更新。对于每块新的 NUCLEO 板,我都会在板上进行固件更新,因为我运行 Linux,并且有 NUCLEO 调试器固件和 Linux 的组合,这使得虚拟驱动器的安装不太可靠,您可能会在编程时受到打击,然后不得不重启再试一次。它是一个 JAVA 程序,因此可以在 Windows、Linux 或 Mac 上运行。

尝试先让 LED 闪烁,然后从那里开始,需要为特定的 gpio 块(GPIOA、B、C,NUCLEO 板文档中提到的任何内容)启用时钟,然后将该引脚设为输出并使用 BSRR 设置/重置每个之间都有延迟。越过你正在路上的那条路。

您将找到的大多数示例都是基于库的,并且 ST 提供的库不止一种。在某些情况下,板子可以在 Arduino 世界中工作,并且 nucleos 可能适合 mbed 世界,因此如果您不想执行上述操作,则可以在该沙盒中进行游戏。但在大多数情况下,外围设备都非常易于使用,ST 的文档更好,不是最好的,也不是最差的,但比大多数更好。ARM 文档也是如此。定期尝试所有这些,以便您知道那里有什么,然后定期选择您想要做项目的那个。这些事情会不断发展(而不是自己滚动,这总是一个已知的数量),所以看看他们能提供什么.

gnu 工具比 llvm 工具更稳定,但两者工作得一样好,gnu 的编译器从大约 4.xx 到现在随着时间的推移变得更糟,如果你认为即使 3.xx llvm 会做得更好但仍然保持同等水平或可悲的是,有时会产生较慢的代码。clang 和其他工具的命令行选项几乎在每个主要版本中都发生了变化,因此尝试使用 llvm 工具维护您的 makefile 是一场噩梦,因此我多次放弃对它们的支持。

我现在为目标 gnu 样式构建 llvm,这样我就不需要那些命令行选项并且效果更好,gcc 在最近的几个主要修订版中越来越草率,在那里留下了通常会优化的指令出去。两者都有效。请注意,使用 clang 编译但使用 binutils 来组装和链接并不罕见,如果您为目标构建 llvm(例如 armv6m),那么您也可以构建一个交叉链接器并使用它。遗憾的是,他们没有汇编器,因此您必须将编译器用作汇编器。

如果二进制文件未编程到目标 mcu 中,NUCLEO 上的虚拟驱动器将放置一个 FAIL.TXT 文件,其中没有任何有用信息。将 0x00000000 用于向量表之类的事情就可以做到这一点。除其他外,您只需要调试您的代码/构建。此外,在检查列表文件时,如果您使用的是 cortex-m0 或 -m0+ 或 cortex-m8 的基本风格(不支持 thumb2 扩展的 Lions 共享的那些),那么您会出错。因此,在将项目从 cortex-m4 或 -m7 转移到 cortex-m0 作为骨架起点时要小心,记住通常以 m0 为目标或始终以 m0 为目标,然后利用额外的 thumb2 扩展,如果你代码有特定的性能问题。


推荐阅读