PIC32 vs dsPIC vs ARM与AVR,无论如何,当我们使用C语言编程时,体系结构是否重要?[关闭]


10

我们目前正在使用32位PIC32单片机。它可以很好地满足我们的需求,但是我们也在探索其他微控制器,这些微控制器可以更好地适应我们的需求+我们还有其他项目正在选择MCU。为了这个目的,我们选择了基于ARM的SAM DA微控制器,它是相同的32位但基于ARM(在工业上比PIC32更流行)。

现在,对于PIC32,我们使用MPLAB,而对于ARM cortex-M0,我们将使用Atmel Studio。我们将在两个平台中使用C语言。我担心的是,我们将使用两个(来自同一公司的)32位微控制器,但它们的体系结构不同。这将要求我们学习两种不同的设备,并会增加“学习曲线” +交付时间。但是另一方面,我也认为,既然我们将在两种情况下都使用C语言,那么ARM的学习曲线就不应该被人听到,也值得探索该处理器。

我的主要问题是,当我们使用C语言编程时,该体系结构有多大的差异,因为它提供了micrcontroller内部的抽象。什么是MPLAP和爱特梅尔Studio中的主要区别,考虑到C语言编程。


2
如果一切都在使用PIC32,那么切换的意义何在?即使代码完全移植(不会),仍然有新的工具链和IDE可以适应。重点是什么?出于宗教原因而进行切换或切换为“基于ARM”(或其他基于任何事物)的做法很愚蠢。您需要有充分的理由,但是您还没有给我们看。
奥林·拉斯罗普

我没有询问切换。我谈到要为其他项目选择不同的体系结构,因为我们正在处理多个项目,而且我们现有设计还有改进的余地。重点是学习曲线和同时使用两种不同架构的挑战。
工程师

我确实发现Atmel Studio提供了比MPLAB youtube视频更
工程师

Answers:


20

这是一个很自以为是的话题。我可以为自己说话(AVR,ARM,MSP430)。

差异1(最显着)在外围设备中。每个MCU都具有相似的UART,SPI,定时器等-只是寄存器名称和位不同。大多数时候,这是我在芯片之间移动代码时必须处理的主要问题。解决方案:使用通用API编写驱动程序,因此您的应用程序可以移植。

差异2是内存架构。如果要在AVR的Flash中放置常量,则必须使用特殊的属性和特殊的函数来读取它们。在ARM世界中,您只需要取消引用指针,因为只有一个地址空间(我不知道有多少PIC处理它,但是会假定它们更接近AVR)。

差异3是中断声明和处理。avr-gccISR()宏。ARM仅具有一个函数名(如someUART_Handler()-如果使用CMSIS标头和启动代码)。ARM中断向量可以放置在任何地方(包括RAM)并在运行时进行修改(例如,如果您有两个可以交换的不同UART协议,则非常方便)。AVR只能在“主闪存”或“引导加载程序部分”中使用向量(因此,如果要以不同方式处理中断,则必须使用一条if语句)。

差异4-睡眠模式和电源控制。如果需要最低功耗,则必须充分利用MCU的所有功能。MCU之间的差异可能很大-有些具有更粗略的省电模式,有些可以启用/禁用各个外设。有些MCU具有可调节的调节器,因此您可以在较低的电压下以较低的速度运行它们,等等。我看不到一种简单的方法来在具有3种全局电源模式,另一种具有7种电源模式和独立的外设时钟控制。

关心可移植性时,最重要的一件事情就是将代码清楚地划分为硬件相关的(驱动程序)和硬件无关的(应用程序)部分。您可以使用模拟驱动程序(例如,控制台而不是UART)在普通PC上开发和测试后者。在原型硬件从回流炉中出来之前,这节省了我很多时间,因为90%的应用程序代码已完成:)

