Você já tem respostas perfeitamente boas. Estou postando isso apenas para compartilhar algumas estatísticas que fiz um dia e me fiz o mesmo tipo de pergunta: O que está ocupando tanto espaço em um esboço mínimo? Qual é o mínimo necessário para alcançar a mesma funcionalidade?
Abaixo estão três versões de um programa intermitente mínimo que alterna o LED no pino 13 a cada segundo. Todas as três versões foram compiladas para um Uno (sem USB envolvido) usando o avr-gcc 4.8.2, avr-libc 1.8.0 e o arduino-core 1.0.5 (não uso o IDE do Arduino).
Primeiro, a maneira padrão do Arduino:
const uint8_t ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
Isso compila para 1018 bytes. Usando ambos avr-nm
e desmontagem , eu dividi esse tamanho em funções individuais. Do maior para o menor:
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 lista acima, a primeira coluna é o tamanho em bytes, e a segunda coluna informa se o código é proveniente da biblioteca principal do Arduino (A, 822 bytes no total), do tempo de execução C (C, 148 bytes) ou do usuário (U , 48 bytes).
Como pode ser visto nesta lista, a maior função é a rotina de manutenção da interrupção de estouro do timer 0. Essa rotina é responsável pelo rastreamento do tempo e é necessária por millis()
, micros()
e delay()
. A segunda maior função é init()
, que define os temporizadores de hardware para o PWM, permite a interrupção do TIMER0_OVF e desconecta o USART (que foi usado pelo carregador de inicialização). Tanto esta como a função anterior são definidas em
<Arduino directory>/hardware/arduino/cores/arduino/wiring.c
.
A seguir está a versão 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);
}
}
A discriminação dos tamanhos individuais:
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
São 132 bytes para o tempo de execução C e 26 bytes de código do usuário, incluindo a função embutida _delay_ms()
.
Pode-se observar que, como esse programa não usa interrupções, a tabela de vetores de interrupção não é necessária e o código do usuário comum pode ser colocado em seu lugar. A seguinte versão do assembly faz exatamente isso:
#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
Isso é montado (com avr-gcc -nostdlib
) em apenas 14 bytes, a maioria dos quais é usada para atrasar as alternâncias para que o piscar fique visível. Se você remover esse loop de atraso, você terminará com um programa de 6 bytes que pisca rápido demais para ser visto (a 2 MHz):
sbi io(DDRB), 5 ; set PB5 as output
loop:
sbi io(PINB), 5 ; toggle PB5
rjmp loop