Eskizler neden bu kadar yer ve hafıza kaplıyor?


12

Yún için bu çizimi derlediğimde:

int led = 7;

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

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

Alırım:

Sketch, 5.098 bayt (% 17) program depolama alanı kullanıyor.

Maksimum 28.672 bayttır. Global değişkenler 153 bayt (% 5) dinamik bellek kullanır ve yerel değişkenler için 2.407 bayt bırakır. Maksimum 2.560 bayttır.

BareMinimum çizimini derlediğimde bile:

void setup() {                
  // setup    
}

void loop() {
  // loop
}

Alırım:

Sketch, 4,548 bayt (% 15) program depolama alanı kullanıyor.

Maksimum 28.672 bayttır. Global değişkenler 151 bayt (% 5) dinamik bellek kullanır ve yerel değişkenler için 2,409 bayt bırakır. Maksimum 2.560 bayttır.

Neden asgari bir çizim tahsis edilen program depolama alanının% 15'ini kaplıyor? Ve neden çok basit bir çizim program depolama alanının% 17'sini kaplıyor? Göre Arduino web :

Programınızda çok sayıda dizge kullanarak hepsini kullanmak kolaydır. Örneğin, şöyle bir bildirim: char message[] = "I support the Cape Wind project.";SRAM'a 33 bayt koyar (her karakter bir bayt ve '\ 0' sonlandırıcı alır).

Ancak, bu eskizlerin hiçbirinde bildirilen herhangi bir dize yoktur.

Görünüşe göre belirtmediğim diğer kütüphaneleri / sınıfları içe aktarabilir veya kullanabilirler. Belki bir sistem varsayılan kütüphanesini içeri aktarır? Yoksa başka bir şey mi?

Yanıtlar:


6

YUN bir kombinasyon. Bölüm Arduino ve Bölüm OpenWRT (Linux). Sorunuz Arduino ile ilgilidir. Bu aslında bir UNO'ya (ATmega328p) değil, Leonardo'ya benzer bir ATmega32u4. 32u4 (Leo), UNO'nun gerçek bir Seri Bağlantı Noktasına (diğer adıyla UART) sahip olduğu USB üzerinden Sanal Seri Bağlantı Noktaları aracılığıyla iletişim kurar (kısa yanıt: bunun desteklenmesi gerekir ). Aşağıda AVR işlemcileri için farklı kart türlerinin istatistikleri bulunmaktadır.

UNO'da USB'yi Seri bağlantı noktasının DTR pinine dönüştüren ve bağlandığında ATmega328'in sıfırlama pimini değiştirerek önyükleyicide yeniden başlatmaya neden olan harici bir yonga olduğunu unutmayın. Buna karşılık Leo / Yun'un USB'den Seri'ye 32u4 ürün yazılımında uygulanmıştır. Bu nedenle, Leo veya YUN'un 32u4 yongasını uzaktan yeniden başlatmak için yüklenen bellenimin her zaman USB istemci tarafı sürücüsünü desteklemesi gerekir. Yaklaşık 4K tüketir.

USB gerekli DEĞİLSE ve bir UNO'da BareMinimum.ino durumunda olduğu gibi başka bir kütüphane kaynağı çağrılmadıysa, çekirdek Arduino Kütüphanesi için sadece yaklaşık 466 bayt gereklidir.

BareMinimum.ino'nun istatistiklerini bir UNO'da (ATmega328p) derleyin

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.

BareMinimum.ino 'yu Leonardo (ATmega32u4) üzerinden derleme

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.

BareMinimum.ino'nun bir Yun (ATmega32u4) üzerindeki istatistiklerini derleyin

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, birçok standart kütüphanede, kesmede, vb. Başka bir örnek, Arduino'nun zamanı takip etmesi, varsayılan olarak bazı kesintileri tanımlaması ve tüm bu işlevlerin biraz alan gerektirmesidir. Programı uzatırsanız, ayak izinin sadece biraz değişeceğini fark edeceksiniz.