在我看来,ARM的好处是“单一文化”-许多编译器(gcc,Keil,IAR等),许多免费且官方支持的IDE(至少适用于NXP,STM32,Silicon Labs, Nordic),许多调试工具(SEGGER-尤其是Ozone,ULINK,OpenOCD ...)和许多芯片供应商(我什至不会开始命名它们)。PIC32主要限于Microchip(但只有在您不喜欢他们的工具时,才有意义。

说到C代码。它是99%相同,一条if语句是相同的,循环以相同的方式工作。但是,您应该注意本机字的大小。例如,for如果将AVR uint8_t用于计数器,则AVR上的循环最快,而ARM uint32_t(或int32_t)上的循环最快。如果使用较小的类型,ARM每次都必须检查8位溢出。

通常,选择MCU和/或供应商主要是出于政治和后勤考虑(除非您有非常明确的工程约束,例如:高温-使用MSP430或Vorago)。即使该应用程序可以在任何东西上运行,并且在产品生命周期内仅需要开发和支持 5%的代码(驱动程序),这对于公司来说仍然是一笔额外的费用。我工作过的所有地方都有喜欢的供应商和MCU系列(例如“除非有充分的理由选择其他东西,否则请选择任何您想要的Kinetis”)。如果您有其他人寻求帮助,它也会有所帮助,因此,作为一名经理,我可以避免拥有5个人的开发部门,而每个人都使用完全不同的芯片。


3
“如果将uint8_t用作计数器,则AVR最快,而在ARM上,uint32_t是最快的类型(或int32_t)。如果使用较小的类型,ARM每次都必须检查8位溢出。” 如果仅需要至少8位,则可以使用uint_fast8_t。
迈克尔(Michael)

@Michael-确保可以使用_fast类型,但不能指望溢出行为。在我的gcc的stdint.h中,我有“ typedef unsigned int uint_fast8_t”,它基本上是一个uint32_t :)
filo

鉴于不同的平台具有不同的功能,尝试编写高效,通用和完整的API十分困难。CPU可能不如外设及其决定的设计要重要。例如,某些设备允许最多在几微秒内的任何时间重新配置各种外围设备,而另一些设备可能需要分布在数百微秒甚至几毫秒内的多个步骤。用于前一种模式的API函数可能在以10,000Hz运行的中断服务例程中可用,但是...
supercat

...无法在需要将操作分散到数百微秒的平台上使用。我不知道为什么硬件设计人员似乎不尽力支持“随时快速操作” API语义,但是许多人使用同步单个操作而不是陈述状态的模型,例如,如果已向打开设备,代码意识到不需要打开设备,代码必须等待设备打开才能发出关闭请求。在API中平稳地处理会增加主要的复杂性。
超级猫

11

我使用了来自四个不同制造商的几种MCU。每次的主要工作是再次熟悉外围设备。

例如,UART本身并不太复杂,我很容易找到驱动程序端口。但是上一次我花了将近一天的时间来整理时钟,I / O引脚中断,启用等。

GPIO可能非常复杂。位设置,位清除,位切换,特殊功能启用/禁用,三态。接下来,您将得到中断:任意边沿,上升沿,下降沿,低电平,高电平,是否自清除。

然后是I2C,SPI,PWM,定时器和另外二十多种外设,每种外设都有自己的时钟使能,并且每次寄存器有新位时都不同。对于所有这些,需要花费大量时间阅读数据表,以了解如何在何种情况下设置哪一位。

上一个制造商有很多代码示例,我发现它们不可用。一切都被抽象了。但是当我找到它时,代码经过了六个!设置GPIO位的函数调用级别。如果您有3GHz处理器,但没有48MHz的MCU,则很好。我的代码最后是一行:

GPIO->set_output = bit.

我试图使用更多的通用驱动程序,但是我已经放弃了。在MCU上,您始终在空间和时钟周期上苦苦挣扎。我发现,如果在称为10KHz的中断例程中生成特定波形,则抽象层是第一个出现在窗口之外的层。

因此,现在我一切正常,除非有非常充分的理由,否则我不打算再次切换。

以上所有因素必须摊销您销售多少产品和节省多少。卖出一百万:节省0.10来切换到其他类型,意味着您可以在软件工时上花费100.000。卖出1000,您只有100可以花。


1
就个人而言,这就是为什么我坚持使用汇编程序。可爱的二进制文件,没有抽象。
伊恩·布兰德

C的预处理器可以很好地处理内容,特别是与__builtin_constant内部函数结合使用时。如果一个限定常数为每个I / O位形式(端口号* 32 +比特数)的,这是可能写一个宏OUTPUT_HI(n)这将产生代码等同于GPIOD->bssr |= 0x400;如果n是像的0x6A常数,而是调用一个简单的子程序如果nIS不固定。话虽这么说,我见过的大多数供应商API介于中等和可怕之间。
超级猫

8

这更多是一种意见/评论,而不是答案。

您不希望也不应该使用 C进行编程。以正确的方式使用 C ++ 会更加出色。(好的,我不得不承认,如果以错误的方式使用它,它会比C差得多。)这限制了您使用具有(现代)C ++编译器的芯片,GCC支持的一切几乎全部,包括AVR(带有有一些限制,filo提到了地址空间不均匀的问题),但是几乎排除了所有PIC(可以支持PIC32,但我还没有看到任何不错的端口)。

当您使用C / C ++进行算法编程时,您提到的选择之间的差异很小(除了8或16位芯片在执行16、32或更高位算术时会处于严重的劣势)。当您需要最后一点性能时,可能需要使用汇编器(您自己的汇编语言或供应商或第三方提供的代码)。在这种情况下,您可能需要重新考虑选择的芯片。

