Duża różnorodność odpowiedzi tutaj ... głównie na różne sposoby.
Od ponad 25 lat piszę wbudowane oprogramowanie niskopoziomowe i oprogramowanie układowe w różnych językach - głównie C (ale z różnymi wersjami Ady, Occam2, PL / M i różnych asemblerów po drodze).
Po długim okresie refleksji i prób i błędów zdecydowałem się na metodę, która dość szybko uzyskuje wyniki i dość łatwo jest tworzyć opakowania testowe i uprzęże (w których DODAJĄ WARTOŚĆ!)
Metoda przebiega mniej więcej tak:
Napisz sterownik lub jednostkę kodu abstrakcji sprzętu dla każdego głównego urządzenia peryferyjnego, którego chcesz użyć. Napisz też jeden z nich, aby zainicjować procesor i skonfigurować wszystko (to tworzy przyjazne środowisko). Zwykle na małych procesorach osadzonych - na przykład twój AVR - może być 10-20 takich jednostek, wszystkie małe. Mogą to być jednostki inicjujące, konwersja A / D do nieskalowanych buforów pamięci, wyjście bitowe, wejście przycisku (bez próbkowania tylko próbkowania), sterowniki modulacji szerokości impulsu, proste sterowniki szeregowe UART / przerwania użycia i małe bufory we / wy. Może być jeszcze kilka innych - np. Sterowniki I2C lub SPI dla EEPROM, EPROM lub innych urządzeń I2C / SPI.
Następnie dla każdej jednostki abstrakcji sprzętu (HAL) / sterownika piszę program testowy. Zależy to od portu szeregowego (UART) i inicjacji procesora - więc pierwszy program testowy wykorzystuje tylko te 2 jednostki i po prostu wykonuje podstawowe operacje wejścia i wyjścia. To pozwala mi przetestować, czy mogę uruchomić procesor i czy mam podstawową obsługę szeregowego we / wy debugowania. Kiedy to zadziała (i tylko wtedy), czy opracowuję inne programy testowe HAL, budując je na znanych urządzeniach UART i INIT. Mogę więc mieć programy testowe do odczytu danych bitowych i wyświetlania ich w ładnej formie (szesnastkowa, dziesiętna, cokolwiek) na moim terminalu szeregowego debugowania. Następnie mogę przejść do większych i bardziej złożonych rzeczy, takich jak programy testowe EEPROM lub EPROM - większość tych menu steruję, więc mogę wybrać test do uruchomienia, uruchomić go i zobaczyć wynik. Nie mogę tego PRZESŁAĆ, ale zwykle nie
Po uruchomieniu całego HAL znajduję sposób na uzyskanie regularnego tykania timera. Zazwyczaj jest to z częstotliwością pomiędzy 4 a 20 ms. To musi być regularne, generowane w przerwie. Rolowanie / przepełnienie liczników jest zwykle sposobem, w jaki można to zrobić. Program obsługi przerwań INCREMENTS następnie „semafor” o rozmiarze bajtu. W tym momencie możesz także majstrować przy zarządzaniu energią, jeśli potrzebujesz. Idea semafora polega na tym, że jeśli jego wartość wynosi> 0, musisz uruchomić „główną pętlę”.
EXECUTIVE uruchamia główną pętlę. Prawie tylko czeka na tym semaforze, aby stał się niezerowy (odciągam ten szczegół od siebie). W tym momencie możesz bawić się licznikami, aby policzyć te tiki (ponieważ znasz częstotliwość tykania), dzięki czemu możesz ustawić flagi pokazujące, czy bieżący tik wykonawczy trwa przez 1 sekundę, 1 minutę i inne typowe interwały może chcieć użyć. Gdy dyrektor wykonawczy wie, że semafor ma wartość> 0, uruchamia pojedyncze przejście przez każdą funkcję „aktualizacji” procesów „aplikacji”.
Procesy aplikacyjne skutecznie siedzą obok siebie i są uruchamiane regularnie przez zaznaczenie „aktualizacja”. Jest to tylko funkcja wywoływana przez organ wykonawczy. Jest to efektywne, wielozadaniowe narzędzie z bardzo prostym, domowym systemem RTOS, który polega na wchodzeniu do wszystkich aplikacji, pracy i wyjściu. Aplikacje muszą utrzymywać własne zmienne stanu i nie mogą wykonywać długotrwałych obliczeń, ponieważ nie ma wyprzedzającego systemu operacyjnego, który wymusiłby uczciwość. Oczywiście czas działania aplikacji (narastająco) powinien być mniejszy niż główny okres zaznaczania.
Powyższe podejście można łatwo rozszerzyć, dzięki czemu można dodawać takie stosy komunikacyjne, które działają asynchronicznie, a komunikaty komunikacyjne mogą być następnie dostarczane do aplikacji (dodajesz nową funkcję do każdej z nich, która jest „modułem obsługi rx_message” i piszesz dyspozytora komunikatów, który przedstawia do której aplikacji wysłać).
To podejście działa na prawie każdym systemie komunikacyjnym, który chcesz nazwać - może (i wykonało) pracę na wielu zastrzeżonych systemach, systemach komunikacji z otwartymi standardami, a nawet na stosach TCP / IP.
Ma również tę zaletę, że składa się z elementów modułowych z dobrze zdefiniowanymi interfejsami. W każdej chwili możesz wciągać i wyjmować elementy, zamieniać różne elementy. W każdym punkcie po drodze możesz dodać uprząż testową lub uchwyty, które opierają się na znanych dobrych częściach dolnej warstwy (poniżej). Odkryłem, że około 30% do 50% projektu może zyskać na dodaniu specjalnie napisanych testów jednostkowych, które zazwyczaj są dość łatwe do dodania.
Zrobiłem to o krok dalej (pomysł, który podrobiłem od kogoś, kto to zrobił) i zastąpiłem warstwę HAL odpowiednikiem na PC. Na przykład możesz używać C / C ++ i winforms lub podobnych na komputerze PC i PORZĄDKU pisząc kod, możesz emulować każdy interfejs (np. EEPROM = plik dyskowy wczytany do pamięci komputera), a następnie uruchomić całą wbudowaną aplikację na komputerze. Możliwość korzystania z przyjaznego środowiska debugowania pozwala zaoszczędzić dużo czasu i wysiłku. Tylko naprawdę duże projekty zazwyczaj uzasadniają taki wysiłek.
Powyższy opis jest czymś, co nie jest unikalne w tym, jak robię rzeczy na platformach osadzonych - natknąłem się na wiele organizacji komercyjnych, które robią podobnie. Sposób jego wykonania jest zwykle bardzo różny we wdrażaniu, ale zasady są często takie same.
Mam nadzieję, że powyższe daje trochę smaku ... to podejście działa w przypadku małych systemów wbudowanych, które działają w kilku kilobajtach z agresywnym zarządzaniem baterią, aż do potworów o mocy 100 000 lub więcej linii zasilanych na stałe. Jeśli uruchamiasz „osadzony” na dużym systemie operacyjnym, takim jak Windows CE itp., Wszystkie powyższe elementy są całkowicie nieistotne. Ale tak czy inaczej, to nie jest PRAWDZIWE oprogramowanie wbudowane.