Bạn đã có một số câu trả lời hoàn toàn tốt. Tôi đang đăng bài này chỉ để chia sẻ một số thống kê tôi đã làm vào một ngày, tôi đã tự hỏi mình cùng một loại câu hỏi: Điều gì đang chiếm quá nhiều không gian trên một bản phác thảo tối thiểu? Tối thiểu cần thiết để đạt được chức năng tương tự là gì?
Dưới đây là ba phiên bản của một chương trình mờ tối thiểu làm bật đèn LED trên chân 13 mỗi giây. Tất cả ba phiên bản đã được biên dịch cho Uno (không có USB) sử dụng avr-gcc 4.8.2, avr-libc 1.8.0 và arduino-core 1.0.5 (Tôi không sử dụng Arduino IDE).
Đầu tiên, cách Arduino chuẩn:
const uint8_t ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
Điều này biên dịch thành 1018 byte. Sử dụng cả hai avr-nm
và tháo gỡ , tôi chia kích thước đó thành các chức năng riêng lẻ. Từ lớn nhất đến nhỏ nhất:
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
Trong danh sách trên, cột đầu tiên là kích thước tính theo byte và cột thứ hai cho biết mã đến từ thư viện lõi Arduino (tổng A, 822 byte), thời gian chạy C (C, 148 byte) hay người dùng (U , 48 byte).
Như có thể thấy trong danh sách này, chức năng lớn nhất là thường trình phục vụ ngắt tràn bộ định thời 0. Thủ tục này có trách nhiệm theo dõi thời gian, và cần thiết millis()
, micros()
và delay()
. Chức năng lớn thứ hai là init()
, thiết lập bộ định thời phần cứng cho PWM, cho phép ngắt TIMER0_OVF và ngắt kết nối USART (được sử dụng bởi bộ tải khởi động). Cả hàm này và hàm trước đều được định nghĩa trong
<Arduino directory>/hardware/arduino/cores/arduino/wiring.c
.
Tiếp theo là phiên bản 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);
}
}
Bảng phân tích kích thước riêng:
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
Đây là 132 byte cho thời gian chạy C và 26 byte mã người dùng, bao gồm cả hàm được nội tuyến _delay_ms()
.
Có thể lưu ý rằng, vì chương trình này không sử dụng các ngắt, nên bảng vectơ ngắt là không cần thiết và mã người dùng thông thường có thể được đặt vào vị trí của nó. Phiên bản lắp ráp sau đây thực hiện chính xác điều đó:
#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
Điều này được tập hợp (với avr-gcc -nostdlib
) chỉ thành 14 byte, hầu hết được sử dụng để trì hoãn các toggles để có thể nhìn thấy chớp mắt. Nếu bạn loại bỏ vòng lặp trì hoãn đó, bạn sẽ thấy chương trình 6 byte nhấp nháy quá nhanh để có thể nhìn thấy (ở 2 MHz):
sbi io(DDRB), 5 ; set PB5 as output
loop:
sbi io(PINB), 5 ; toggle PB5
rjmp loop