首页 文章

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

提问于
浏览
2

我对ST系列MCU的语言/术语/术语有点困惑,我认为这阻碍了我的进步 .

一点背景:我是一名EE,通过使用AVR平台的必修大学课程,学到了我对FW的全部了解 . 喜欢它,非常简单易用 . 快速浏览唯一的数据表,然后敲打你're abstracting away! Macro',pound定义等等......真是太简单了!你写了一个 main.c 和一个Makefile,用avr-dude编译和解雇......生活是美好的 .

然后,我走进了STM32 ARM核心的脱粒机,并且神圣抽烟...... STDPerifieral Libraries,HAL layer,CubeMX,汇编启动文件,CMSIS,IDE特定项目文件以及14种不同类型的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可以't find, and that',其中的东西是铁轨....例如,其中一些项目调用 stm32f4xx.h 文件,但CubeMX没有生成代码 .

我不得不去系统的其他地方并将该文件复制到 ./Inc 文件夹,但这似乎真的倒退了...而且,它仍然没有构建 .

如果我要在这里保持水头,我需要AVR到STM32 12步康复计划或者其他什么......

So, the big question is:

你们如何从第一个 git clone xxxx 中获取这些项目中的一个并将其构建为仅使用 arm-none-eabi 和Makefiles在STM32F411发现板上运行...不允许使用IDE .

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

谢谢!

1 回答

  • 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那样使用相同的臂芯(或至少相同的名称,臂部源中的项目很容易修改,与或没有浮点,16位读取或32位读取等,记录在该核心的trm中,造成更多痛苦) .

    在像ST这样的公司内,他们已经创建了不同的外围设备,如gpio,uart,pll / clock等 . 如果你拿裸机,与寄存器交谈没有其他库接近,你会看到从皮质m3到皮质m0的过渡他们开始使用不同的gpio外周,但也许其他一些是相同的 . 快进到今天我们只有来自ST的cortex-m3,cortex-m0,cortex-m0,cortex-m4和cortex-m7设备,并且有一个混合和匹配一个产品线的外围设备可能有一个类似于早期的计时器cortex-m3产品,但gpio更像是第一个cortex-m0产品 . 他们似乎将他们创建的每个新家庭与外围设备混合在一起 . 所以我可能拥有一个特定芯片的代码,这个芯片在其他特定芯片上工作得很好,同一个家族甚至可能有点不同 . 但是把它转移到另一个st stm32家族,也许uart代码可以工作,但gpio没有,拿第一个程序并将它移动到另一个家庭,也许gpio工作,而uart没有 . 一旦你为每个不同的外围设备 Build 了自己的库,你就可以混合使用它们,并且它们的库会尝试这样做,并且使用普通的理想调用,这样代码端口会更好一点,但是你必须构建不同的芯片可以将不同的东西连接起来 . 不完美 .

    另外看看多大年纪以来非常受欢迎的感谢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或使用链接时间如果 - 然后 - 否则解决方案 .

    我发现芯片供应商提供的库比仅仅阅读文档和编程寄存器更难 . 因人而异 . 我建议你继续尝试使用旧的和新的库,并尝试直接进入寄存器,找到最适合你的东西 . 这些武器会以相似的价格,动力等方式绕着avrs绕圈跑,所以试图使用这些其他部件是有 Value 的 . 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对抗较旧的核心可能会导致细微的差异 . 再次基于臂和臂的芯片正在改进/改变比基于atmel avr的芯片更快 . 在第一个或第二个之后,你得到它的悬念......

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

相关问题