对硬件进行编码时,可以使用某些抽象层(通常由制造商提供)或编写自己的抽象层(基于数据表和/或示例代码)。IME现有的C抽象(mbed,cmsis等)通常在功能上(几乎)是正确的,但是在性能方面却令人吃惊(要检查针脚操作中有关6层间接寻址的旧版),可用性和可移植性。他们希望向您公开特定芯片的所有功能,而在几乎所有情况下,您将不需要它们,而不必关心它们,这会将您的代码锁定在该特定供应商(可能是该特定芯片)上。

这是C ++可以做得更好的地方:正确完成操作后,引脚集可以经过6层或更多层抽象层(因为这样可以提供更好的(便携式!)接口和较短的代码),但可以提供与目标无关的接口对于简单的情况仍然产生与汇编器相同的机器代码

我使用的编码风格的一小段代码,可能会让您热心,也可能会惊恐地转身:

// GPIO part of a HAL for atsam3xa
enum class _port { a = 0x400E0E00U, . . . };

template< _port P, uint32_t pin >
struct _pin_in_out_base : _pin_in_out_root {

   static void direction_set_direct( pin_direction d ){
      ( ( d == pin_direction::input )
         ? ((Pio*)P)->PIO_ODR : ((Pio*)P)->PIO_OER )  = ( 0x1U << pin );
   }

   static void set_direct( bool v ){
      ( v ? ((Pio*)P)->PIO_SODR : ((Pio*)P)->PIO_CODR )  = ( 0x1U << pin );    
   }
};

// a general GPIO needs some boilerplate functionality
template< _port P, uint32_t pin >
using _pin_in_out = _box_creator< _pin_in_out_base< P, pin > >;

// an Arduino Due has an on-board led, and (suppose) it is active low
using _led = _pin_in_out< _port::b, 27 >;
using led  = invert< pin_out< _led > >;

实际上,还有更多的抽象层。但是,将led的最终使用(假设打开它)并没有显示目标的复杂性或细节(对于arduin uno或ST32蓝色药丸,代码将是相同的)。

target::led::init();
target::led::set( 1 );

