Teoretyczne minimum: kluczowe pytania z programowania mikrokontrolerów
Programiści mikrokontrolerów muszą znać specyfikę języka C w warunkach ograniczonych zasobów. Poniżej — zestaw praktycznych pytań z naciskiem na kompilację, pamięć i optymalizację. Te tematy regularnie pojawiają się na rozmowach kwalifikacyjnych w firmach zajmujących się embedded.
Kluczowe słowa kluczowe i ich zastosowanie
Static ogranicza widoczność zmiennej do bieżącego pliku lub funkcji, zachowując wartość między wywołaniami dla lokalnych static. Volatile zapobiega optymalizacji przez kompilator dla zmiennych zmienianych z zewnątrz (rejestry, pamięć, przerwania).
Przykład problematycznego kodu:
int square(volatile int *ptr) {
return *ptr * *ptr;
}
Tutaj volatile dotyczy tylko pierwszego dereferencjonowania, drugie może być zoptymalizowane. Poprawnie: (ptr) (*ptr).
Const volatile jest możliwe dla danych niezmienianych z zewnątrz, jak rejestry statusowe. Register sugeruje kompilatorowi przechowywanie zmiennej w rejestrze (przestarzałe w C11). Restrict wskazuje brak aliasingu wskaźników dla wektoryzacji.
Praca z pamięcią i strukturami
Globalna zmienna const ląduje w Flash. Dla stałej tablicy 32 KB o wartości 0xFF użyj skryptu linkera z sekcją fill(0xFF).
Operacje bitowe: sprawdzenie bitu — n & (1 << k), czyszczenie — n & ~(1 << k). Porównanie float: fabs(a - b) < EPSILON.
Rozmiar struktur zależy od wyrównania. Dla
struct Foo {
int iiii;
char c;
};
rozmiar 8 bajtów (4 bajty paddingu). Pakowanie w GCC: __attribute__((packed)).
Przykład wyświetlania stringów:
char str5[]={'s','t','r','i','n','g'}; // size:7 len:6
char *str1="string"; // size:4 len:6 (wskaźnik)
char str3[]="string"; // size:7 len:6
char str4[10]="string"; // size:10 len:6
Systemy build i toolchain
System build automatyzuje kompilację, linkowanie, zależności. Make obsługuje zmiany plików, Ninja — szybszy dzięki paralelizowaniu i prostocie.
VPATH w GNU Make szuka źródeł w podanych katalogach.
Ścieżka kodu: .c → .i (preprocesor) → .s (asembler) → .o → .elf → .bin/.hex.
Wyłączyć ostrzeżenie GCC: #pragma GCC diagnostic ignored "-Wrestrict".
Skompresować firmware: -ffunction-sections -fdata-sections, ld --gc-sections, optymalizacja -Os.
Artefakty builda
.o— pliki obiektowe.elf— plik wykonywalny z symbolami.hex/.bin— do flashowania (hex z adresami)
Binutils: objdump (dizassembler), objcopy (konwerter), readelf (analiza ELF).
Sprawdzić build pliku: make V=1 lub -n (dry-run).
Struktury danych i algorytmy
Bufor cykliczny — pierścieniowa tablica z indeksami head/tail, różni się od FIFO brakiem przesunięć.
Usuwanie z listy powiązanej: zmieniamy prev->next = node->next (potrzebny prev) lub node->next->prev = node->prev (dwukierunkowa).
Polimorfizm w C: wskaźniki na funkcje w strukturach (podobnie jak vtable).
Zadanie bez if/switch:
void print_num(int n) {
const char *nums[] = {"","Jeden","Dwa"};
printf("%s", nums[n]);
}
do-while(0) dla makr — pojedyncza iteracja z wieloma statementami.
Przerwania i RTOS
Przerwanie — sprzętowy mechanizm przejścia do ISR po zdarzeniu. Tabela wektorów zawiera adresy ISR. Cortex-M4: 12–16 taktów na wejście (zapis kontekstu).
Algorytm: hardware zapisuje PC/LR, ładuje wektor, jump do ISR, return przez POP.
Reentrant funkcja — bezpieczna dla równoległych wywołań (brak static/lokalnych bez synchronizacji).
W RTOS:
- Wątek — jednostka wykonania ze stosem
- Mutex — wzajemne wykluczanie
- Semafory — licznik zdarzeń
- Spinlock — blokada busy-wait
Problem w kodzie: deadlock z powodu kolejności lock (TaskA: ma→mb, TaskB: mb→mc→ma).
Inwersja priorytetów: wysoki priorytet czeka na niski. Rozwiązanie: dziedziczenie priorytetu.
Operacje atomowe
Przykład: __atomic_exchange_n(&x, &y, __ATOMIC_SEQ_CST).
Bit-banding — bezpośredni dostęp do bitów bez masek (Cortex-M3/4).
Toolchain i debugowanie
ABI — interfejs binarnej kompatybilności (calling convention).
Segmenty: .text (Flash kod), .data (RAM inicjowane), .bss (RAM zera), .rodata (Flash stałe).
Rozłożyć preprocesor: gcc -E file.c > expanded.i.
Dokumenty: datasheet, reference manual, programming manual, schemat.
Co ważne
- Volatile obowiązkowe dla rejestrów peryferii
- Pakowane struktury oszczędzają RAM, ale spowalniają dostęp
- RTOS wymaga kodu thread-safe i ochrony sekcji krytycznych
- Optymalizacja builda: sections + gc-sections
- Przerwania działają na stosie bieżącego wątku
Dla paddingu 0xFF w strukturach: skrypt linkera z fill lub __attribute__((packed)) + ręczne wypełnienie.
— Editorial Team
Brak komentarzy.