如何将项目从一个STMFx系列移植到同一系列中的另一个STMFx系列

时间:2017-08-23 21:02:52

标签: makefile arm stm32 porting stm32f4discovery

我对ST系列MCU的语言/术语/术语感到有些困惑,我认为这阻碍了我的进步。

一点背景:我是一名通过使用AVR平台的必修大学课程学习了解FW的所有知识。喜欢它,非常简单易用。 快速查看唯一的数据表,然后 bang 即可摘要! Macro's,pound定义等等......真是太简单了!你写了一个main.c和一个Makefile,用avr-dude编译和解雇......生活很美好。

然后,我走进了STM32 ARM核心的脱粒机,并且神圣抽烟...... STDPerifieral Libraries,HAL layer,CubeMX,程序集启动文件,CMSIS,IDE特定项目文件以及十几种不同类型的STM32F4 ......我很快就被淹没了!

我真正的问题是我有这个STM32F411发现板,我需要运行20x4字符LCD。我在github上找到了几个不错的项目:

https://stm32f4-discovery.net/2014/06/library-16-interfacing-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中选择其中一个项目,然后使用arm-none-eabi和仅限Makefiles在STM32F411发现板上构建它...不允许使用IDE。

并且,在开发8位AVR和STM32F4之间需要了解哪些最重要的差异? 凭借有限的经验和我正在制作的新手错误的数量,我认为最安全的方法是使用CubeMX创建一个项目,以便至少设置所有内容,然后仔细尝试手动添加源文件?但是,如果我使用Unix作为IDE,那仍然无法解决我对这些设备的理智工作流程的误解。

谢谢!

1 个答案:

答案 0 :(得分:4)

这是一个非常广泛的问题。首先,请确定您的avr体验到底是什么,如果您从main.c开始,那么其他人就会为您构建您的沙箱/工具环境,包括引导程序。 avr比手臂更哈佛,所以实际上它更像是一个没有人为你工作而真正构建的PITA。

没有理由说经验不能完全相同。您可以在程序中的外设寄存器中读取有关外设程序寄存​​器的avr文档,并使其正常工作。您可以获取st文档,阅读某些外设的寄存器,查看程序中该外设的寄存器并使其正常工作。

你可以为你的avr带一些类似arduino的库,阅读有关api调用的内容,编写一个可以进行调用的程序,对设备进行编程。可以为你的st芯片带一些库并做同样的事情,api调用不会是一样的。 arduino库与atmel或其他方制作的其他avr库不同。你可以跳到mbed.org上,开始为你的或者一些st筹码编写api调用,然后编写一个有效的二进制文件。

所有mcu芯片供应商都需要提供库,因为不是每个人都愿意或(因此他们认为)能够通过它们(不是库不是裸机,而只是api调用,所以系统就像)。他们不会在这个时代生存。同样,你必须更新一些新名称,以便更改库。 ARM很精彩,但同时也是皇家PITA。因为它们使核心不是芯片,并且它们的核心被不同的东西所包围。有大量的cortex-m4解决方案使用相同的cortex-m4核心,但你不能编写一个适用于所有这些的cortex-m4程序,因为芯片供应商特定的东西都是不同的(肯定是一个巨大的if-then-然后 - 否则程序会起作用,如果你能让它适合)。 ARM正在尝试创建一个看起来相同的魔术层,供应商正在被拖延,因此这个CMSIS和MBED是ARM驱动的概念来解决这个问题。 AVR没有这个问题,核心和芯片供应商是同一个。现在有许多不同的AVR内核,即使你有相同的外设,你可能也无法编写一个适用于所有它们的二进制文件,或者可能有一个二进制文件(xmega)不能在另一个上运行(微小的) )因为avr核心实现的差异,即使外围设备是相同的。虽然avr问题比手臂问题小得多,但都包含在一家公司内。所以外围设备如果变化的程度几乎不会与atmel vs nxp vs st vs ti相比,使用相同的arm核心(或者至少相同的名称,arm源中的项目很容易修改,与或者没有浮点数,16位读取或32位读取等,记录在该核心的trm中,造成更多痛苦)。

在像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没有。一旦你为每个不同的外围设备建立了一个自己的库,你就可以混合使用它们并且它们的库会尝试这样做,并且使用一个普通的理想调用,这样代码端口会更好一点,但你必须为不同的芯片可以将不同的东西联系起来。不完美。

