tes*_*123 2 porting arm makefile stm32 stm32f4discovery
总的来说,我对 MCU 的 ST 系列的语言/行话/术语有点困惑,我认为这阻碍了我的进步。
一点背景:我是一名 EE,通过使用 AVR 平台的大学必修课程了解了我对 FW 的所有了解。喜欢它,非常简单且易于使用。快速浏览一下,通过独一无二的数据表,并一鼓作气你抽象掉!宏的、磅定义等...就是这么简单!你编写一个main.c和一个 Makefile,编译并用 avr-dude 启动......生活是美好的。
然后,我走进了STM32 ARM内核的脱粒机,天啊……STDPerifieral Libraries、HAL层、CubeMX、程序集启动文件、CMSIS、IDE特定项目文件,还有十几种不同类型的只是STM32F4.. .我已经快不知所措了!
我真正的问题是我有这个 STM32F411 探索板,我需要运行一个 20x4 字符 LCD。我在 github 等上找到了几个看起来不错的项目:
https://stm32f4-discovery.net/2014/06/library-16-interface-hd44780-lcd-controller-with-stm32f4/
https://github.com/EarToEarOak/STM32F4-HD44780
https://github.com/6thimage/hd44780
https://github.com/mirkoggl/STM32F4_LiquidCrystal
但是,我无法弄清楚如何正确编译其中的任何一个。我一直在使用 CubeMx 来生成 Makefile 作为起点,主要是因为它制作了一个链接器脚本和我显然需要的 .s 文件,但我完全没有经验或想法如何自己做到这一点。这就是我对 CubeMX 的依赖。
我认为我的大部分问题是,通过尝试将 CubeMX 生成的 MCU 特定代码和各种在线项目代码粘合在一起,我遇到了很多冲突......在线项目正在调用一些 .h 文件或引用某些东西Makefile 找不到,这就是事情出轨的地方......例如,其中一些项目正在调用该stm32f4xx.h文件,但 CubeMX 并未将其包含在其生成的代码中。
我不得不去系统中的其他地方并将该文件复制到文件./Inc夹中,但这似乎真的倒退了......而且,它仍然没有构建。
如果我要在这里保持头脑清醒,我需要一个 AVR 到 STM32 12 步康复程序或其他东西......
所以,最大的问题是:
你们如何从第一个项目开始git clone xxxx并构建它以在 STM32F411 探索板上运行,arm-none-eabi并且仅使用Makefiles...不允许 IDE。
并且,开发 8 位 AVR 和 STM32F4 之间最重要的区别是什么?凭借有限的经验和我犯的大量新手错误,我认为最安全的方法是使用 CubeMX 制作一个项目,以便至少设置所有内容,然后仔细尝试手动添加源文件? 但这仍然不能解决我对使用 Unix 作为 IDE 时这些设备的正常工作流程应该是什么的根本误解。
谢谢!
所以一个非常广泛的问题。首先,最重要的是你的 avr 体验究竟是什么,很明显,如果你从 main.c 开始,那么其他人为你构建了你的沙箱/工具环境,包括引导程序。avr 比 arm 更像哈佛,所以实际上它更像是一个真正构建的 PITA,没有人为你做工作。
没有理由为什么体验不能完全相同。您可以阅读有关某些外围设备的寄存器的 avr 文档,查看程序中该外围设备的寄存器并使其工作。您可以使用 st 文档,阅读一些外围设备的寄存器,在程序中查看该外围设备的寄存器并使其工作。
你可以为你的 avr 使用一些像 arduino 这样的库,阅读 api 调用,编写一个程序来进行一些调用,对设备进行编程。可以为您的 st 芯片取一些库并做同样的事情,api 调用不会相同。arduino 库与由 atmel 或其他方制作的其他 avr 库不同的 api 调用。您可以访问 mbed.org 并开始为您的或某些 st 芯片编写 api 调用,然后生成一个工作二进制文件。
所有 mcu 芯片供应商都需要提供库,因为不是每个人都愿意或(所以他们认为)能够以自己的方式裸机(不是说库不是裸机,而只是像系统一样的 api 调用)。他们不会在这个时代生存。同样,您必须使用一些新名称获得更好的更新,以便库发生变化。ARM 很棒,但同时也是皇家 PITA。因为他们制造内核而不是芯片,并且他们的内核被不同的东西包围。有大量的 cortex-m4 解决方案使用相同的 cortex-m4 内核,但是您无法编写一个适用于所有这些内核的 cortex-m4 程序,因为芯片供应商特定的东西都是不同的(当然,如果-那么-如果你能让它适合,否则程序会起作用)。ARM 正在尝试制作一个看起来相同的魔法层,并且供应商被拖着走,所以这个 CMSIS 和 MBED 是 ARM 驱动的概念来解决这个问题。AVR没有这个问题,核心和芯片厂商是一回事。现在有许多不同的 AVR 内核,即使你有相同的外围设备,你也可能无法编写一个适用于所有这些内核的二进制文件,或者可能有一个 (xmega) 的二进制文件不能在另一个 (微小的) 因为 avr 核心实现的差异,即使外围设备相同。avr 问题虽然比 arm 问题小得多,但都包含在一个公司内。因此,如果外围设备不同,它们的变化几乎不会像 atmel、nxp、st、ti、其他使用相同 arm 内核(或至少相同名称的,
在像 ST 这样的公司中,他们创建了不同的外设,包括多个定时器、gpio、uart、pll/clock 等。从 cortex-m3 到 cortex-m0 的过渡他们开始使用不同的 gpio 外设,但也许其他一些是相同的。快进到今天,我们拥有仅来自 ST 的基于 cortex-m3、cortex-m0、cortex-m0+、cortex-m4 和 cortex-m7 的设备,并且有混合和匹配一条产品线的外设可能具有类似于早期的定时器cortex-m3 产品但 gpio 更像是第一个 cortex-m0 产品。他们似乎混合和匹配他们从外围设备池中创建的每个新系列。所以我可能有一个特定芯片的代码,可以在其他特定芯片上很好地工作,同一个家庭,甚至可能略有不同。但是将它移到另一个 st stm32 系列,也许 uart 代码可以工作,但 gpio 没有,将第一个程序移到另一个系列,也许 gpio 可以工作而 uart 不工作。一旦您为每个不同的外围设备拥有了自己的库,您就可以混合和匹配它,并且他们的库会尝试这样做,并且理想情况下使用通用的调用,这样代码端口会更好一些,但您必须为不同的芯片来连接不同的东西。不完美。
还要看看在atmega328p之前说非常流行的多亏了arduino和avr-freaks,那东西相对古老。在那之后,所有的 stm32 都被创建了,并且由于各种原因,大小/速度/内部政治/等不同的外围选择被创建。所有上面提到的 cortex-m 变体都是在 atmega328p 没有改变的时候用 mcu 世界中的不同目标用例创建的。
因此,如果您使用一个 avr 文档和一个 avr,并且有一个可以正常工作的工具链,那么您可以直接从 avr 文档中编写程序。如果您为一个 stm32 芯片使用一个 stm32 文档,请使用任何适用于 arm 的 gcc 交叉编译器(arm-none-eabi、arm-linux-gnueabi 等),执行您自己的 boostrap 和链接器脚本,这对于cortex-m,你可以直接从 stm32 文档和 arm 文档编写程序,没有问题,两者都写得很好。对不同的 stm32 芯片重复,对基于 cortex-m 的 nxp 芯片重复,对基于 cortex-m 的 ti 芯片重复。
作为一名程序员,尽管您想为两个芯片编写一个程序,但您需要查看这两个芯片,看看有什么共同点和不同之处,然后设计您的程序以避免差异或 if-then-else 或使用链接时间 if- then-else 解决方案。
我发现芯片供应商提供的库比阅读文档和编程寄存器更难使用。天啊。我建议您继续尝试使用他们的旧库和新库,并尝试直接访问寄存器并找到最适合您的库。手臂将以相似的价格、功率等围绕 avrs 运行,因此尝试使用这些其他部件是有价值的。avr、msp430 等也已成为基于 cortex-m 的产品,因此您应该专业地深入研究一个或多个。
例如,stm32f411 的简单 LED 闪光灯,在 pa5 上带有 LED
flash.s
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.align
.thumb_func
.globl PUT16
PUT16:
strh r1,[r0]
bx lr
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
.end
Run Code Online (Sandbox Code Playgroud)
不是main.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define RCCBASE 0x40023800
#define RCC_AHB1ENR (RCCBASE+0x30)
#define GPIOABASE 0x40020000
#define GPIOA_MODER (GPIOABASE+0x00)
#define GPIOA_OTYPER (GPIOABASE+0x04)
#define GPIOA_OSPEEDR (GPIOABASE+0x08)
#define GPIOA_PUPDR (GPIOABASE+0x0C)
#define GPIOA_BSRR (GPIOABASE+0x18)
#define STK_CSR 0xE000E010
#define STK_RVR 0xE000E014
#define STK_CVR 0xE000E018
static void led_init ( void )
{
unsigned int ra;
ra=GET32(RCC_AHB1ENR);
ra|=1<<0; //enable GPIOA
PUT32(RCC_AHB1ENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<10); //PA5
ra|=1<<10; //PA5
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=~(1<<5); //PA5
PUT32(GPIOA_OTYPER,ra);
ra=GET32(GPIOA_OSPEEDR);
ra|=3<<10; //PA5
PUT32(GPIOA_OSPEEDR,ra);
//pupdr
ra=GET32(GPIOA_PUPDR);
ra&=~(3<<10); //PA5
PUT32(GPIOA_PUPDR,ra);
}
static void led_on ( void )
{
PUT32(GPIOA_BSRR,((1<<5)<<0));
}
static void led_off ( void )
{
PUT32(GPIOA_BSRR,((1<<5)<<16));
}
void do_delay ( unsigned int sec )
{
unsigned int ra,rb,rc,rd;
rb=GET32(STK_CVR);
for(rd=0;rd<sec;)
{
ra=GET32(STK_CVR);
rc=(rb-ra)&0x00FFFFFF;
if(rc>=16000000)
{
rb=ra;
rd++;
}
}
}
int notmain ( void )
{
unsigned int rx;
led_init();
PUT32(STK_CSR,0x00000004);
PUT32(STK_RVR,0xFFFFFFFF);
PUT32(STK_CSR,0x00000005);
for(rx=0;rx<5;rx++)
{
led_on();
while(1) if(GET32(STK_CVR)&0x200000) break;
led_off();
while(1) if((GET32(STK_CVR)&0x200000)==0) break;
}
while(1)
{
led_on();
do_delay(10);
led_off();
do_delay(1);
}
return(0);
}
Run Code Online (Sandbox Code Playgroud)
闪存文件
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
Run Code Online (Sandbox Code Playgroud)
然后编译
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m4 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -mcpu=cortex-m4 -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
Run Code Online (Sandbox Code Playgroud)
可以替换为 arm-whatever-linux-gnueabi 并且仍将构建和运行。
有一些工具链在看到 main() 时会添加额外的东西,所以从历史上看,我个人避免这样做。我不在 mcus/etc 上使用 .data 从闪存启动,所以不必复制它(烧伤 .text 是),我不假设 .bss 为零我在使用它们之前初始化东西。所以我可以在我的引导程序上作弊,但是有很多稍微复杂的链接器脚本的例子,它们给你 bss 偏移量和大小以及 .data 偏移量和大小和目的地,如果你希望你的 C 更纯粹. 我也更喜欢控制用于加载和存储的指令,有些/许多人希望编译器选择正确,但已经看到失败。天啊。所以有很多个人风格,但是拿你的 stm32f11 文档看看那些地址和寄存器,即使你完全讨厌我的代码或风格,你仍然应该看到使用该外围设备是多么容易。同样,计时器在 arm 文档中。如今,作为其中的一员,一些供应商拥有自己的修改形式的 arm 文档版本,因此涵盖了许多 arm 信息,但仍然存在一些差距。
作为 arm 的一般规则,请从芯片供应商的文档中找出芯片中的 arm 核心。然后转到武器站点并找到该内核(在本例中为 cortex-m4)的技术参考手册 (TRM)。然后在该文档中 arm 提到架构 armv7-m 获取 armv7-m 的架构参考手册。这三个文档是您的主要信息来源,在您的第一个 cortex-m4 之后,您可能只需要 99% 的芯片供应商文档,当然在芯片供应商内。还要找到 cpuid 寄存器或芯片 id 或任何文档称它为的内容,并将其与您从芯片/臂核心读出的内容进行比较。有时会有不同版本的 arm 内核(r1p3 表示修订版 1. 3) 并且很少见,但在修订之间发生变化意味着对旧核心使用最新的文档可能会导致细微的差异。同样,基于 arm 和 arm 的芯片比基于 atmel avr 的芯片改进/变化得更快。在第一次或第二次之后,你就掌握了它的窍门......
例如,如果您打算使用 PIC32,那么 pic32 文档将是一个类似的故事微芯片,然后转到核心文档的 mips(然后希望该微芯片记录的外围设备甚至是 atmel 的一半(他们现在自己)、ti、st、nxp 等)。另一种混合购买处理器内核并将我自己的东西包裹起来。pic32s 以这种方式编程很痛苦,真的需要埋在微芯片工具链中的库,并使用更多的力量等等。没有理由基于 mips 的不应该能够与基于 arm 的竞争,但他们不......mips 或芯片供应商或组合在那里有过错。