Masz już kilka doskonale dobrych odpowiedzi. Publikuję to tylko po to, by podzielić się statystykami, które zrobiłem pewnego dnia zadałem sobie te same pytania: co zajmuje tyle miejsca na minimalnym szkicu? Jakie jest minimum potrzebne do osiągnięcia tej samej funkcjonalności?
Poniżej znajdują się trzy wersje minimalnie mrugającego programu, który przełącza diodę LED na pinie 13 co sekundę. Wszystkie trzy wersje zostały skompilowane dla Uno (bez udziału USB) przy użyciu avr-gcc 4.8.2, avr-libc 1.8.0 i arduino-core 1.0.5 (nie używam Arduino IDE).
Po pierwsze, standardowy sposób Arduino:
const uint8_t ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
To kompiluje się do 1018 bajtów. Używając obu avr-nm
i demontażu , podzieliłem ten rozmiar na poszczególne funkcje. Od największego do najmniejszego:
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
Na powyższej liście pierwsza kolumna ma rozmiar w bajtach, a druga kolumna informuje, czy kod pochodzi z biblioteki podstawowej Arduino (A, łącznie 822 bajtów), środowiska wykonawczego C (C, 148 bajtów) czy użytkownika (U , 48 bajtów).
Jak można zobaczyć na tej liście, największą funkcją jest procedura obsługująca przerwanie przepełnienia timera 0. Procedura ta jest odpowiedzialna za śledzenie czasu, a jest potrzebny millis()
, micros()
i delay()
. Druga co do wielkości funkcja init()
, która ustawia timery sprzętowe dla PWM, włącza przerwanie TIMER0_OVF i rozłącza USART (który był używany przez bootloader). Zarówno ta, jak i poprzednia funkcja są zdefiniowane w
<Arduino directory>/hardware/arduino/cores/arduino/wiring.c
.
Dalej jest wersja 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);
}
}
Podział poszczególnych rozmiarów:
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
Jest to 132 bajty dla środowiska wykonawczego C i 26 bajtów kodu użytkownika, w tym funkcji wbudowanej _delay_ms()
.
Można zauważyć, że ponieważ ten program nie używa przerwań, tablica wektorów przerwań nie jest potrzebna, a zwykły kod użytkownika może zostać wstawiony na jej miejsce. Następująca wersja zestawu robi dokładnie to:
#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
Jest to złożone (z avr-gcc -nostdlib
) w zaledwie 14 bajtów, z których większość jest wykorzystywana do opóźniania przełączania, aby widoczne było mrugnięcie. Po usunięciu tej pętli opóźnienia powstaje 6-bajtowy program, który miga zbyt szybko, aby można go było zobaczyć (przy 2 MHz):
sbi io(DDRB), 5 ; set PB5 as output
loop:
sbi io(PINB), 5 ; toggle PB5
rjmp loop