Sytuacja kobiet w IT w 2024 roku
25.02.20197 min
Karol Dudek
Comarch

Karol DudekStarszy ProgramistaComarch

Na co zwrócić uwagę przy projektowaniu i uruchamianiu urządzeń IoT

Sprawdź, na co zwrócić uwagę przy projektowaniu i uruchamianiu urządzeń IoT

Na co zwrócić uwagę przy projektowaniu i uruchamianiu urządzeń IoT

Projektowanie systemów wbudowanych to skomplikowany proces, a decyzje, które podejmiemy na samym początku, mają duży wpływ na kolejne etapy. Zdefiniowanie kluczowych wymagań jest ważnym punktem, od którego należy rozpocząć prace. To na ich podstawie wypracowuje się koncepcje działania systemu i wychwytuje możliwe problemy.

W kolejnym kroku inżynierowie dobierają najważniejsze podzespoły systemu, uwzględniając przy tym parametry techniczne, cenę i dostępność komponentów. Duża ilość aspektów, która musi zostać przeanalizowana, zwiększa ryzyko popełnienia błędu, co w efekcie może prowadzić do niepowodzenia całego przedsięwzięcia. Etap ten należy przeprowadzić ze szczególną starannością i dbałością o każdy szczegół.

W systemach wbudowanych, oprogramowanie i sprzęt są ściśle powiązane. Dlatego granica dzieląca te dwa elementy jest bardzo cienka. Czasem szukamy błędu programowego, debugując firmware, podczas gdy usterka występuje po stronie sprzętu lub na odwrót. Trudno jest zdefiniować jedyny właściwy sposób postępowania w momencie wykrycia niewłaściwego działania.

Podczas analizy problemu warto zdawać sobie sprawę, że oprócz ograniczonych zasobów sprzętowych mikrokontrolerów (niewielka ilość pamięci i niska częstotliwość taktowania), systemy wbudowane mają dodatkowy parametr - czas. Często używamy pojęcia systemu czasu rzeczywistego w kontekście systemów wbudowanych. Jeśli czas reakcji będzie niewystarczający, system nie jest uznawany za działający, nawet jeśli pod względem funkcjonalnym jest prawidłowy. Przykładem błędnego działania może być zbyt późne zadziałanie poduszek powietrznych podczas zderzenia samochodów.

Jak przygotować się do debugowania?

Jeśli uczestniczysz w projekcie od samego początku, a pierwszy prototyp dopiero powstaje, to jesteś w dobrym położeniu. Na początku przeanalizuj bardzo dokładnie schemat i postaraj się zrozumieć, jak urządzenie ma działać. Jeśli czegoś nie rozumiesz, nie przechodź do kolejnych bloków, bo i tak podczas późniejszego uruchamiania będziesz musiał je rozumieć. W przypadku gdy nadal masz wątpliwości, poproś projektanta o wytłumaczenie idei, jaką ma realizować dany blok.

Po zapoznaniu się, w jaki sposób ma działać cały system, dobrze jest zastanowić się, z jakimi problemami możemy się spotkać i co ułatwiłoby ich debugowanie. W większości przypadków dobrym pomysłem jest umieszczenie LED’ów diagnostycznych. Nie można również pominąć wyprowadzenia kluczowych sygnałów w postaci test PAD’ów na płytce PCB. Jest to bardzo istotne w dobie miniaturyzacji, gdzie podzespoły są bardzo małe, a obwody drukowane wielowarstwowe. Dla typowej aplikacji warto mieć bezpośredni dostęp do:

- wszystkich linii zasilania - umożliwia sprawdzenie poziomu napięcia i jakości zasilania podczas pracy pod obciążeniem
- linii magistral szeregowych - diagnostyka komunikacji między zewnętrznymi peryferiami
- linii wyzwalających przerwania IRQ
- innych linii ważnych z punktu widzenia funkcjonowania urządzenia, np. sygnałów analogowych wchodzących na przetwornik ADC
- sygnału nadmiarowego, służącego do wyzwalania oscyloskopu lub analizy przy użyciu analizatora logicznego np. pomiar czasu wykonywania funkcji obsługi przerwania.

Zaplanowanie kwestii dotyczących oprogramowania i sposobów jego weryfikacji jest równie istotne. Warto na tym etapie odpowiedzieć sobie na kilka pytań:

- Jakim kanałem komunikacyjnym będą przesyłane informacje diagnostyczne? Może okazać się, że semihosting będzie niewygodny lub niewystarczający i warto pomyśleć o wykorzystaniu magistrali UART, lub SPI.
- Czym będzie można zmieniać nastawy, kontrolować sterowanie i wyzwalać funkcje testujące na urządzeniu?
- Co będzie odpowiadać za kontrolowanie zajętości stosu i pamięci RAM?
- W jaki sposób mierzyć wykorzystanie zasobów CPU podczas normalnej pracy aplikacji?