Ben şahsen kontrol cihazlarını "şişirmeden" çıplak minimumla programlamayı seviyorum, ancak EE.SE ve SO dünyasına hızlı bir şekilde gireceksiniz çünkü kullanımı kolay birkaç fonksiyon artık kutudan çıkmayacak. PinMode ve digitalWrite için daha az yer kaplayan, ancak statik olarak derlenmiş pimler ( leddeğişken olamaz, ancak sabittir) gibi diğer dezavantajlarla birlikte gelen bazı alternatif kütüphaneler vardır .


Yani temelde siz sormadan her türlü standart kütüphanede derleniyor mu? Temiz.
hichris123

Evet, buna genellikle "şişkinlik" diyorum, ama bu gerçekten kullanışlı bir şey. Arduino, çok fazla düşünmeden çalışan düşük bir giriş seviyesi ortamıdır. Daha fazlasına ihtiyacınız varsa, Arduino alternatif kütüphaneler kullanmanıza izin verir veya çıplak metale karşı derleyebilirsiniz. Sonuncusu
Arduino

@Mpflaga cevabımı görün. Çok fazla şişkinlik yok. Ya da en azından çekirdek kütüphanede çıplak asgari işlevsellik için. Eskiz olarak adlandırılmadığı sürece, standart kütüphanelerin pek çoğu dahil değildir. Daha ziyade% 15, 32u4'ün USB desteğinden kaynaklanıyor.
mpflaga

4

Zaten bazı mükemmel cevaplarınız var. Bunu sadece bir gün yaptığım bazı istatistikleri paylaşmak için gönderiyorum. Kendime aynı soruları sordum: Minimal bir taslakta bu kadar yer kaplayan nedir? Aynı işlevselliği elde etmek için gereken minimum miktar nedir?

Aşağıda, her saniye pin 13'teki LED'i değiştiren minimal bir blinky programının üç versiyonu bulunmaktadır. Her üç sürüm de avr-gcc 4.8.2, avr-libc 1.8.0 ve arduino-core 1.0.5 (Arduino IDE kullanmıyorum) kullanılarak bir Uno (USB dahil değil) için derlenmiştir.

İlk olarak, standart Arduino yolu:

const uint8_t ledPin = 13;

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

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

Bu 1018 bayta derlenir. Her ikisini de avr-nmve sökmeyi kullanarak , bu boyutu bireysel işlevlere ayırdım. Büyükten küçüğe:

 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

Yukarıdaki listede, ilk sütun bayt cinsinden boyuttur ve ikinci sütun kodun Arduino çekirdek kütüphanesinden (toplam A, 822 bayt), C çalışma zamanından (C, 148 bayt) veya kullanıcıdan (U , 48 bayt).

Bu listede görülebileceği gibi, en büyük fonksiyon zamanlayıcı 0 taşma kesintisine hizmet eden rutindir. Bu rutin süresini izleme sorumludur ve ihtiyaç duyduğu millis(), micros()ve delay(). İkinci en büyük işlev, init()PWM için donanım zamanlayıcılarını ayarlayan TIMER0_OVF kesmesini etkinleştiren ve USART (önyükleyici tarafından kullanılan) bağlantısını kesen işlevdir. Hem bu hem de önceki işlev <Arduino directory>/hardware/arduino/cores/arduino/wiring.c.

Sonraki C + avr-libc sürümüdür:

#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);
    }
}

Bireysel boyutların dökümü:

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

Bu, C çalışma zamanı için 132 bayt ve inline işlevi dahil 26 baytlık kullanıcı kodudur _delay_ms().

Bu program kesintileri kullanmadığından, kesme vektör tablosuna gerek olmadığı ve yerine normal kullanıcı kodunun konabileceği belirtilebilir. Aşağıdaki derleme sürümü tam olarak bunu yapar:

#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