另外看看多大年纪以来,非常受欢迎的感谢arduino和也许avr-freaks之前atmega328p这个东西比较古老。在那之后,所有的stm32都被创建了,并且由于各种原因,大小/速度/内部政治/等创建了不同的外围选择。在atmega328p没有改变的时间内,所有上述皮质m变异都是在mcu世界中使用不同的目标用例创建的。

因此,如果您使用一个avr文档和一个avr,并且有一个有点工作的工具链,您可以直接从avr文档编写程序。如果您为一个stm32芯片获取一个stm32文档,请使用几乎任何gcc交叉编译器的arm(arm-none-eabi,arm-linux-gnueabi等),执行自己的boostrap和链接器脚本,这些都非常简单。在cortex-m中,你可以直接从stm32文档和arm文档编写程序,没有任何问题都写得有点好。重复一个不同的stm32芯片,重复基于皮质m的nxp芯片,重复基于皮质m的芯片。

作为一名程序员,虽然你想为两个芯片编写一个程序,你需要查看两个芯片,看看常见的和不同的是什么,并设计你的程序,以避免差异或if-then-else或使用链接时间if-then-else解决方案。

我发现芯片厂商的库比阅读文档和编程寄存器更难。因人而异。我建议你继续尝试使用旧的和新的库,并尝试直接进入寄存器,找到最适合你的。这些武器将以相似的价格,动力等方式绕着avrs绕圈跑,因此试图使用这些其他部件是有价值的。 avr,msp430等已经成为基于cortex-m的产品,所以你应该专业地研究一种或多种产品。

所以例如stm32f411的一个简单的led闪光灯,带有一个led的pa5

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

notmain.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);
}

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

然后编译

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

可以替换为arm-whatever-linux-gnueabi,仍然可以构建并运行。

有一些工具链在他们看到main()时添加了额外的东西,所以历史上我个人避免这样做。我没有在mcus / etc上使用.data启动闪存,所以不必复制它(烧伤.text是),我不认为.bss为零我在使用之前初始化东西。所以我可以欺骗我的引导程序,但是有许多稍微复杂的链接器脚本的例子可以为你提供bss偏移量和大小以及.data偏移量和大小和目标,如果你希望你的C更加纯净。我也更喜欢控制用于加载和存储的指令,有些/很多希望编译器选择正确,看到失败。因人而异。所以有很多个人风格,但是拿你的stm32f11文档看看那些地址和寄存器,即使你完全讨厌我的代码或风格,你仍然应该看到使用该外设是多么容易。同样,计时器也在手臂文档中。这些天他们中的一个是一些供应商以修改的形式拥有他们自己的arm文档版本,因此涵盖了大部分的手臂信息,但仍然存在一些差距。

作为一般规则,从芯片供应商的文档中了解您的芯片中有哪些臂芯。然后去武器站点找到该核心的技术参考手册(TRM)(本例中为cortex-m4)。然后在那份文件中提到armv7-m是armv7-m的架构参考手册。这三个文档是您的主要信息来源,在您的第一个cortex-m4之后,您可能只需要99%的芯片供应商文档,当然在芯片供应商中。还可以找到cpuid寄存器或芯片ID或doc调用它的任何内容,并将其与您从芯片/ arm核心读出的内容进行比较。有时候有不同版本的arm核心(r1p3意思是修订版1.3)并且很少见,但是在修订之间发生变化意味着使用最新的doc来对抗较旧的核心可能会导致细微的差异。再次基于臂和臂的芯片正在改进/改变比基于avmel avr的芯片更快。在第一个或第二个之后你就可以了解它......

如果您打算使用PIC32,那么对于pic32文档来说它将是一个类似的故事微芯片,然后是核心文档的mips(然后希望微芯片记录他们的外围设备甚至是atmel的一半)(他们现在拥有),ti,st,nxp等)。另一种混合购买处理器核心并包裹我自己的东西。 pic32s以这种方式编程是痛苦的,真的需要埋藏在微芯片工具链中的库,并使用更多的功率,等等没有理由为什么基于mips不能与基于手臂的竞争,但他们不... mips或芯片供应商或其组合在那里有过错。