Tworzenie oprogramowania

Opracowywanie architektury

Podczas opracowywania architektury całego systemu, należy jasno rozgraniczyć bloki działające blisko sprzętu - np. sterowniki do obwodów peryferyjnych oraz moduły, które implementują konkretne funkcje. Podzielenie aplikacji na moduły zwiększa przejrzystość kodu i upraszcza debugowanie. Jeśli tylko to możliwe, nie analizuj błędów w kodzie algorytmu bezpośrednio na urządzeniu docelowym. Lepiej jest tworzyć program niezależny od sprzętu na komputer i pokryć je odpowiednimi testami.

Łatwiejszy dostęp do przetwarzanych danych zwiększa możliwości debugowania oraz efektywność pracy. Tutaj należy zwrócić uwagę, aby wspomniane fragmenty kodu, były pisane w sposób umożliwiający przenoszenie pomiędzy platformami. Niesie to za sobą kolejne korzyści. Jeśli zdecydujemy się użyć danego modułu do innego projektu działającego na zupełnie innym sprzęcie, z dużym prawdopodobieństwem uruchomimy go bez większych problemów.

Płytki ewaluacyjne

Już na etapie projektowania urządzenia warto wyposażyć się w płytki ewaluacyjne i rozpocząć przygotowanie oprogramowania. Równoległe prace projektowe zespołów odpowiedzialnych za sprzęt i oprogramowanie pozwalają przyspieszyć proces powstawania działającego prototypu. Daje to również możliwość przetestowania niepewnych idei, jeszcze przed zamówieniem obwodów drukowanych. Zwiększa to szanse na wyprodukowanie w pełni działającego prototypu już w pierwszej iteracji.

Organizacja pracy

Zespół programistów powinien tak zorganizować swoją pracę, aby w pierwszej kolejności zaimplementować obsługę wszystkich driverów i komponentów współpracujących ze sprzętem. Przy wykorzystaniu bloków obsługujących najniższą warstwę, przygotowuje się testy sprawdzające współdziałanie podzespołów.

Jeżeli urządzenie docelowo będzie zasilane z baterii lub akumulatora, warto przygotować drugi wariant programu, który wprowadzi target w stan możliwie najniższego zużycia energii. Daje to możliwość weryfikacji prototypu zaraz po jego zmontowaniu oraz prowadzi do szybszego znalezienia ewentualnych problemów, które zgłasza się projektantom. Dopiero po przygotowaniu firmware testującego, należy przejść do implementowania modułów funkcjonalnych.

Forma programu

Jeżeli chodzi o formę samego programu, warto pamiętać o częstszym użyciu asercji w kodzie. Poprzez wykorzystanie makra ASSERT, możliwe jest wychwycenie nieoczekiwanych okoliczności w czasie działania aplikacji. Sprowadza się to do tego, że programiści mogą weryfikować, czy założenia w ich kodzie są prawdziwe. Używanie asercji zdecydowanie pomaga w znajdowaniu nieprawidłowości lub błędów założeń. Niestety wielu inżynierów nie umieszcza ich w swoich programach.

Ciekawym pomysłem jest również dodanie instrukcji włączającej czerwoną diodę LED oraz wyzwolenie software breakpoint w funkcji implementującej obsługę asercji. Pozwala to na wychwycenie nieprawidłowości, nawet jeżeli target w danym momencie nie jest podłączony do debugera. Mało tego, jeśli podłączymy płytkę do debugera (bez odłączenia zasilania), jesteśmy w stanie stwierdzić, co spowodowało zatrzymanie.

Uruchomienie prototypu

Podczas pierwszego uruchomienia prototypu urządzenia, warto podłączyć go do zasilacza laboratoryjnego z możliwością monitorowania i ograniczenia poboru prądu. Jeżeli płyta nie pobiera więcej prądu, niż się tego spodziewamy, to weryfikujemy multimetrem poziomy napięć wszystkich sekcji zasilania. Dopiero po upewnieniu się, że zasilanie jest poprawne, podłączamy debuger i rozpoczynamy dalsze prace.

Kolejny etap to zaprogramowanie urządzenia docelowego, przygotowanym wcześniej programem testującym i szczegółowa analiza działania peryferii oraz komponentów współpracujących. Podczas uruchamiania można niestety napotkać dużo problemów. Nowoczesne mikrokontrolery posiadają rozbudowane możliwości konfiguracyjne z dużą ilością rejestrów sterujących i statusowych dla jednego bloku, a ich ustawienie nie zawsze jest oczywiste. Przeglądanie tylko jednego dokumentu z reguły nie wystarcza. Gdy sprzęt nie działa prawidłowo, zmuszeni jesteśmy do wielokrotnej analizy kilku not katalogowych jednocześnie.

