Oprócz doskonałych sugestii zawartych w innych odpowiedziach tutaj, chcę skomentować, że może istnieć ogromna różnica w tym, ile kompilatorów (i linkerów) może zoptymalizować kod.
Kilka lat temu pracowałem w firmie, w której produkt korzystał z ATMega8. Kiedy przybyłem na scenę, ten produkt miał trzy różne wersje kodu źródłowego, aby zapewnić oddzielne zestawy funkcji dla różnych konfiguracji produktu. Kod źródłowy został skompilowany przy użyciu taniego kompilatora C, a każdy zestaw kodu ledwo mieści się w 8 kilobajtach pamięci FLASH urządzenia.
Zleciłem firmie zakup wysokiej klasy kompilatora od firmy dobrze znanej w społeczności AVR. Następnie zacząłem pracować nad kodami źródłowymi oprogramowania i ustawiłem opcje kompilatora pod kątem maksymalnej optymalizacji.
Kiedy skończyłem, wszystkie opcje produktu zmieściły się w jednym obrazie, który był mniejszy niż 8 KB. W rzeczywistości było wystarczająco dużo miejsca, aby dodać do kodu programowy UART przeznaczony wyłącznie do transmisji, tak aby oprogramowanie mogło wyrzucić wewnętrzne informacje, które były używane do kalibracji parametrów produktu. Wyjście UART zostało wyzwolone, gdy sygnał 28 V został przyłożony do jednego z kanałów A / D przez dzielnik napięcia. Wyzwalacz był potrzebny, ponieważ oprogramowanie wyjściowe UART wykorzystywało GPIO, które normalnie było sygnałem, który był wyjściem z produktu.