POL
ENG

GCC i G++ dla procesorów AVR

Do niedawna, do pisania programu na dany mikrokontroler używałem dedykowanego GUI, które bardzo często posiada wbudowane narzędzia do kompilacji i budowania, a nierzadko również programowania. Wraz z doświadczeniem i większym skomplikowaniem projektu naturalnym jest, że człowiek odczuwa naturalną potrzebę przetestowania skomplikowanych architektur zanim jeszcze znajdą się w prawdziwym sprzęcie. Pozwala to zaoszczędzić sporo czasu i pieniędzy w przyszłości. Stąd pojawiła się potrzeba wykorzystania testów jednostkowych. Niestety, dotychczas używane przeze mnie narzędzia nie oferują takiej funkcjonalności. Przyzwyczajenie z pracy spowodowało, że chciałem użyć GTest-a. Musiałem więc jakoś połączyć dwa typy kompilacji w jeden. To spowodowało, że musiałem się zagłębić nieco bardziej w narzędzia do budowania.

Pierwszym naturalnym wyborem okazał się CMake. Jest to naturalna ewolucja popularnego Make file. Po pierwszym spotkaniu okazało się, że kompilator użyty do budowania danej wersji oprogramowmnia jest zdefiniowany w zmiennej środowiskowej. Podobno istnieją jakieś haki, ale są one mało eleganckie. Jak się później okazało, większość systemów budowania bazuje na zmiennej środowiskowej. To zmusiło mnie do głębszego zapoznania się z procesem budowania i napisania prostego narzędzia w Python-ie.

Język skryptowy jakim jest Python, posiada wiele gotowych rozwiązań, które ułatwiają tworzenie własnych aplikacji. Jako głównego kompilatora kodu testów postanowiłem użyć GCC, którego odmianę wykorzystuję również do kompilacji kodu mikrokontrolera. I tak proste narzędzie z czasem ewoluowało i dalej to robi zwiększając liczbę parametrów jakie można jej przekazać.

Proces budowania:

Każdy pojedynczy plik C/CPP można przekompilować otrzymując plik obiektowy:

g++.exe -c example_code.cpp -o ex_code.o


Jeżeli plik z kodem źródłowym zawiera inne pliki nagłówkowe, to linii poleceń należy dołączyć ścieżki zawierające owe pliki. Służy do tego przełącznik '-I' (includes) pisane wielką literą, ponieważ podajemy ścieżkę do katalogu, a nie pliku. WAŻNE: Ścieżkę podajemy bez spacji między przełącznikiem.

g++.exe -c example_code.cpp -Icode/example/headers -Icode/different/example/headers -o ex_code.o


Wynikowy plik jest zbiorem definicji i deklaracji. Żeby podejrzeć zawartość danego pliku można posłużyć się aplikacją objdump.exe. Jako parametr przyjmuje ona plik obiektu.

Innym ciekawym przełącznikiem, który warto zastosować jest stopień optymalizacji kodu '-O'. Istnieją różne typy optymalizacji: czasu, ilości zajomwanego miejsca, optymalizacji lub zupełny brak.

Dodatkowo, korzystając z funkcjonalności preprocesora, możemy na poziomie kompilatora zdefiniować potrzebne definicje (w kodzie jako #define). Do tego celu złuży przełącznik '-D' i tak jak w przypadku katalogów, wartość podajemy bez spacji.

Podczas pisania programu, można popełnić wiele błędów. Nie wszystkie z nich są poważne i nie muszą powodować przerwania procesu kompilacji. Są to tak zwane ostrzeżenie (Warnings). Przełącznikiem '-W' możemy zdefiniować kompilatorowi, które z nich powinien nam zgłaszać.

Ostatnim z wielu ciekawych o jakim chce wspomnieć jest przełącznik, definiujący wersję standartu języka w jakim napisany jest nasz program. Jest to przełącznik '-std='.

Przykładowa rozbudowana linia polecenia:

g++.exe -Ifirmware. -Ifirmwareusbheaders -Wall -gdwarf-2 -std=c++11 -DF_CPU=16000000UL -Os -c main.cpp -o main.o


Proces linkowania:

Kiedy wszystkie pliki są już gotowe, to można je połączyć (zlinkować) w jedną całość. Zadaniem linkera jest połączenie wszystkich deklaracji funkcji z ich implementacją. Jeżeli którejś brakuje, to linker zgłosi błąd i przerwie działanie. Aby tego dokonać należy zebrać wszystkie pliki obiektowe i połączyć w jeden plik wynikowy. Najprostszy sposób wygląda następująco:

g++.exe main.o usb.o uart.o -o my_program.exe


Oczywiście pozostałe przełączniki również działają dopóki używamy tej samej aplikacji.

Generowanie plików wynikowych i podsumowań:

Gdy program jest już gotowy jako plik wykonywalny na danym systemie, to może zaistnieć potrzeba wygenerowania pliku binarnego HEX. W tym celu możemy się posłużyć aplikacją kopiującą plik i zamieniającą format wynikowy.

objcopy.exe -O ihex program.exe output_program.hex


Jeżeli chcemy poznać wynikowy rozmiar kodu, to musimy skorzystać z aplikacji size.exe

size.exe -dt program.exe


Wszelkie prawa zastrzeżone. Projekt i wykonanie strony SrcPro.pl