Zanim spróbujesz weryfikować inną funkcjonalność, upewnij się, czy wszystkie sterowniki działają prawidłowo i można na nich polegać. Aby uniknąć działania urządzenia w sposób nieprzewidywalny, warto między innymi sprawdzić:

- zapis i odczyt z pamięci zewnętrznych, zwłaszcza z interfejsem równoległym
- częstotliwość działania magistral SPI i I²C w dozwolonym zakresie zewnętrznych komponentów
- działanie modułów mikrokontrolera, np. czas konwersji ADC

Dobrą praktyką jest dokumentowanie wyników przeprowadzonych testów. Na przykład poprzez zapisanie zrzutu ekranu, przedstawiającego zarejestrowane przebiegi działania magistrali SPI, z prawidłową częstotliwością i zależnością fazową od innych sygnałów.

Debugowanie

Cechą charakterystyczną systemów wbudowanych jest to, że błędy mogą być umiejscowione nie tylko w oprogramowaniu, ale także w sprzęcie. Podczas debugowania szukamy usterek w kodzie, ale również konfliktów sprzętowych, zakłóceń czy niedopasowanych opóźnień. Często problemy sprzętowe próbuje się usuwać za pomocą obejść programowych. Jest to efektywniejsze pod względem ekonomicznym, ale nie zmienia to faktu, że przyczynę problemu i tak trzeba znaleźć.

Podstawa sprawnego debugowania

Podstawa sprawnego debugowania to możliwość zapisywania zdarzeń lub wartości w celu ich dalszej analizy. Często realizuje się to poprzez wydzielenie fragmentu pamięci RAM lub Flash i sukcesywnego dopisywania kolejnych zdarzeń. Po zakończeniu interesującego nas cyklu można zatrzymać debuger lub pobrać dane poprzez dowolny interfejs. Jeżeli bufor znajduje się w pamięci RAM, zyskujemy możliwość rejestrowania zdarzeń z bardzo dużą szybkością. Minusem buforów śledzących jest to, że zajmują spory fragment pamięci, a zarejestrowane dane trudniej nałożyć na zewnętrzne zdarzenia sprzętowe.

Zamiast kolekcjonować dane w pamięci RAM, można wysłać je, wykorzystując kilka pinów lub szybkie interfejsy szeregowe, np. SPI i przechwytywać za pomocą analizatora logicznego. Zmniejsza to zapotrzebowanie na pamięć, a nadmiarowy kod nie zajmuje dużo miejsca i jest szybki. Przy takim podejściu, jedyne ograniczenie to dostępność wyprowadzeń po stronie urządzenia docelowego oraz pojemność pamięci analizatora. Zewnętrzna synchronizacja zdarzeń przestaje być problemem, a używanie stosunkowo nowego analizatora logicznego, daje dużo możliwości wyzwalania.

Im więcej pinów posiadasz, tym łatwiej można przesyłać dane z urządzenia. Zwykle zapasowych pinów jest niewiele lub nie ma do nich łatwego dostępu, co wymusza „bycie kreatywnym”. Dobrą praktyką projektową jest wyprowadzenie pozostałych nieużywanych sygnałów, zwłaszcza przy projektowaniu pierwszego prototypu. Umożliwia to sprawniejsze debugowanie oraz rozszerzanie możliwości ewaluacji kolejnych prototypów.

Częste awarie aplikacji

Gdy obserwujesz częste awarie aplikacji z nieznanych powodów, powinieneś zaplanować przygotowanie prostszego przypadku, zamiast kontynuować debugowanie z kompletną funkcjonalnością. Ograniczy to ilość czynników, które mogą powodować błąd, a zatem debugowanie będzie wymagało mniej wysiłku.

W kolejnych krokach można kontynuować „rozbieranie” aplikacji tak, aby błąd był wciąż powtarzalny. Uzyskana w ten sposób uproszczona wersja aplikacji ma mniej zależności, przez co łatwiej jest znaleźć źródło problemu. Zawsze staraj się patrzeć na problem szeroko. Nie koncentruj się tylko na jednym aspekcie. Usterka może występować w oprogramowaniu aplikacyjnym, sterowniku lub sprzęcie. Jeśli modyfikacja warunków pracy (np. temperatura, szybkość magistrali) powoduje zmianę częstości występowania problemu, to prawdopodobnie przyczyna związana jest ze sprzętem.

Podsumowanie

Jak widać, uruchamianie prototypów urządzeń nie należy do czynności prostych. O ich finalnym powodzeniu może decydować wiele czynników. Ważne, żeby skrupulatnie podchodzić do wszystkich działań i nie poprzestawać w dążeniu do doskonałości, która - jak wszyscy wiemy - chyba nie istnieje ☺

<p>Loading...</p>