Vraag:
Waarom nemen schetsen zoveel ruimte en geheugen in beslag?
hichris123
2014-02-22 03:29:25 UTC
view on stackexchange narkive permalink

Als ik deze sketch compileer voor de Yún:

  int led = 7; void setup () {pinMode (led, OUTPUT); } void loop () {digitalWrite (led, HIGH);}  

Ik krijg:

Sketch gebruikt 5.098 bytes (17%) programma-opslagruimte .

Het maximum is 28.672 bytes. Globale variabelen gebruiken 153 bytes (5%) dynamisch geheugen, waardoor er 2.407 bytes overblijven voor lokale variabelen. Het maximum is 2560 bytes.

Zelfs als ik de schets van BareMinimum compileer:

  void setup () {// setup} void loop () {// loop}  

Ik krijg:

Sketch gebruikt 4.548 bytes (15%) programma-opslagruimte.

Maximum is 28.672 bytes . Globale variabelen gebruiken 151 bytes (5%) dynamisch geheugen, waardoor er 2.409 bytes overblijven voor lokale variabelen. Het maximum is 2.560 bytes.

Waarom neemt een absoluut minimum aan sketch 15% van de toegewezen programma-opslagruimte in beslag? En waarom neemt een heel eenvoudige schets 17% van de programma-opslagruimte in beslag? Volgens de Arduino-website:

Het is gemakkelijk om alles te gebruiken door veel strings in je programma te hebben. Een declaratie zoals: char message [] = "Ik steun het Cape Wind-project."; zet 33 bytes in SRAM (elk karakter neemt een byte plus de '\ 0' terminator).

Er zijn echter geen strings gedeclareerd in geen van deze schetsen.

Het lijkt erop dat ze andere bibliotheken / klassen zouden importeren of gebruiken die ik niet ' t specificeren. Misschien importeert het een standaardbibliotheek van het systeem? Of is het iets anders?

Vier antwoorden:
#1
+7
jippie
2014-02-22 03:36:27 UTC
view on stackexchange narkive permalink

Arduino compileert in veel standaard bibliotheken, interrupts, ... etc. De pinMode- en digitalWrite-functies gebruiken bijvoorbeeld een opzoektabel om tijdens runtime uit te zoeken naar welke GPIO-registers gegevens moeten worden geschreven. Een ander voorbeeld is dat Arduino de tijd bijhoudt, standaard enkele interrupts definieert en al deze functionaliteit wat ruimte vereist. Je zult merken dat als je het programma uitbreidt, de footprint maar een klein beetje zal veranderen.

Persoonlijk vind ik het leuk om controllers te programmeren met een absoluut minimum, zonder "bloat", maar je zult snel de wereld van EE.SE en SO omdat verschillende gebruiksvriendelijke functies niet langer uit de doos werken. Er zijn enkele alternatieve bibliotheken voor pinMode en digitalWrite die compileren tot een kleinere footprint, maar die andere nadelen hebben, zoals bijvoorbeeld statische gecompileerde pinnen (waarbij led geen variabele kan zijn, maar een constante).

Dus in principe compileert het in allerlei standaardbibliotheken zonder dat u erom vraagt? Netjes.
Ja, ik noem het meestal "bloat", maar het is echt iets om te gebruiken. Arduino is een lage instapomgeving die gewoon werkt zonder al te veel nadenken. Als je meer nodig hebt, kun je met Arduino alternatieve bibliotheken gebruiken of je kunt compileren tegen bare metal. De laatste valt waarschijnlijk buiten het bereik van Arduino.SE
Zie mijn @mpflaga-antwoord. Er is niet zoveel opgeblazen gevoel. Of in ieder geval in de kernbibliotheek voor een absoluut minimum aan functionaliteit. Er zijn niet echt veel standaardbibliotheken inbegrepen, tenzij de sketch wordt genoemd. De 15% is eerder te danken aan de USB-ondersteuning van de 32u4.
#2
+6
mpflaga
2014-02-22 11:58:43 UTC
view on stackexchange narkive permalink

De YUN is een combo. Gedeeltelijk Arduino en gedeeltelijk OpenWRT (Linux). Uw vraag heeft betrekking op de Arduino. Waar dit eigenlijk een ATmega32u4 is die lijkt op een Leonardo en niet een UNO (ATmega328p). De 32u4 (Leo) communiceert via virtuele seriële poorten over de USB (kort antwoord: dit moet worden ondersteund) , waar de UNO een echte seriële poort heeft (ook bekend als UART). Hieronder vindt u statistieken van de verschillende soorten kaarten voor de AVR-processors.

Opmerking over de UNO is er een externe chip die USB converteert naar de DTR-pin van de seriële poort die de reset-pin van de ATmega328 omschakelt wanneer deze is aangesloten, wat een herstart veroorzaakt naar de bootloader. De USB to Serial van Leo / Yun is daarentegen geïmplementeerd in de firmware van de 32u4. Om de Leo of YUN's 32u4-chip op afstand opnieuw op te starten, moet de geladen firmware dus altijd het USB-stuurprogramma aan de clientzijde ondersteunen. Die verbruikt ongeveer 4K.

Als de USB NIET nodig was en er geen andere bibliotheekbronnen werden aangeroepen, zoals in het geval van BareMinimum.ino op een UNO, zijn slechts ongeveer 466 bytes nodig voor de kernbibliotheek van Arduino.

compileer statistieken van BareMinimum.ino op een UNO (ATmega328p)

  Sketch gebruikt 466 bytes (1%) programma-opslagruimte. Het maximum is 32.256 bytes. Globale variabelen gebruiken 9 bytes (0%) dynamisch geheugen, waardoor er 2.039 bytes overblijven voor lokale variabelen. Het maximum is 2.048 bytes.  

statistieken van BareMinimum.ino compileren op een Leonardo (ATmega32u4)

  Sketch gebruikt 4.554 bytes (15%) programmaopslagruimte . Het maximum is 28.672 bytes. Globale variabelen gebruiken 151 bytes (5%) dynamisch geheugen, waardoor er 2.409 bytes overblijven voor lokale variabelen. Het maximum is 2.560 bytes.  

statistieken van BareMinimum.ino compileren op een Yun (ATmega32u4)

  Sketch gebruikt 4.548 bytes (15%) programmaopslagruimte . Het maximum is 28.672 bytes. Globale variabelen gebruiken 151 bytes (5%) dynamisch geheugen, waardoor er 2.409 bytes overblijven voor lokale variabelen. Het maximum is 2560 bytes.  
#3
+4
Edgar Bonet
2015-07-07 13:12:17 UTC
view on stackexchange narkive permalink

Je hebt al een aantal prima antwoorden. Ik plaats dit alleen om een ​​paar statistieken te delen die ik op een dag deed. Ik stelde mezelf dezelfde soort vragen: wat neemt er zoveel ruimte in op een minimale schets? Wat is minimaal nodig om dezelfde functionaliteit te bereiken?

Hieronder staan ​​drie versies van een minimaal blinky programma dat de LED elke seconde op pin 13 zet. Alle drie de versies zijn gecompileerd voor anUno (geen USB betrokken) met behulp van avr-gcc 4.8.2, avr-libc 1.8.0 en carduino-core 1.0.5 (ik gebruik de Arduino IDE niet).

Ten eerste de standaard Arduino-manier:

  const uint8_t ledPin = 13; void setup () {pinMode (ledPin, OUTPUT);} void loop () {digitalWrite (ledPin, HIGH); vertraging (1000); digitalWrite (ledPin, LOW); delay (1000);}  

Dit compileert tot 1018 bytes. Door zowel avr-nm als demontage te gebruiken, heb ik die grootte opgesplitst in afzonderlijke functies. Van groot naar klein:

  148 A ISR (TIMER0_OVF_vect) 118 A init 114 A pinMode 108 A digitalWrite 104 C vectortabel 82 A turnOffPWM 76 A vertraging 70 A micros 40 U lus 26 A hoofd 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 poort_to_output_PGM 10 A poort_naar_modus_PGM 8 U hoeft geen setup 8 C .init9 (call main, jmp exit) ------------------------ 1018 TOTAAL  

In de bovenstaande lijst is de eerste kolom de grootte in bytes , en de tweede kolom vertelt of de code afkomstig is van de Arduino-kernbibliotheek (A, 822 bytes totaal), de C-runtime (C, 148 bytes) of de gebruiker (U, 48 bytes).

Zoals kan zijn gezien in deze lijst, is de grootste functie de routines die de timer 0 overflow-interrupt onderhouden. Deze routine is verantwoordelijk voor het bijhouden van tijd, en is nodig voor millis () , micros () en delay () . De op een na grootste functie is init () , waarmee de hardwaretimers worden ingesteld voor PWM, schakelt TIMER0_OVF interrupt in en verbreekt de verbinding met de USART (die werd gebruikt door de bootloader). Zowel deze als de vorige functie zijn gedefinieerd in <Arduino-directory> / hardware / arduino / cores / arduino / bedrading.c .

De volgende is de C + avr-libc-versie:

  #include <avr / io.h> # include <util / delay.h>int main (void) {DDRB | = _BV (PB5); / * stel pin PB5 in als output * / voor (;;) {PINB = _BV (PB5); / * schakel PB5 * / _delay_ms (1000); }}  

De uitsplitsing van de individuele maten:

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

Dit is 132 bytes voor de C-runtime en 26 bytes gebruikerscode, inclusief de inline-functie _delay_ms().

Het zou kunnen worden opgemerkt dat, aangezien dit programma gebruikt geen interrupts, de interruptvectortabel is niet nodig en de gewone gebruikerscode kan op zijn plaats worden gezet. De volgende assembly-versie doet precies dat:

  #include <avr / io.h> # definieer io (reg) _SFR_IO_ADDR (reg) sbi io (DDRB), 5; stel PB5 in als outputloop: sbi io (PINB), 5; schakelaar PB5 ldi r26, 49; vertraging voor 49 * 2 ^ 16 * 5 cycli vertraging: sbiw r24, 1 sbci r26, 0 brne vertraging rjmp loop  

Dit wordt geassembleerd (met avr-gcc -nostdlib ) in slechts 14 bytes, waarvan de meeste worden gebruikt om het schakelen te vertragen, zodat het knipperen zichtbaar is. Als je die vertragingslus verwijdert, krijg je een 6-byteprogramma dat te snel knippert om gezien te worden (op 2 MHz):

  sbi io (DDRB), 5; stel PB5 in als outputloop: sbi io (PINB), 5; schakel PB5 rjmp-lus  
in
#4
+3
Nick Gammon
2015-07-07 07:17:10 UTC
view on stackexchange narkive permalink

Ik heb een bericht geschreven over Waarom duurt het 1000 bytes om één LED te laten knipperen?.

Het korte antwoord is: "Het duurt niet 2000 bytes om te knipperen twee LED's! "

Het langere antwoord is dat de standaard Arduino-bibliotheken (die je niet hoeft te gebruiken als je dat niet wilt) een aantal leuke functies hebben om je leven. U kunt bijvoorbeeld pinnen op nummer adresseren tijdens runtime, waarbij de bibliotheek (bijvoorbeeld) pin 8 omzet in de juiste poort en het juiste bitnummer. Als u poorttoegang hard codeert, kunt u die overhead besparen.

Zelfs als u ze niet gebruikt, bevatten de standaardbibliotheken code om "tikken" te tellen, zodat u de huidige "tijd" kunt achterhalen ( door millis () ) aan te roepen. Om dit te doen, moet het de overhead van een aantal interruptserviceroutines toevoegen.

Als je deze schets vereenvoudigt (op de Arduino Uno), krijg je het geheugengebruik van het programma teruggebracht tot 178 bytes (op IDE 1.0. 6):

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

OK, 178 bytes is niet zo veel, en daarvan zijn de eerste 104 bytes de hardware-interruptvectoren (4 bytes elk, voor 26 vectoren).

Er zijn dus waarschijnlijk maar 74 bytes nodig om een ​​LED te laten knipperen. En van die 74 bytes zijn de meeste eigenlijk de code die wordt gegenereerd door de compiler om het globale geheugen te initialiseren. Als je genoeg code toevoegt om twee LED's te laten knipperen:

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

Vervolgens neemt de codegrootte toe tot 186 bytes. Dus daarom zou je kunnen stellen dat er slechts 186 - 178 = 8 bytes nodig zijn om een ​​LED te laten knipperen.

Dus 8 bytes om een ​​LED te laten knipperen. Klinkt behoorlijk efficiënt voor mij.


Mocht je in de verleiding komen om dit thuis te proberen, dan moet ik erop wijzen dat hoewel de geposte code hierboven twee LED's laat knipperen, dit inderdaad erg snel gebeurt. In feite knipperen ze op 2 MHz - zie screenshot. Kanaal 1 (geel) is pin 12, kanaal 2 (cyaan) is pin 13.

Rapid blinking of pins 12 and 13

Zoals je kunt zien, hebben de uitgangspennen een blokgolf met een frequentie van 2 MHz. Pin 13 verandert van status 62,5 ns (één klokcyclus) voor pin 12, vanwege de volgorde van het omschakelen van de pinnen in de code.

Dus tenzij je veel betere ogen hebt dan de mijne, zul je dat niet doen daadwerkelijk een knipperend effect zien.


Als een grappig extraatje kun je eigenlijk twee pinnen in de dezelfde hoeveelheid programmaruimte schakelen als één pin.

  int main () {DDRB = bit (4) | bit (5); // stel pinnen 12 en 13 in om uit te voeren terwijl (true) PINB = bit (4) | bit (5); // wissel pinnen 12 en 13} // einde van hoofd  

Dat compileert in 178 bytes.

Dit geeft je een hogere frequentie:

Very rapid blinking of pins 12 and 13

Nu zijn we op 2,66 MHz.

Dit is heel logisch. Zijn de standaardbibliotheken dus alleen kopteksten automatisch opgenomen tijdens het bouwen? En hoe kon je ze * niet * opnemen?
De linker verwijdert agressief code die niet wordt gebruikt. Door `init ()` niet aan te roepen (zoals de normale `main ()` doet), werd het bestand bedrading.c (dat `init` erin heeft) niet gekoppeld. Als resultaat was de verwerking voor de interrupt handlers ( voor `millis ()`, `micros ()` enz.) werd weggelaten. Het is waarschijnlijk niet bijzonder praktisch om het weg te laten, tenzij u de dingen nooit hoeft te timen, maar het feit is dat de schets groter wordt afhankelijk van wat u erin stopt. Als u bijvoorbeeld Serieel gebruikt, krijgen zowel het programmageheugen als het RAM een klap.


Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 3.0-licentie waaronder het wordt gedistribueerd.
Loading...