Mengapa sketsa memakan banyak ruang dan memori?

12

Ketika saya menyusun sketsa ini untuk Yún:

int led = 7;

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

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

Saya mendapat:

Sketsa menggunakan 5.098 byte (17%) dari ruang penyimpanan program.

Maksimal adalah 28.672 byte. Variabel global menggunakan 153 byte (5%) dari memori dinamis, meninggalkan 2.407 byte untuk variabel lokal. Maksimal 2,560 byte.

Bahkan ketika saya mengkompilasi sketsa BareMinimum:

void setup() {                
  // setup    
}

void loop() {
  // loop
}

Saya mendapat:

Sketsa menggunakan 4.548 byte (15%) dari ruang penyimpanan program.

Maksimal adalah 28.672 byte. Variabel global menggunakan 151 byte (5%) dari memori dinamis, meninggalkan 2.409 byte untuk variabel lokal. Maksimal 2,560 byte.

Mengapa sketsa minimum kosong menghabiskan 15% dari ruang penyimpanan program yang dialokasikan? Dan mengapa sketsa yang sangat sederhana memakan 17% ruang penyimpanan program? Menurut situs web Arduino :

Sangat mudah untuk menggunakan semuanya dengan memiliki banyak string dalam program Anda. Misalnya, deklarasi seperti: char message[] = "I support the Cape Wind project.";menempatkan 33 byte ke SRAM (setiap karakter mengambil byte, plus terminator '\ 0').

Namun, tidak ada string yang dinyatakan dalam salah satu sketsa ini.

Sepertinya mereka mungkin mengimpor atau menggunakan perpustakaan / kelas lain yang tidak saya tentukan. Mungkin mengimpor perpustakaan sistem default? Atau itu sesuatu yang lain?

hichris123
sumber

Jawaban:

6

YUN adalah kombo. Bagian Arduino dan Bagian OpenWRT (Linux). Pertanyaan Anda mengacu pada Arduino. Di mana ini sebenarnya adalah ATmega32u4 mirip dengan Leonardo dan bukan UNO (ATmega328p). 32u4 (Leo) berkomunikasi melalui Virtual Serial Ports melalui USB (jawaban singkat: ini perlu didukung) , di mana UNO memiliki Port Serial nyata (alias UART). Di bawah ini adalah statistik build dari berbagai tipe board untuk prosesor AVR.

Catatan pada UNO ada chip eksternal yang mengubah USB ke pin DTR port serial yang mengaktifkan pin reset ATmega328 ketika terhubung menyebabkan reboot ke bootloader. Sebaliknya Leo / Yun USB to Serial diimplementasikan dalam firmware 32u4. Oleh karena itu untuk melakukan reboot jarak jauh chip Leo atau YU 32u4, firmware yang dimuat harus selalu mendukung driver sisi klien USB. Yang mengkonsumsi sekitar 4K.

Jika USB TIDAK diperlukan dan tidak ada sumber pustaka lain yang dipanggil seperti dalam kasus BareMinimum.ino pada UNO, hanya sekitar 466 byte yang diperlukan untuk inti Arduino Library.

kompilasi statistik BareMinimum.ino pada UNO (ATmega328p)

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.

kompilasi statistik BareMinimum.ino pada Leonardo (ATmega32u4)

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.

kompilasi statistik BareMinimum.ino pada Yun (ATmega32u4)

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.
mpflaga
sumber
7

Arduino mengkompilasi di banyak pustaka standar, interupsi, ... dll. Misalnya fungsi pinMode dan digitalWrite menggunakan tabel pencarian untuk mencari tahu pada saat run time mana GPIO mendaftar untuk menulis data. Contoh lain adalah Arduino melacak waktu, menetapkan beberapa gangguan secara default dan semua fungsi ini membutuhkan ruang. Anda akan melihat bahwa jika Anda memperpanjang program, cetakan kaki hanya akan sedikit berubah.

