arm-none-eabi-gcc with nucleo L432KC 开发板

arm-none-eabi-gcc with nucleo L432KC board

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

中使用的程序一样

我有点喜欢使用 vim 和 gdb 调试器,我很高兴为我的 avr atmega328p 使用 avr-gcc 和 avra 进行组装一段时间但现在我想继续并深入研究嵌入式系统,所以我买了我的 nucleo 开发板 Documentation Page

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

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

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

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

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

Bootstrap、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

Main/entry 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.

linker 脚本 flash.ld

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

虽然命令行 -Ttext= 和 -Tdata= 等出现在 linker 中,但我建议不要使用它们,除非懒惰时偶尔出现 Stack Overflow 答案。命令行选项有一些令人不快的问题,包括一个非常 long-standing 的错误,他们仍然没有修复(这些工具被神奇地组合在一起以处理错误的结果,所以我猜没人在乎)。

建设

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- 这样的事情,arm-whatever-whatever 的最后 10 或 15 年应该可以工作。我只是将编译器用作编译器,将 linker 用作 linker。尽可能接近零工具链特定魔力。

始终检查输出,看看如果没有别的,向量 table 看起来不错 notmain.list(我使用了 disassembler,例如该工具试图 disassemble 向量,忽略那里的反汇编)。

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

地址space对0x08000000

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

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

minimal-ish linker 脚本和最小的 bootstrap 仅在您不依赖初始化 .data 和清零 .bss 时才有效。

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

对于这个例子来说效果很好。就像其他人一样,您可以随心所欲地使 linker 脚本过于复杂。或者你可以保持简单。作为 bare-metal 你已经想要牺牲 C 库了,那么如果你必须初始化你的全局变量 运行 时间怎么办?在写入内存之前不应该读取内存,因此 .bss 也不需要为零。

可以将此解决方案用于您的第一个 blink led 程序(bare-metal 的 hello world)的微不足道的延迟。

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

因为反弹不在 notmain.c 的优化域中,编译器不会像

那样尝试将其作为死代码删除
for(ra=0;ra<100000;ra++) continue;

如果尝试以这种方式进行延迟,会的。在你最初的 blink 之后,led 开始以轮询方式使用定时器,在精通处理器、芯片和外围设备之前不要接近中断,并且已经弄清楚中断是如何以轮询方式为该芯片工作的.如果你不这样做,那么我们将再次与你在这里 questions/problems。

CMSIS headers 如果您实际查看它们,它是相当丑陋的,并且 CMSIS 在某种程度上具有统一的语法,因为 ARM 的另一个伟大创意变坏了。抓住机会,就像您使用他们的图书馆抓住机会一样。 (看看源码就明白我的意思了)

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

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

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

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

先尝试 blink 一个 led,然后从那里开始,需要为特定的 gpio 块启用时钟(GPIOA、B、C,无论 NUCLEO 板文档中注明什么),然后制作固定一个输出并使用 BSRR 到 set/reset,每个之间有一个延迟。越过你正在路上的那个。

您会发现大多数示例都是基于库的,并且 ST 的库有不止一种风格。在某些情况下,一块电路板可以在 Arduino 世界中工作,而 nucleos 可能适合 mbed 世界,因此如果您不想做上述事情,您可以在那个沙盒中玩。但在大多数情况下,外围设备非常易于使用,ST 的文档更好,不是最好也不是最差,但比大多数更好。 ARMs 文档也是如此。定期尝试所有这些,以便您知道那里有什么,然后定期选择您想要在其中进行项目的一个。这些东西在发展(当然不是自己推出,这总是一个已知的数量),所以看看他们必须提供什么.

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

我现在为目标 gnu 风格构建 llvm,这样我就不需要那些命令行选项并且做得更好,gcc 在最近的几个主要修订版中越来越草率,在那里留下了说明通常会被优化掉。两者都有效。请注意,使用 clang 进行编译并不罕见,但 binutils 用于 assemble 和 link,如果您为目标构建 llvm(例如 armv6m),那么您可以构建一个交叉 linker 作为好并使用它。遗憾的是他们没有 assembler 所以你必须使用编译器作为 assembler.

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