STM32L1 - uzyskanie niskiego poboru mocy wymaga zwrócenia uwagi na szczegóły

| Technika

Niski pobór mocy, niewielki pobierany prąd, małe zużycie energii to popularne określenia, jakimi opisuje się wiele nowoczesnych podzespołów elektronicznych, zwłaszcza mikrokontrolerów. Pozornie określenia te brzmią jak synonimy i w wielu materiałach technicznych tak są traktowane, czasem nawet zastępuje się je elastycznym jak guma pojęciem "dużej wydajności", które jest na tyle enigmatyczne, że może oznaczać dosłownie wszystko. Niemniej traktowanie tych parametrów w ten sposób przez projektantów może prowadzić do nieoptymalnych projektów.

STM32L1 - uzyskanie niskiego poboru mocy wymaga zwrócenia uwagi na szczegóły

Weźmy dwa przykładowe mikrokontrolery, jeden 16-bitowy, a drugi 32-bitowy. Gdy porównamy pobierany przez nie prąd w stanie aktywnym i w stanie low power, okaże się że jednostka 32-bitowa niekoniecznie jest lepsza od 16-bitowej (tab. 1). Gdy porównamy zużycie energii pobieranej przez te same układy w układach, które są aktywne przez 10% czasu lub 0,1%, okaże się, że w zależności od sytuacji raz jeden układ jest lepszy, raz drugi.

Tabela 1. Prąd pobierany i zużywana energia w różnych warunkach pracy dla dwóch MCU

Różnice te biorą się z innej architektury i wydajności, widać więc, że dla realizacji tego samego zadania układ 32-bitowy jest w stanie szybciej zakończyć pracę i przejść w stan uśpienia (10% vs. 7%) i tym samym zużyć mniej energii (201 µJ vs. 147 µJ). Widać, że nominalne wartości pobieranego prądu lub zużywanej energii nie dają pełnego obrazu potrzeb energetycznych i trzeba je odnieść do wydajności.

Oprogramowanie

Ogromne możliwości optymalizacji aplikacji od strony energetycznej kryją się w konstrukcji oprogramowania. Już samo włączenie optymalizacji kodu w kompilatorze pokazuje, że kosztem niewielkiego wzrostu objętości kodu wynikowego można osiągnąć blisko dwukrotną redukcję zużycia pobieranej energii (tab. 2).

Rys. 1. Przykładowe zmiany w kodzie, które zmniejszają liczbę instrukcji niezbędnych do wykonania określonego zadania

Idąc tym tropem, można stwierdzić, że statystycznie w aplikacji działającej nieprzerwanie przez 5 lat z wykorzystaniem typowego mikrokontrolera każda instrukcja wykonywana jest ok. 160 mln razy. Usunięcie z kodu tylko 1 instrukcji jest w stanie wydłużyć czas działania o około 2 dni. Duża wydajność przetwarzania mikrokontrolera 32-bitowego pozwala projektantowi na użycie nieoptymalnego kodu pod względem objętości i taką jego konstrukcję, aby liczba instrukcji była minimalna.

Rys. 2. Rezygnacja z wywołań podprogramów poprzez wbudowanie kodu podprogramu do głównego wątku pozwala zaoszczędzić na wykonaniu instrukcji call/return

Przykłady drobnych zmian, które przynoszą takie korzyści, pokazane zostały na rysunkach 1a-d. Kosztem rozbudowy kodu o dwa bajty zaoszczędzono 2 instrukcje (b), natomiast zastąpienie krótkiej pętli ośmiokrotnym podstawieniem "ręcznym elementów tablicy", pozwoliło skrócić wykonywanie programu o 56 skoków warunkowych (c).

Takie zmiany nie są dokonywane przez kompilator w ramach optymalizacji. Podobne efekty może dać rezygnacja z wywołań podprogramów poprzez wbudowanie kodu podprogramu do głównego wątku, nawet kilka razy pod rząd, jak sugeruje to rysunek 2. Nie jest to rozwiązanie, które można określić jako eleganckie i zgodne ze sztuką programowania, znacząco rozbudowuje też wielkość kodu, ale pozwala zaoszczędzić kilka wywołań podprogramu i instrukcji powrotu.

Rys. 3. Redukcja powtórek operacji i wykorzystanie większej liczby zmiennych do przechowywania wyników cząstkowych też jest sposobem na wymierne oszczędności

Warto popracować też nad obliczeniami matematycznymi, redukując powtórki operacji i wykorzystując większą liczbę zmiennych do przechowywania wyników cząstkowych (rys. 3). Ponownie trudno to uznać za optymalne działanie z punktu widzenia zajętości pamięci, ale jest to korzystne, jeśli chodzi o liczbę instrukcji wykonywanych w ramach zadania.

W większości nowoczesnych mikrokontrolerów zasoby pamięci operacyjnej rzadko stanowią ograniczenie, stąd warto z tych możliwości korzystać. Obliczenia matematyczne też warto przemyśleć po kątem optymalizacji, zamiast wykonywać je na siłę za pomocą jednostki FPU. Przykład, jak drobna zmiana pozwala na trzykrotne przyspieszenie wykonania pierwiastkowania, pokazany został na rysunku 4, gdzie operację zamieniono na dwukrotne potęgowanie.

Rys. 4. Zamiast pierwiastkować lepiej dwa razy potęgować

Optymalizacja kodu pod kątem energetycznym to także wykorzystanie możliwości tkwiących w warstwie sprzętowej mikrokontrolerów STM32. Wybrane operacje akwizycji danych za pomocą przetwornika ADC mogą być realizowane bez udziału jednostki centralnej bezpośrednio pomiędzy przetwornikiem a pamięcią. CPU jest aktywowane dopiero po zakończeniu akwizycji w celi przetworzenia danych. Daje to znaczące oszczędności energii.

Podsumowanie

Tabela 2. Wielkość kodu przykładowego programu i energia zużywana na jego wykonanie

Uzyskanie minimalnego zużycia energii zasilającej wymaga zwrócenia uwagi na szczegóły i wykorzystania wszystkich dostępnych mechanizmów składających się na aplikację embedded, takich jak architektura mikrokontrolera, układy peryferyjne, opcje optymalizacji oprogramowania tkwiące w kompilatorze, optymalizacja algorytmu i kodu. Z punktu widzenia oszczędności energii wydajny procesor 32-bitowy jest najlepszym rozwiązaniem, gdyż duża wydajność obliczeniowa pozwala skrócić do minimum czas jego aktywności.

Robert Magdziak

Zobacz również