เหตุใดร่างจึงใช้พื้นที่และหน่วยความจำมาก


12

เมื่อฉันรวบรวมร่างนี้สำหรับYún:

int led = 7;

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

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

ฉันเข้าใจ:

Sketch ใช้พื้นที่เก็บข้อมูลโปรแกรม 5,098 ไบต์ (17%)

สูงสุดคือ 28,672 ไบต์ ตัวแปรกลางใช้ 153 ไบต์ (5%) ของหน่วยความจำแบบไดนามิกเหลือ 2,407 ไบต์สำหรับตัวแปรท้องถิ่น สูงสุดคือ 2,560 ไบต์

แม้ว่าฉันจะรวบรวมร่าง BareMinimum:

void setup() {                
  // setup    
}

void loop() {
  // loop
}

ฉันเข้าใจ:

Sketch ใช้พื้นที่เก็บข้อมูลโปรแกรม 4,548 ไบต์ (15%)

สูงสุดคือ 28,672 ไบต์ ตัวแปรโกลบอลใช้หน่วยความจำแบบไดนามิก 151 ไบต์ (5%) โดยเหลือ 2,409 ไบต์สำหรับตัวแปรโลคัล สูงสุดคือ 2,560 ไบต์

เหตุใดร่างร่างขั้นต่ำเปลือยจึงใช้พื้นที่จัดเก็บโปรแกรม 15%? และทำไมร่างแบบง่าย ๆ จึงใช้พื้นที่จัดเก็บโปรแกรม 17%? ตามเว็บไซต์ Arduino :

ใช้งานง่ายขึ้นโดยมีสตริงจำนวนมากในโปรแกรมของคุณ ตัวอย่างเช่นการประกาศเช่น: char message[] = "I support the Cape Wind project.";ใส่ 33 ไบต์ลงใน SRAM (อักขระแต่ละตัวใช้เวลาเป็นไบต์รวมถึงจุดสิ้นสุด '\ 0')

อย่างไรก็ตามไม่มีการประกาศสตริงใด ๆ ในภาพร่างใดภาพหนึ่ง

ดูเหมือนว่าพวกเขาอาจนำเข้าหรือใช้ไลบรารี / คลาสอื่นที่ฉันไม่ได้ระบุ อาจจะนำเข้าไลบรารีเริ่มต้นระบบหรือไม่ หรือมันเป็นอย่างอื่น?

คำตอบ:


6

YUN เป็นคอมโบ ส่วน Arduino และ OpenWRT ส่วนหนึ่ง (Linux) คำถามของคุณอ้างอิงถึง Arduino ที่นี่เป็น ATmega32u4 คล้ายกับ Leonardo ไม่ใช่ UNO (ATmega328p) 32u4 (Leo) สื่อสารผ่านพอร์ตอนุกรมเสมือนผ่าน USB (คำตอบสั้น ๆ : ต้องได้รับการสนับสนุน)โดยที่ UNO มีพอร์ตอนุกรมจริง (aka UART) ด้านล่างนี้เป็นการสร้างสถิติของบอร์ดประเภทต่าง ๆ สำหรับโปรเซสเซอร์ AVR

หมายเหตุเกี่ยวกับ UNO มีชิปภายนอกที่แปลง USB เป็นพิน DTR ของพอร์ตอนุกรมซึ่งจะสลับพินการรีเซ็ต ATmega328 เมื่อเชื่อมต่อทำให้เกิดการรีบูตเครื่อง ในทางตรงกันข้าม USB / Serial ของ Leo / Yun ได้รับการติดตั้งในเฟิร์มแวร์ของ 32u4 ดังนั้นเพื่อรีบูตชิพ 32u4 ของ Leo หรือ YUN จากระยะไกลการโหลดเฟิร์มแวร์จะต้องรองรับไดรเวอร์ฝั่งไคลเอนต์ USB เสมอ ซึ่งกินไฟประมาณ 4K

หากไม่จำเป็นต้องใช้ USB และไม่มีการเรียกใช้ทรัพยากรห้องสมุดอื่น ๆ ในกรณีของ BareMinimum.ino บน UNO จำเป็นต้องใช้เพียง 466 ไบต์สำหรับ Core Arduino Library