Saya pribadi suka memprogram pengontrol dengan minimum, tanpa "mengasapi", tetapi Anda akan dengan cepat memasuki dunia EE.SE dan SO karena beberapa fungsi yang mudah digunakan tidak lagi berfungsi. Ada beberapa pustaka alternatif untuk pinMode dan digitalWrite yang mengkompilasi menjadi jejak yang lebih kecil, tetapi datang dengan kelemahan lain seperti misalnya pin dikompilasi statis (di mana ledtidak dapat menjadi variabel, tetapi merupakan konstanta).

jippie
sumber
Jadi pada dasarnya kompilasi di semua jenis perpustakaan standar tanpa Anda minta? Rapi.
hichris123
Ya, saya biasanya menyebutnya "mengasapi", tetapi itu benar-benar hal kegunaan. Arduino adalah lingkungan entry level rendah yang hanya berfungsi tanpa banyak berpikir. Jika Anda membutuhkan lebih banyak, Arduino memungkinkan Anda untuk menggunakan perpustakaan alternatif atau Anda dapat melakukan kompilasi terhadap bare metal. Yang terakhir mungkin di luar ruang lingkup untuk Arduino.SE
jippie
Lihat jawaban @mpflaga saya. Tidak ada banyak kembung. Atau setidaknya di perpustakaan inti untuk fungsionalitas minimum. Tidak ada banyak perpustakaan standar yang disertakan, kecuali disebut sketsa. Sebaliknya 15% adalah karena dukungan USB dari 32u4.
mpflaga
4

Anda sudah memiliki jawaban yang sangat bagus. Saya memposting ini hanya untuk membagikan beberapa statistik yang saya lakukan suatu hari saya bertanya pada diri sendiri pertanyaan yang sama: Apa yang mengambil begitu banyak ruang pada sketsa minimal? Berapa minimum yang dibutuhkan untuk mencapai fungsi yang sama?

Di bawah ini adalah tiga versi program blinky minimal yang mengaktifkan LED pada pin 13 setiap detik. Ketiga versi telah dikompilasi untuk Uno (tidak ada USB yang terlibat) menggunakan avr-gcc 4.8.2, avr-libc 1.8.0 dan arduino-core 1.0.5 (Saya tidak menggunakan Arduino IDE).

Pertama, cara Arduino standar:

const uint8_t ledPin = 13;

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

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

Ini mengkompilasi hingga 1018 byte. Menggunakan keduanya avr-nmdan pembongkaran , saya membagi ukuran itu menjadi fungsi individu. Dari terbesar ke terkecil:

 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

Dalam daftar di atas, kolom pertama adalah ukuran dalam byte, dan kolom kedua memberitahu apakah kode tersebut berasal dari perpustakaan inti Arduino (A, total 822 byte), runtime C (C, 148 byte) atau pengguna (U , 48 byte).

Seperti yang dapat dilihat dalam daftar ini, fungsi terbesar adalah servis rutin timer 0 overflow interrupt. Rutin ini bertanggung jawab untuk melacak waktu, dan dibutuhkan oleh millis(), micros()dan delay(). Fungsi terbesar kedua adalah init(), yang mengatur timer perangkat keras untuk PWM, memungkinkan TIMER0_OVF mengganggu dan memutus USART (yang digunakan oleh bootloader). Baik ini dan fungsi sebelumnya didefinisikan dalam <Arduino directory>/hardware/arduino/cores/arduino/wiring.c.

Berikutnya adalah versi 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);
    }
}

Rincian ukuran individu:

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

Ini adalah 132 byte untuk runtime C dan 26 byte kode pengguna, termasuk fungsi sebaris _delay_ms().

Dapat dicatat bahwa, karena program ini tidak menggunakan interupsi, tabel vektor interupsi tidak diperlukan, dan kode pengguna reguler dapat diletakkan di tempatnya. Versi perakitan berikut melakukan hal itu:

#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

Ini dirakit (dengan avr-gcc -nostdlib) menjadi hanya 14 byte, yang sebagian besar digunakan untuk menunda matikan sehingga kedipan terlihat. Jika Anda menghapus loop penundaan itu, Anda berakhir dengan program 6-byte yang berkedip terlalu cepat untuk dilihat (pada 2 MHz):

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

