У вас уже есть несколько очень хороших ответов. Я публикую это только для того, чтобы поделиться статистикой, которую я однажды сделал, и я задал себе те же вопросы: что занимает так много места на минимальном наброске? Какой минимум необходим для достижения той же функциональности?
Ниже приведены три версии минимальной мигающей программы, которая переключает светодиод на контакт 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);
}
Это составляет 1018 байт. Используя 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 (C, 148 байтов) или пользователя (U 48 байт).
Как видно из этого списка, самой большой функцией является подпрограмма, обслуживающая прерывание переполнения таймера 0. Эта процедура отвечает за отслеживание времени и необходима millis()
, micros()
и delay()
. Вторая по величине функция init()
, которая устанавливает аппаратные таймеры для ШИМ, включает прерывание TIMER0_OVF и отключает USART (который использовался загрузчиком). И эта, и предыдущая функция определены в
<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 МГц):
sbi io(DDRB), 5 ; set PB5 as output
loop:
sbi io(PINB), 5 ; toggle PB5
rjmp loop