这是一个非常广泛的问题。首先,您的 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 程序,因为芯片供应商特定的东西都是不同的(肯定会有大量的 if-then-否则程序会工作,如果你能让它适应)。 ARM 正在尝试制作一个看起来相同的魔法层,并且供应商被拖着走,所以这个 CMSIS 和 MBED 是 ARM 驱动的概念来解决这个问题。 AVR没有这个问题,核心和芯片厂商是一回事。现在有许多不同的 AVR 内核,即使您有相同的外围设备,您也可能无法编写一个适用于所有内核的二进制文件,或者可能有一个 (xmega) 的二进制文件不适用于另一个 (tiny ) 因为即使外围设备相同,avr 核心实现也存在差异。 avr 问题虽然比 arm 问题小得多,但都包含在一家公司中。因此,如果外围设备有所不同,它们的变化不会像 atmel vs nxp vs st vs ti vs 使用相同 arm 内核的其他设备(或至少同名,arm 源代码中的项目易于修改,与或者没有带有 16 位提取或 32 位提取等的浮点,记录在该内核的 trm 中,从而产生更多痛苦)。
在像 ST 这样的公司中,他们仅在 cortex-ms 上创建了不同的外围设备,包括许多计时器、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 都被创建了,并且由于各种原因,大小/速度/内部政治/等不同的外围设备选择被创建。上面提到的所有 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 解决方案。
我发现芯片供应商提供的库比仅仅阅读文档和编程寄存器更难使用。 YMMV。我建议您继续尝试使用他们的旧库和新库,并尝试直接访问寄存器并找到最适合您的库。这些手臂将以相似的价格、功率等围绕 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
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 为零我在使用它们之前对其进行了初始化。所以我可以在我的引导程序上作弊,但是有很多稍微复杂一点的链接器脚本的例子,如果你想让你的 C 更纯粹.我也更喜欢控制用于加载和存储的指令,一些/很多希望编译器正确选择,已经看到失败。 YMMV。所以有很多个人风格,但是拿你的 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 等)。另一种混合购买处理器内核并将我自己的东西包裹在它周围。 pic32 以这种方式编程很痛苦,真的需要埋在微芯片工具链中的库,并使用更多的功能等等。没有理由基于 mips 不应该能够与基于 arm 的竞争,但他们不...... mips 或芯片供应商或组合存在错误。