รวบรวมสถิติของ BareMinimum.ino บน 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.

รวบรวมสถิติของ BareMinimum.ino บน 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.

รวบรวมสถิติของ BareMinimum.ino บนหยุน (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.

7

Arduino รวบรวมในไลบรารีมาตรฐานอินเตอร์รัปต์ ... ฯลฯ ตัวอย่างเช่นฟังก์ชั่น pinMode และ digitalWrite ใช้ตารางการค้นหาเพื่อค้นหาเวลาใช้งานที่ GPIO ลงทะเบียนเพื่อเขียนข้อมูล อีกตัวอย่างหนึ่งคือ Arduino ติดตามเวลากำหนดอินเทอร์รัปต์บางอย่างตามค่าเริ่มต้นและฟังก์ชั่นทั้งหมดนี้ต้องการพื้นที่บางส่วน คุณจะสังเกตเห็นว่าหากคุณขยายโปรแกรมพิมพ์เท้าจะเปลี่ยนเพียงเล็กน้อยเท่านั้น

ฉันเองชอบที่จะตั้งโปรแกรมคอนโทรลเลอร์ขั้นต่ำสุดโดยไม่มี "bloat" แต่คุณจะเข้าสู่โลกของ EE.SE และ SO อย่างรวดเร็วเพราะฟังก์ชั่นที่ใช้งานง่ายหลายอย่างจะไม่ทำงานนอกกรอบอีกต่อไป มีไลบรารีทางเลือกบางส่วนสำหรับ pinMode และ digitalWrite ที่รวบรวมเป็นรอยเท้าขนาดเล็ก แต่มาพร้อมกับข้อเสียอื่น ๆ เช่นขาหมุดรวบรวมแบบคงที่ (ที่ledไม่สามารถเป็นตัวแปร แต่เป็นค่าคงที่)


ดังนั้นโดยทั่วไปมันจะรวบรวมในไลบรารีมาตรฐานทุกประเภทโดยที่คุณไม่ต้องถาม เรียบร้อย
hichris123

ใช่ฉันมักจะเรียกมันว่า "bloat" แต่มันเป็นสิ่งที่ใช้งานได้จริง ๆ Arduino เป็นสภาพแวดล้อมระดับเริ่มต้นที่ใช้งานได้โดยไม่ต้องคิดมาก หากคุณต้องการมากกว่านี้ Arduino อนุญาตให้คุณใช้ห้องสมุดทางเลือกหรือคุณสามารถรวบรวมกับโลหะเปลือยได้ ล่าสุดอาจไม่อยู่ในขอบเขตสำหรับ Arduino.SE
jippie

ดูคำตอบ @mpflaga ของฉัน มีไม่มากเท่า bloat หรืออย่างน้อยก็ในไลบรารีหลักสำหรับการใช้งานขั้นต่ำที่เปลือยเปล่า มีไลบรารีมาตรฐานไม่มากนักยกเว้นว่าเรียกว่าร่าง ค่อนข้าง 15% เกิดจากการรองรับ USB ของ 32u4
mpflaga

4

คุณมีคำตอบที่ดีอย่างสมบูรณ์อยู่แล้ว ฉันโพสต์สิ่งนี้เพียงเพื่อแบ่งปันสถิติบางอย่างที่ฉันทำในวันหนึ่งฉันถามคำถามแบบเดียวกันกับตัวเองว่า: อะไรคือการใช้พื้นที่มากในการร่างแบบย่อ อะไรคือความต้องการขั้นต่ำในการบรรลุหน้าที่การทำงานเดียวกัน

ด้านล่างนี้เป็นโปรแกรมบั๊กเล็ก ๆ สามเวอร์ชันที่สลับ LED ที่ขา 13 ทุกวินาที ทั้งสามเวอร์ชั่นได้รับการคอมไพล์สำหรับ Uno (ไม่เกี่ยวข้องกับ USB) โดยใช้ avr-gcc 4.8.2, avr-libc 1.8.0 และ arduino-core 1.0.5 (ฉันไม่ได้ใช้ Arduino IDE)

ครั้งแรกที่วิธี Arduino มาตรฐาน:

const uint8_t ledPin = 13;

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

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

ซึ่งรวบรวมได้ถึง 1,018 ไบต์ เมื่อใช้ทั้งสอง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 runtime (C, 148 ไบต์) หรือผู้ใช้ (U , 48 ไบต์)

ดังที่เห็นได้ในรายการนี้ฟังก์ชั่นที่ใหญ่ที่สุดคือรูทีนการซ่อมบำรุงตัวจับเวลา 0 โอเวอร์โฟลว์อินเตอร์รัปต์ ประจำวันนี้เป็นผู้รับผิดชอบในการติดตามของเวลาและเป็นสิ่งจำเป็นโดยmillis(), และmicros() delay()ฟังก์ชั่นที่ใหญ่เป็นอันดับสองคือinit()ซึ่งตั้งค่าตัวจับเวลาฮาร์ดแวร์สำหรับ PWM เปิดใช้งาน TIMER0_OVF ขัดจังหวะและยกเลิกการเชื่อมต่อ USART (ซึ่งใช้โดย bootloader) <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

นี่คือ 132 ไบต์สำหรับรันไทม์ C และ 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

ฉันเขียนโพสต์เกี่ยวกับเหตุใดจึงต้องใช้ 1,000 ไบต์ในการกะพริบไฟ LED หนึ่งดวง .

คำตอบสั้น ๆ คือ: "ไม่ต้องใช้ 2000 ไบต์ในการกระพริบไฟ LED สองดวง!"

คำตอบอีกต่อไปก็คือไลบรารี Arduino มาตรฐาน (ซึ่งคุณไม่จำเป็นต้องใช้ถ้าคุณไม่ต้องการ) มีฟังก์ชั่นที่ดีเพื่อทำให้ชีวิตของคุณง่ายขึ้น ตัวอย่างเช่นคุณสามารถระบุตำแหน่งหมุดตามหมายเลขที่รันไทม์ซึ่งไลบรารีแปลง (พูด) พิน 8 เป็นพอร์ตที่ถูกต้องและหมายเลขบิตที่ถูกต้อง หากคุณใช้รหัสฮาร์ดพอร์ตคุณสามารถบันทึกค่าใช้จ่ายนั้นได้

แม้ว่าคุณจะไม่ได้ใช้ไลบรารีมาตรฐานก็มีรหัสเพื่อนับ "เห็บ" เพื่อให้คุณสามารถค้นหา "เวลา" ปัจจุบัน (โดยการโทรmillis()) เมื่อต้องการทำเช่นนี้จะต้องเพิ่มค่าใช้จ่ายของรูทีนบริการขัดจังหวะบางส่วน

หากคุณลดความซับซ้อน (บน Arduino Uno) ลงในร่างนี้คุณจะได้รับการใช้งานหน่วยความจำของโปรแกรมลดลงถึง 178 ไบต์ (บน IDE 1.0.6):

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

ตกลง 178 ไบต์นั้นไม่มากนักและ 104 ไบต์แรกนั้นเป็นเวกเตอร์ที่ขัดจังหวะด้วยฮาร์ดแวร์ (แต่ละ 4 ไบต์สำหรับ 26 เวกเตอร์)

ดังนั้นเนื้อหามีเพียง 74 ไบต์ที่จำเป็นในการกระพริบไฟ LED และ 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 Pin 13 เปลี่ยนสถานะ 62.5 ns (หนึ่งรอบนาฬิกา) ก่อนถึง pin 12 เนื่องจากคำสั่งของการสลับของพินในรหัส

ดังนั้นถ้าคุณมีดวงตาที่ดีกว่าของฉันคุณจะไม่เห็นผลกระพริบใด ๆ


เป็นพิเศษสนุกคุณจริงสามารถสลับสองขาในเดียวกันจำนวนของพื้นที่โครงการเป็นหนึ่งสลับขา

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()ฯลฯ ) ถูกมองข้าม อาจไม่เหมาะที่จะเว้นเสียแต่ว่าคุณไม่จำเป็นต้องใช้เวลาทำ แต่ความจริงก็คือร่างนั้นโตขึ้นตามขนาดที่คุณใส่ไว้ ตัวอย่างเช่นหากคุณใช้ Serial ทั้งหน่วยความจำของโปรแกรมและ RAM จะได้รับความนิยม
Nick Gammon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.