Saya menulis posting tentang Mengapa perlu 1.000 byte untuk berkedip satu LED? .

Jawaban singkatnya adalah: "Tidak perlu 2000 byte untuk berkedip dua LED!"

Jawaban yang lebih panjang adalah bahwa pustaka Arduino standar (yang tidak harus Anda gunakan jika tidak mau) memiliki beberapa fungsionalitas yang bagus untuk menyederhanakan hidup Anda. Misalnya, Anda dapat mengatasi pin dengan angka saat runtime, di mana perpustakaan mengkonversi (katakanlah) pin 8 ke port yang benar dan nomor bit yang benar. Jika Anda mengakses port hard-code, Anda bisa menghemat overhead itu.

Bahkan jika Anda tidak menggunakannya, pustaka standar menyertakan kode untuk menghitung "ticks" sehingga Anda dapat mengetahui "waktu" saat ini (dengan menelepon millis()). Untuk melakukan ini harus menambahkan overhead dari beberapa rutinitas layanan interupsi.

Jika Anda menyederhanakan (pada Arduino Uno) untuk sketsa ini, Anda mendapatkan penggunaan memori program hingga 178 byte (pada IDE 1.0.6):

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

OK, 178 byte tidak terlalu banyak, dan 104 byte pertama adalah vektor interupsi perangkat keras (masing-masing 4 byte, untuk 26 vektor).

Jadi bisa dibilang, hanya ada 74 byte yang diperlukan untuk berkedip LED. Dan dari 74 byte itu kebanyakan adalah kode yang dibuat oleh kompiler untuk menginisialisasi memori global. Jika Anda menambahkan kode yang cukup untuk berkedip dua 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
    }
  }

Kemudian ukuran kode meningkat menjadi 186 byte. Jadi karena itu Anda bisa berpendapat bahwa hanya perlu 186 - 178 = 8byte untuk berkedip LED.

Jadi, 8 byte untuk berkedip LED. Kedengarannya cukup efisien bagi saya.


Jika Anda tergoda untuk mencoba ini di rumah, saya harus menunjukkan bahwa sementara kode yang diposting di atas berkedip dua LED, itu memang sangat cepat. Bahkan, mereka berkedip pada 2 MHz - lihat tangkapan layar. Saluran 1 (kuning) adalah pin 12, saluran 2 (cyan) adalah pin 13.

Berkedip cepat pin 12 dan 13

Seperti yang Anda lihat, pin output memiliki gelombang persegi dengan frekuensi 2 MHz. Pin 13 mengubah status 62,5 ns (satu siklus clock) sebelum pin 12, karena urutan toggling pin dalam kode.

Jadi, kecuali jika Anda memiliki mata yang jauh lebih baik daripada mata saya, Anda tidak akan benar-benar melihat efek berkedip.


Sebagai tambahan yang lucu, Anda sebenarnya dapat mengaktifkan dua pin dalam jumlah ruang program yang sama dengan mengganti satu pin.

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

Itu mengkompilasi menjadi 178 byte.

Ini memberi Anda frekuensi yang lebih tinggi:

Kedipan pin 12 dan 13 yang sangat cepat

Sekarang kita mencapai 2,66 MHz.

Nick Gammon
sumber
Ini masuk akal. Jadi, apakah pustaka standar hanya tajuk yang disertakan secara otomatis pada waktu pembuatan? Dan bagaimana Anda bisa tidak memasukkan mereka?
hichris123
2
Linker secara agresif menghapus kode yang tidak digunakan. Dengan tidak memanggil init()(seperti biasanya main()) maka file wiring.c (yang ada initdi dalamnya) tidak ditautkan. Akibatnya, pemrosesan untuk penangan interupsi (untuk millis(), micros()dll.) Dihilangkan. Mungkin tidak terlalu praktis untuk menghilangkannya, kecuali Anda tidak perlu mengatur waktu, tetapi faktanya sketsa itu tumbuh dalam ukuran tergantung pada apa yang Anda masukkan ke dalamnya. Misalnya, jika Anda menggunakan Serial, memori program dan RAM terpukul.
Nick Gammon