Bu avr-gcc -nostdlibsadece 14 bayta monte edilir (ile birlikte ), çoğu yanıp sönmeyi görünür hale getirmek için geçişleri geciktirmek için kullanılır. Bu gecikme döngüsünü kaldırırsanız, görülemeyecek kadar hızlı yanıp sönen 6 baytlık bir programla sonuçlanırsınız (2 MHz'de):

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

3

Ben yaklaşık bir yazı yazdı o bir LED yanıp 1000 bayt sürer Why? .

Kısa cevap: " İki LED'in yanıp sönmesi 2000 bayt sürmez !"

Uzun cevap, standart Arduino kütüphanelerinin (istemiyorsanız kullanmak zorunda kalmamanız) hayatınızı basitleştirmek için hoş bir işlevselliğe sahip olmasıdır. Örneğin, çalışma zamanında pinleri adrese göre adresleyebilirsiniz, burada kütüphane pin 8'i doğru porta ve doğru bit numarasına dönüştürür (diyelim). Sabit kod bağlantı noktası erişiminiz varsa, bu yükü kaydedebilirsiniz.

Bunları kullanmasanız bile, standart kütüphaneler "keneleri" saymak için kod içerir, böylece geçerli "zamanı" öğrenebilirsiniz (arayarak millis()). Bunu yapmak için bazı kesinti servis rutinlerinin ek yükünü eklemek gerekir.

Bu çizime basitleştirirseniz (Arduino Uno'da) program belleği kullanımını 178 bayta (IDE 1.0.6'da) indirirsiniz:

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

Tamam, 178 bayt o kadar da değil ve ilk 104 bayt donanım kesme vektörleridir (26 vektör için her biri 4 bayt).

Bu yüzden tartışmalı bir şekilde, bir LED'in yanıp sönmesi için sadece 74 bayt gerekir. Ve bu 74 baytın çoğu, derleyici tarafından global belleği başlatmak için üretilen koddur. İki LED'i yanıp sönmeye yetecek kadar kod eklerseniz:

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

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

Daha sonra kod boyutu 186 bayta çıkar. Bu nedenle, 186 - 178 = 8bir LED'in yanıp sönmesinin sadece bayt sürdüğünü iddia edebilirsiniz .

Yani, 8 bayt bir LED yanıp söner. Bana oldukça etkili geliyor.


Bunu evde denemek için cazip olursanız, yukarıdaki kodun iki LED'i yanıp sönerken, gerçekten çok hızlı olduğunu belirtmeliyim. Aslında, 2 MHz'de yanıp sönüyorlar - ekran görüntüsüne bakın. Kanal 1 (sarı) pim 12, kanal 2 (mavi) pim 13'tür.

12 ve 13 numaralı pimlerin hızlı yanıp sönmesi

Gördüğünüz gibi, çıkış pinleri 2 MHz frekanslı bir kare dalgaya sahiptir. Pim 13, koddaki pimlerin geçiş sırasına bağlı olarak pim 12'den önce durumu 62.5 ns (bir saat döngüsü) değiştirir.

Yani benimkinden çok daha iyi gözleriniz yoksa, göz kırpma etkisi görmezsiniz.


Eğlenceli bir ekstra olarak, aslında geçiş yapabilirsiniz iki yılında pimleri aynı programı alanı miktarıyla bir pimi geçiş olarak.

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

Bu 178 bayt olarak derlenir.

Bu size daha yüksek bir frekans verir:

12 ve 13 numaralı pimlerin çok hızlı yanıp sönmesi

Şimdi 2,66 MHz'e kadar çıktık.


Bu bir ton anlam ifade ediyor. Standart kitaplıklar yalnızca üstbilgiler derleme sırasında otomatik olarak dahil edilir mi? Ve nasıl başardık değil onları da?
hichris123

2
Bağlayıcı, kullanılmayan kodu agresif bir şekilde çıkarır. init()(Normalde olduğu main()gibi) çağrılmadan sonra wiring.c dosyası (içinde olan init) bağlanmadı. Sonuç olarak, kesme işleyicileri (için millis(), micros()vb.) İşleme atlandı. Hiçbir zaman zamanlamaya ihtiyacınız olmadıkça, muhtemelen atlamak özellikle pratik değildir, ancak gerçek şu ki, taslağın içine koyduğunuza bağlı olarak boyutu büyür. Örneğin, Seri kullanıyorsanız, hem program belleği hem de RAM bir vuruş alır.
Nick Gammon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.