为什么草图要占用这么多空间和内存?


12

当我为Yún编译此草图时:

int led = 7;

void setup() {                
  pinMode(led, OUTPUT);     
}

void loop() {
  digitalWrite(led, HIGH);
}

我得到:

Sketch使用了5,098字节(17%)的程序存储空间。

最大为28,672字节。全局变量使用153字节(5%)的动态内存,剩余2407字节用于局部变量。最大为2560字节。

即使当我编译BareMinimum草图时:

void setup() {                
  // setup    
}

void loop() {
  // loop
}

我得到:

Sketch使用4,548字节(15%)的程序存储空间。

最大为28,672字节。全局变量使用151字节(5%)的动态内存,剩余2409字节用于局部变量。最大为2560字节。

为什么最少的草图要占用分配的程序存储空间的15%?为什么一个非常简单的草图会占据程序存储空间的17%?根据Arduino网站

通过在程序中包含很多字符串,可以轻松使用所有内容。例如,这样的声明:char message[] = "I support the Cape Wind project.";将33个字节放入SRAM(每个字符占用一个字节,加上'\ 0'终止符)。

但是,在这些草图中都没有声明任何字符串。

似乎它们可能会导入或使用我未指定的其他库/类。也许它会导入系统默认库?或者是别的什么?

Answers:


6

YUN是一个组合。部分Arduino和部分OpenWRT(Linux)。您的问题是关于Arduino的。实际上是类似于Leonardo的ATmega32u4,而不是UNO(ATmega328p)。32u4(Leo)通过USB上的虚拟串行端口进行通信(简答:需要支持),其中UNO具有真实的串行端口(也称为UART)。以下是AVR处理器不同板类型的构建统计信息。

请注意,在UNO上有一个外部芯片,可将USB转换为串行端口的DTR引脚,该芯片在连接时会触发ATmega328的复位引脚,从而导致引导加载程序重启。相比之下,Leo / Yun的USB转串口是在32u4的固件中实现的。因此,为了远程重启Leo或YUN的32u4芯片,加载的固件必须始终支持USB客户端驱动程序。消耗大约4K。

如果不需要USB,并且像UNO上的BareMinimum.ino一样,没有调用其他库资源,则核心Arduino库仅需要大约466个字节。

在UNO(ATmega328p)上编译BareMinimum.ino的统计信息

Sketch uses 466 bytes (1%) of program storage space. Maximum is 32,256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

在Leonardo(ATmega32u4)上编译BareMinimum.ino的统计信息

Sketch uses 4,554 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 151 bytes (5%) of dynamic memory, leaving 2,409 bytes for local variables. Maximum is 2,560 bytes.

在Yun(ATmega32u4)上编译BareMinimum.ino的统计信息

Sketch uses 4,548 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 151 bytes (5%) of dynamic memory, leaving 2,409 bytes for local variables. Maximum is 2,560 bytes.

7

Arduino在许多标准库,中断等中进行编译。例如,pinMode和digitalWrite函数使用查找表在运行时确定哪些GPIO寄存器向其写入数据。另一个例子是Arduino跟踪时间,默认情况下它定义了一些中断,而所有这些功能都需要一些空间。您会注意到,如果扩展程序,则占用空间只会稍有变化。

我个人喜欢对控制器进行最低限度的编程,而不会“膨胀”,但是您很快就会进入EE.SE和SO领域,因为一些易于使用的功能将不再可用。有一些用于pinMode和digitalWrite的替代库,它们可以编译成较小的占用空间,但也有其他缺点,例如静态编译的pin(其中led不能是变量,而可以是常量)。


因此,基本上,您无需询问即可将其编译为各种标准库?整齐。
hichris123

是的,我通常称其为“膨胀”,但这确实是一个可用性问题。Arduino是低入门级环境,无需过多考虑即可工作。如果您需要更多,Arduino允许您使用替代库,也可以针对裸机进行编译。最后一个可能不在Arduino.SE的范围内
jippie 2014年

看到我的@mpflaga答案。没有那么大的膨胀。或至少在核心库中提供最低限度的功能。除非称为草图,否则实际上并没有太多标准库。而是15%是由于32u4的USB支持。
mpflaga 2014年

4

您已经有了一些非常好的答案。我发布此内容只是为了分享有一天我做的一些统计数据,我问自己同样的问题:在最小的草图上占用这么多空间是什么?实现相同功能的最低要求是多少?

以下是最小闪烁程序的三个版本,该程序每秒切换引脚13上的LED。所有三个版本均已使用avr-gcc 4.8.2,avr-libc 1.8.0和arduino-core 1.0.5(我不使用Arduino IDE)针对Uno(不涉及USB)进行编译。

首先,标准的Arduino方式:

const uint8_t ledPin = 13;

void setup() {
    pinMode(ledPin, OUTPUT);
}

void loop() {
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(ledPin, LOW);
    delay(1000);
}

编译为1018字节。通过同时使用avr-nm和反汇编,我将大小分解为各个功能。从最大到最小:

 148 A ISR(TIMER0_OVF_vect)
 118 A init
 114 A pinMode
 108 A digitalWrite
 104 C vector table
  82 A turnOffPWM
  76 A delay
  70 A micros
  40 U loop
  26 A main
  20 A digital_pin_to_timer_PGM
  20 A digital_pin_to_port_PGM
  20 A digital_pin_to_bit_mask_PGM
  16 C __do_clear_bss
  12 C __init
  10 A port_to_output_PGM
  10 A port_to_mode_PGM
   8 U setup
   8 C .init9 (call main, jmp exit)
   4 C __bad_interrupt
   4 C _exit