编译器并没有被所有这些层所吓倒,并且因为没有涉及虚拟函数,所以优化器可以查看所有内容(一些细节,省略了,例如启用了外围时钟):

 mov.w  r2, #134217728  ; 0x8000000
 ldr    r3, [pc, #24]   
 str    r2, [r3, #16]
 str    r2, [r3, #48]   

如果我意识到可以将PIO寄存器与通用基址进行偏移使用,那我将用汇编程序编写它。在这种情况下,我可能会这样做,但是编译器在优化这些方面比我要好得多。

据我所知,它是:为您的硬件编写一个抽象层,但是在现代C ++(概念,模板)中进行操作,因此不会损害您的性能。有了它,您可以轻松切换到另一个芯片。您甚至可以开始使用自己所熟悉的,熟悉的随机芯片,具有良好的调试工具等进行开发,并将最终选择推迟到以后(当您获得有关所需内存,CPU速度等的更多信息时)。

IMO嵌入式开发的亮点之一是首先选择芯片(在该论坛上经常问到这个问题:我应该选择哪种芯片...。最好的答案通常是:没关系。)

(编辑-对“因此,性能明智的C或C ++处于同一水平?”的回应)

对于相同的构造,C和C ++相同。C ++具有更多的抽象构造(仅几个:类,模板,constexpr),它们可以像任何工具一样用于好坏。为了使讨论更加有趣:不是每个人都同意什么是好是坏。


那么从性能角度来看,C还是C ++处于同一水平?我认为C ++将有更多的重载。绝对是您为我指出了正确的方向,C ++是不使用C的方法
工程师

C ++模板迫使编译时多态性在性能方面可能为零(甚至是负数),因为针对每个特定用例编译了代码。不过,这的确可以最有效地提高目标速度(GCC为O3)。就像虚拟函数一样,运行时多态可能遭受更大的损失,尽管可以说更容易维护并且在某些情况下足够好。
汉斯(Hans)

1
您声称C ++更好,但随后您开始使用C风格的强制转换。耻辱。
JAB

@JAB我从未对这种新型演员表感到太多,但我会尝试一下。但是我当前的优先级是这个库的其他部分。真正的问题当然是我无法将指针作为模板参数传递。
Wouter van Ooijen

@Hans我的cto(编译时对象)样式具有相当狭窄的用例(接近硬件,已知编译时的情况),它更像是C杀手,而不是替代基于虚拟的OO的传统用法。一个有用的附带说明是,不存在间接关系使得可以计算堆栈大小。
Wouter van Ooijen

4

如果我理解正确,那么您想知道平台在C语言环境中“弹出”平台的哪些特定于体系结构的功能,这使得在两个平台上编写可维护的可移植代码更具挑战性。

C已经是非常灵活的,因为它是“便携式汇编程序”。您选择的所有平台都提供了支持C89和C99语言标准的GCC /商业编译器,这意味着您可以在所有平台上运行类似的代码。

有一些注意事项:

  • 一些体系结构是冯·诺依曼(ARM,MIPS),其他体系结构是哈佛。当您的C程序需要从ROM读取数据(例如打印字符串),数据定义为“ const”或类似内容时,就会出现主要限制。

一些平台/编译器可以比其他平台更好地隐藏此“限制”。例如,在AVR上,您需要使用特定的宏来读取ROM数据。在PIC24 / dsPIC上,还有专用的tblrd指令可用。但是,此外,某些部分还具有“程序空间可见性”(PSVPAG)功能,该功能允许将FLASH的页面映射到RAM,从而无需tblrd即可立即进行数据寻址。编译器可以非常有效地执行此操作。

ARM和MIPS是冯·诺依曼(Von Neumann),因此将ROM,RAM和外围设备的存储区打包到1条总线上。您不会注意到从RAM或“ ROM”读取数据之间的任何区别。

  • 如果您跳到C以下,并查看针对某些操作生成的指令,您会发现I / O方面存在一些较大差异。ARM和MIPS是RISC 加载存储寄存器架构。这意味着内存总线上的数据访问必须通过MOV指令进行。这也意味着对外设值的任何修改都将导致读-修改-写(RMW)操作。有一些支持位绑定的ARM部件,它们在I / O外设空间中映射集/ clr位寄存器。但是,您需要自己编写此访问权限的代码。

另一方面,PIC24允许A​​LU操作直接通过间接寻址(即使使用指针修改也可以)读取和写入数据。它具有类似于CISC架构的某些特征,因此1条指令可以完成更多工作。这种设计可能导致更复杂的CPU内核,更低的时钟,更高的功耗等。幸运的是,您已经设计了该部件。;-)

这些差异可能意味着PIC24的I / O操作可能比类似时钟的ARM或MIPS芯片“更强大”。但是,对于相同的价格/封装/设计约束,您可能会得到更高的时钟ARM / MIPS部件。我想用实际的话来说,我认为很多“学习平台”正在掌握体系结构可以做和不能做的事情,几组操作的速度等等。

  • 每个零件系列的外围设备,时钟管理等均不同。严格来说,这将在供应商之间的ARM生态系统内发生变化,除了少数受Cortex m约束的外设(例如NVIC和SysTick)。

这些差异可以通过设备驱动程序进行某种程度的封装,但最终嵌入式固件与硬件的耦合程度很高,因此有时无法避免自定义工作。

另外,如果您要离开Microchip / former Atmel的生态系统,您可能会发现ARM部件需要更多的设置才能使它们运行。我的意思是 启用到外设的时钟,然后配置外设并“启用”它们,分别设置NVIC,等等。这只是学习过程的一部分。一旦记住以正确的顺序完成所有这些操作,在某些时候为所有这些微控制器编写设备驱动程序将感觉非常相似。

  • 另外,请尝试使用stdint.h,stdbool.h等库。这些整数类型使宽度明确,这使得平台之间的代码行为最可预测。这可能意味着在8位AVR上使用32位整数。但是,如果您的代码需要它,那就这样吧。

3

是的,没有。从程序员的角度来看,理想情况下,您要隐藏指令集的详细信息。但这在某种程度上已经无关紧要,而外围设备(即编写程序的全部目的)并不是指令集的一部分。现在,您不能同时在这些指令集之间比较4096Byte的闪存部分,尤其是在使用C的情况下,闪存/内存的消耗量很大程度上取决于指令集和编译器,有些闪存永远不应该看到编译器(咳嗽的PIC咳嗽),这是由于编译消耗了这些资源的浪费。其他闪存消耗的开销较小。在使用高级语言时,性能也是一个问题,而在MCU应用中,性能至关重要,因此,每块MCU花费3美元或1美元会有所不同。

如果要使编程变得更容易(以产品的整体成本为基础),则应该能够下载mcu的开发人员软件包,从而使指令集架构成为您从未见过的东西,因此,如果这是您的主要关注点,那么它不用担心。就使用这些库的产品成本而言,仍然需要您花钱,但是,上市时间可能会更短,我发现与直接与外围设备交谈相比,使用库需要更多的时间/工作。

最重要的是,这些指令集是您最少的担心,请转到实际问题上。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.