-----------------------------------
1018   TOTAL

在上面的列表中,第一列是字节大小,第二列表明代码是来自Arduino核心库(A,总共822个字节),C运行时(C,148个字节)还是用户(U) ,48个字节)。

从该列表可以看出,最大的功能是为定时器0溢出中断提供服务的例程。该程序负责跟踪时间的,由需要的millis()micros()delay()。第二大功能是init(),它为PWM设置硬件定时器,使能TIMER0_OVF中断并断开USART(由引导程序使用)。此函数和上一个函数都在中定义 <Arduino directory>/hardware/arduino/cores/arduino/wiring.c

接下来是C + avr-libc版本:

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB |= _BV(PB5);     /* set pin PB5 as output */
    for (;;) {
        PINB = _BV(PB5);  /* toggle PB5 */
        _delay_ms(1000);
    }
}

各个尺寸的细分:

104 C vector table
 26 U main
 12 C __init
  8 C .init9 (call main, jmp exit)
  4 C __bad_interrupt
  4 C _exit
----------------------------------
158   TOTAL

对于C运行时,这是132个字节,包括内联函数的用户代码是26个字节_delay_ms()

应该指出的是,由于该程序不使用中断,因此不需要中断向量表,并且可以将常规用户代码放在其位置。以下程序集版本正是这样做的:

#include <avr/io.h>
#define io(reg) _SFR_IO_ADDR(reg)

    sbi io(DDRB), 5  ; set PB5 as output
loop:
    sbi io(PINB), 5  ; toggle PB5
    ldi r26, 49      ; delay for 49 * 2^16 * 5 cycles
delay:
    sbiw r24, 1
    sbci r26, 0
    brne delay
    rjmp loop

avr-gcc -nostdlib仅用14个字节组合在一起(带有),其中大部分用于延迟切换,以便可以看到闪烁。如果删除该延迟循环,最终将得到一个6字节的程序,该程序闪烁得太快而看不见(在2 MHz时):

    sbi io(DDRB), 5  ; set PB5 as output
loop:
    sbi io(PINB), 5  ; toggle PB5
    rjmp loop

3

我写了一篇文章,为什么一个LED闪烁需要1000字节?

简单的回答是:“ 两个 LED 闪烁不需要2000字节!”

更长的答案是,标准的Arduino库(如果您不想使用则不必使用)具有一些不错的功能来简化您的生活。例如,您可以在运行时按编号对引脚进行寻址,其中库将(例如)引脚8转换为正确的端口和正确的位数。如果您对端口访问进行硬编码,则可以节省开销。

即使您不使用它们,标准库也包含用于计数“滴答声”的代码,因此您可以找出当前的“时间”(通过调用millis())。为此,它必须增加一些中断服务程序的开销。

如果您简化(在Arduino Uno上)到此草图,则会使程序内存使用量减少到178字节(在IDE 1.0.6上):

int main ()
  {
  DDRB = bit (5);
  while (true)
    PINB = bit (5);
  }

好的,这不是178个字节,其中的前104个字节是硬件中断向量(每个中断4个字节,共26个向量)。

因此可以说,LED闪烁只需74个字节。在这74个字节中,大多数实际上是由编译器生成的用于初始化全局内存的代码。如果添加足够的代码以使两个LED闪烁:

int main ()
  {
  DDRB = bit (5);  // pin 13
  DDRB |= bit (4);  // pin 12

  while (true)
    {
    PINB = bit (5); // pin 13
    PINB = bit (4); // pin 12
    }
  }

然后,代码大小增加到186个字节。因此,您可能会争辩说,186 - 178 = 8使LED闪烁仅需字节。

因此,需要8个字节来使LED闪烁。对我来说听起来很有效率。


万一您想在家中尝试此操作,我应该指出,尽管上面发布的代码使两个LED闪烁,但实际上的确非常快。实际上,它们以2 MHz的频率闪烁-参见屏幕截图。通道1(黄色)是引脚12,通道2(青色)是引脚13。

12和13针快速闪烁

如您所见,输出引脚具有频率为2 MHz的方波。由于代码中引脚的切换顺序,引脚13在引脚12之前的状态更改为62.5 ns(一个时钟周期)。

因此,除非您的眼睛比我的眼睛好得多,否则您实际上不会看到任何眨眼的效果。


作为一个有趣的额外的,实际上你可以切换2个在销相同量的程序空间可翻转一个引脚。

int main ()
  {
  DDRB = bit (4) | bit (5);  // set pins 12 and 13 to output

  while (true)
    PINB =  bit (4) | bit (5); // toggle pins 12 and 13
  } // end of main

编译为178个字节。

这会给您更高的频率:

针脚12和13的快速闪烁

现在我们达到了2.66 MHz。


这很有意义。那么标准库只是在构建时自动包含的头文件吗?您怎么包括它们呢?
hichris123

2
链接器会主动清除未使用的代码。通过不调用init()(作为正常main()一样)然后将该文件wiring.c(其具有init在它)没有在联的。其结果是,对中断处理程序的处理(millis()micros()省略等)。除非您永远不需要计时,否则省略它可能不是特别实用,但是事实是草图的大小取决于您放入的内容。例如,如果使用串行,则程序存储器和RAM都会受到影响。
尼克·加蒙
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.