Kurs STM32L4 – #6 – oszczędzanie energii (5 lat na baterii?)

Kurs STM32L4 – #6 – oszczędzanie energii (5 lat na baterii?)

Energooszczędne urządzenia, które na niewielkiej baterii mogą pracować przez długie miesiące, to ciekawe zagadnienie. Warto więc zapamiętać, że jedną z cech STM32L4 jest niski pobór prądu.

Tym razem sprawdzimy, jak w stosunkowo prosty sposób można znacznie ograniczyć pobór prądu naszego układu, zmieniając tylko kod programu.

Czego dowiesz się z tej części kursu STM32L4?

Obniżenie taktowania mikrokontrolera i wprowadzenie go w tryby uśpienia to coś, dzięki czemu nasze urządzenie może być ekstremalnie energooszczędne. Wykonując ćwiczenia z tej części kursu, dowiesz się, jak w prosty sposób obniżyć prąd, który pobiera mikrokontroler. Przyda się tu m.in. znajomość zegara RTC, który omówiliśmy w poprzedniej części kursu. Wykonując ćwiczenia, prześledzisz dokładnie kolejne etapy optymalizacji, które sprawią, że układ, który pobierał średnio 16,36 mA będzie pobierał średnio 5,1 µA (1 µA w uśpieniu) i mógłby pracować 5 lat na pastylkowej baterii CR2032!

Dzięki zmianom w kodzie nasz układ pobiera w stanie uśpienia tylko 1 µA

Dzięki zmianom w kodzie nasz układ pobiera w stanie uśpienia tylko 1 µA

Ta część kursu jest trochę inna…

Już na wstępie uprzedzamy, że ta część kursu jest trochę inna od pozostałych. Projektowanie i budowa energooszczędnych urządzeń to skomplikowany temat, który wykracza poza zakres kursu dla osób początkujących. Trudność polega na tym, że wiele zależy od tego, co dokładnie ma robić dany układ, do tego wymagana jest dobra znajomość elektroniki, a dokładne poznanie trybów oszczędzania energii danego mikrokontrolera to temat na długie godziny studiowania dokumentacji.

Dlatego postanowiliśmy, że w tej części naszego kursu programowania STM32L4 przedstawimy ogólnie temat energooszczędności i pokażemy bardzo prosty przykład praktyczny. Naszym głównym celem było opisanie tego zagadnienia w taki sposób, aby zainteresować nim jak najwięcej osób.

Krótki wstęp teoretyczny: powtórka z fizyki

Zakładamy, że na tym etapie pojęcia prądu i napięcia (oraz sposoby ich pomiaru) są wszystkim znane. W przypadku problemów zachęcamy do lektury naszego kursu elektroniki oraz wpisu, który tłumaczy, czym w elektronice jest moc. Mamy jednak świadomość, że nie wszyscy czytelnicy pamiętają np. wzory związane z mocą i pracą – stąd poniższe, krótkie przypomnienie.

Moc elektryczna i praca

W ramach szybkiego przypomnienia: wyobraźmy sobie urządzenie, które zasilamy z 5 V, a ono pobiera 500 mA. Jeśli pomnożymy te dwie wartości, to otrzymamy moc elektryczną (wyrażaną w watach) tego układu – tutaj byłoby to 5 V * 0,5 A = 2,5 W.

Znając moc danego urządzenia, możemy policzyć pobór energii. Praca to iloczyn mocy i czasu. Jeśli weźmiemy moc wyrażoną w watach, a czas pracy będzie podany w sekundach, to wynik zostanie wyrażony w dżulach. Czyli – kontynuując nasz przykład – urządzenie o mocy 2,5 W, które pracuje przez godzinę (3600 sekund), „wykonuje pracę” o wartości 9000 J.

Elektronicy w swojej pracy raczej sporadycznie korzystają z takiej jednostki jak dżule

Elektronicy w swojej pracy raczej sporadycznie korzystają z takiej jednostki jak dżule

Obliczyliśmy „coś”, ale raczej nasz multimetr nie mierzy niczego w dżulach, tym bardziej nie będziemy szukać baterii na podstawie tak obliczonego zapotrzebowania. Tak samo nasze rachunki za prąd nie są przysyłane w dżulach, bo jest on rozliczany w tzw. kilowatogodzinach. Ta jednostka jest już znacznie bardziej intuicyjna – mówi po prostu, ile energii pobierze urządzenie o mocy 1000 W, które będzie działało przez godzinę. Możemy więc przeliczyć, że 1 kWh = 1000 W * 3600 s = 3 600 000 J.

Pojemność akumulatorów

Weźmy np. akumulator samochodowy o napięciu 12 V i pojemności 60 Ah. Amperogodziny możemy interpretować jako prąd, który akumulator mógłby dostarczać do zasilanego układu przez godzinę, zanim zostałby rozładowany. Czyli skoro akumulator ma pojemność 60 Ah, to gdybyśmy pobierali z niego 60 A, w teorii uległby rozładowaniu po jednej godzinie.

Akumulator samochodowy

Akumulator samochodowy

Jest to uproszczone rozumowanie, bo pobieranie tak dużego prądu nie byłoby zbyt dobrym pomysłem i pewnie uszkodziłoby akumulator (należałoby to sprawdzić w dokumentacji). Ale gdybyśmy podłączyli do niego urządzenie, które pobiera 6 A, wtedy taki układ mógłby spokojnie działać przez 10 h.

Jak to się ma do energii? Mamy ampery i czas, znamy też napięcie. Możemy policzyć zgromadzoną w akumulatorze energię: W = 12 V * 60 Ah = 720 Wh = 0,72 kWh = 2592000 J. Widać więc, że zarówno kilowatogodziny, amperogodziny, jak i znane z fizyki dżule pozwalają nam określić ilość energii.

Zmienność poboru prądu w czasie

Migająca dioda, większe obciążenie mikrokontrolera, albo wykorzystywanie peryferii – to wszystko ma wpływ na chwilowy pobór prądu przez urządzenie i sprawia, że również moc chwilowa ulega zmianom. W związku z tym, aby policzyć energię pobieraną przez urządzenie, musielibyśmy podzielić czas na małe przedziały, w każdym dokonać pomiarów, a następnie zsumować odpowiednio wyniki.

Pobór prądu przez większość urządzeń zmienia się w czasie

Pobór prądu przez większość urządzeń zmienia się w czasie

Mówiąc „po matematycznemu”, musielibyśmy dokonać całkowania, które uwielbiają wszyscy studenci. Spokojnie, nie będziemy tutaj nic całkować, ale warto zapamiętać, że mnożenie mocy przez czas, aby policzyć energię, ma sens tylko wtedy, gdy moc urządzenia jest w przybliżeniu stała. Jeśli ulega zmianie, bo np. usypiamy mikrokontroler, musimy podzielić czas na przedziały i policzyć wszystko oddzielnie.

Podejście praktyczne

W praktyce sprawa wygląda tak, że najczęściej elektronik dysponuje informacją, że akumlator(ek) ma pojemność równą ileś Ah lub nawet częściej mAh (miliamperogodzin). Jeśli założymy, że mamy dobry akumulator, i pominiemy np. efekt starzenia się ogniw, to możemy uznać, że jego pojemność jest stała.

Przykładowy akumulator o napięciu znamionowym 9,6 V i pojemności 600 mAh

Przykładowy akumulator o napięciu znamionowym 9,6 V i pojemności 600 mAh

Nas interesuje, na ile czasu wystarczy akumulator, jeśli podłączymy do niego urządzenie pobierające ileś miliamperów. Czyli wykorzystujemy wzór na moc przekształcony do następującej postaci: t = W / P. Ilość energii zależy od wybranego modelu akumulatora, natomiast tym, na co mamy wpływ, pisząc program, jest pobierana moc. Z logiki (oraz z powyższego wzoru) wynika, że im więcej pobieramy mocy, tym krócej układ będzie działał przy danym rodzaju zasilania.

Napięcie zasilające układ jest najczęściej stałe, więc jedyną zmienną jest prąd pobierany przez układ. Dlatego w dalszej części tego artykułu będziemy mierzyli prąd pobierany przez układ i na tej podstawie będziemy szacowali, jak długo mógłby pracować, gdybyśmy zasilali go z konkretnego źródła zasilania.

STM32L4, czyli L jak ultra-low-power

STM32L476RG jest częścią rodziny STM32L4, a litera L w symbolu tych mikrokontrolerów to skrót od określenia ultra-low-power. Oznacza to, że układy te wyposażono w mechanizmy, dzięki którym mogą one pobierać znikomy prąd (nawet pojedyncze mikroampery).

Ważne jest podkreślenie faktu, że „mogą one pobierać znikomy prąd”. Mikrokontrolery STM32L4 nie zrobią wszystkiego za nas. To od programisty zależy, czy wykorzysta opcje, które przewidział producent układu, aby obniżyć zużycie energii. Zmiana mikrokontrolera i wgranie „zwykłego kodu” na STM32L4 nie sprawi nagle, że układ zacznie działać na baterii przez wiele lat. Wszystko zmieni się jednak, gdy świadomie wykorzystamy np. dostępne tryby usypiania układu.

Mowa tutaj o specjalnych trybach pracy, w których układ obniża swoją wydajność i ogranicza dostępne peryferia – wszystko po to, aby pobierał on coraz mniejszy prąd. Przełączanie mikrokontrolera w taki energooszczędny stan najczęściej określa się jako usypianie, a powrót do pełnej wydajności nazywamy budzeniem mikrokontrolera. Oto przykładowe (nie wszystkie) tryby pracy układów STM32L4:

Przykładowe tryby pracy, które pozwalają obniżyć pobór prądu (z dokumentacji mikrokontrolera)

Przykładowe tryby pracy, które pozwalają obniżyć pobór prądu (z dokumentacji mikrokontrolera)

Czasami będzie nam zależało, aby mikrokontroler spał „tylko trochę”, bo chcemy, aby cały czas mógł coś robić. Innym razem uznamy, że układ może być kompletnie uśpiony i nie musi nic robić, a do pracy będzie go pobudzał sygnał z zegara RTC. Możliwości jest wiele – sprawdźmy te najprostsze.

Praktyczny eksperyment z oszczędzaniem energii

Zaczynamy od utworzenia nowego projektu z STM32L476RG. Już standardowo: włączamy debugger przez SWD (System Core > SYS), pin PA5 wykorzystamy do sterowania diodą (wpisujemy go jako LD2), a pin PC13 przypisujemy do przycisku (z etykietą USER_BUTTON). Włączamy też USART2 – tym razem nawet nie będziemy z niego korzystać, ale włączmy ten moduł np. w trybie asynchronicznym dla testu.

W poprzedniej części kursu poznaliśmy najważniejsze informacje na temat zegarów. Wykorzystajmy tę wiedzę w praktyce. Włączamy użycie rezonatora kwarcowego niskiej częstotliwości: System Core > RCC, opcję LSE zmieniamy na Crystal/Ceramic Resonator. Dla formalności sprawdzamy też, czy CubeMX wykorzystał rezonator kwarcowy do kalibrowania zegara wysokiej częstotliwości (MSI Auto Calibration).

Standardowe ustawienia dla nowego projektu z STM32L4

Standardowe ustawienia dla nowego projektu z STM32L4

Na koniec – wisienka na torcie – przechodzimy do zakładki konfigurującej zegary i ustawiamy tam, że układ ma pracować z maksymalną możliwą częstotliwością, tj. 80 MHz. Skoro takie są jego możliwości, to trzeba z nich korzystać! Jeśli coś z tego opisu nie jest jasne, to koniecznie zajrzyj do poprzednich części kursu – znajdziecie tam dokładny opis wszystkich tych kroków.

Ustawienie maksymalnej częstotliwości taktowania układu

Ustawienie maksymalnej częstotliwości taktowania układu

Program testowy – symulacja czujnika

Załóżmy, że chcemy napisać program zbierający dane z czujnika. Nieważne, co to będzie – może to być czujnik temperatury, ciśnienia, wysokości lub odległości albo zaawansowany skaner, który laserowo bada otoczenie. Dla nas nie ma to teraz żadnego znaczenia, bo na czym innym chcemy się tu skupić.

Niezależnie od tego, jaki czujnik byśmy wybrali, to praca z takim sensorem sprowadziłaby się zapewne do korzystania z dwóch funkcji – jednej, która rozpoczyna pomiar, i drugiej, która go kończy i wyłącza czujnik. Obsługą prawdziwych czujników zajmiemy się w dalszej części kursu, a teraz posłużymy się… diodą świecącą, która będzie symulowała start i koniec pomiaru naszego wymyślonego czujnika.

Dodajemy więc do kodu następujące dwie funkcje:

W pierwszym testowym programie możemy włączyć diodę i przyjąć, że pomiar wykonujemy w sposób ciągły. Kod może wyglądać następująco:

Uruchamianie programów bez debuggera

Debugger to świetne narzędzie, które pozwala na dokładne analizowanie tego, jak działa układ, jednak nie zawsze potrzebujemy takiej opcji (szczególnie przy tego typu programie jak powyższy). Co więcej, korzystanie z debuggera wymaga od nas ręcznego uruchomienia programu – często jest to pomocne, ale nie zawsze. Jest szybsza metoda programowania! Wystarczy, że będąc w perspektywie C/C++, naciśniemy przycisk z zielonym trójkątem, który opisany będzie jako „Run main.c”.

Wgrywanie programu bez debuggera

Wgrywanie programu bez debuggera

Dlaczego wspominamy o tym w tej części kursu? Dlatego że aktywacja debuggera sprawia, że układ pobiera w niektórych trybach więcej prądu (bo muszą być aktywne moduły wewnątrz STM32, które odpowiadają za obsługę debuggera). Liczy się tu każdy mikroamper, więc rezygnujemy z debugowania, aby nie fałszować wyników.

Wróćmy do praktyki. Gdy uruchomimy ten program, zobaczymy, że dioda LD2 świeci ciągle, zatem nasz udawany pomiar trwa nieprzerwanie, a o to nam chodziło. To będzie nasz punkt wyjściowy.

Ile prądu pobiera ten program?

Prąd pobierany jest przez układ elektroniczny, ale w tym przypadku będziemy oczywiście ingerować praktycznie tylko w oprogramowanie, dlatego możemy potocznie powiedzieć, że chcemy zmierzyć, ile prądu pobiera taka wersja programu. Jak to zrobić?

Pierwsza metoda to oczywiście wykorzystanie miernika i zmierzenie tego, ile pobiera nasz układ, gdy jest podłączony do PC. Wpięcie uniwersalnego miernika do gniazda USB jest trudne, ale jeśli nie zależy nam na dużej dokładności, to możemy wykorzystać tester USB (nie musisz tego robić).

Popularny tester USB

Popularny tester USB

W naszym przypadku pobór wskazywany przez taki tester wynosił 80 mA przy 5,1 V. Oczywiście pomiar ten nie jest dokładny ze względu na metodologię jego wykonania, ale tutaj nam wystarczy. Ten wynik oznacza, że układ do pracy potrzebuje P = 5,1 * 0,08 = 0,4 W. To mało, jeśli wartość tę zestawimy z czajnikiem, który pobiera 2000 W.

Załóżmy, że nasz układ miałby być zasilany przez powerbank o pojemności 2000 mAh. Do obliczeń możemy przyjąć, że napięcie na wyjściu tego źródła zasilania to równe 5 V. Mnożąc 2000 mA, czyli 2 A, razy 5 V, dostajemy moc równą 10 W. Teoretycznie powerbank może zasilać urządzenie pobierające 10 W przez godzinę. Nasza płytka pobiera 0,4 W, więc powinna pracować przez 25 godzin, zanim powerbank zostanie rozładowany.

Te same wyniki możemy uzyskać prościej, pomijając napięcia i skupiając się na pobieranym prądzie (napięcie wyjściowe powerbanka jest takie samo jak napięcie zasilania naszej płytki). Skoro nasza płytka pobiera 80 mA, to jej czas działania obliczymy następująco: 2000 mAh / 80 mA = 25 h. Czyli licząc optymistycznie, powerbank wystarczyłby na nieco ponad dobę pracy. Dość słaby wynik jak na tak prosty układ. Pora to naprawić!

Pomiar prądu samego mikrokontrolera na Nucleo

W pierwszej wersji po prostu podłączyliśmy naszą płytkę Nucleo do powerbanka. Takie rozwiązanie może dobrze się sprawdzić podczas prototypowania lub nauki, ale powoduje ogromne marnotrawstwo energii. Płytka Nucleo składa się przecież z dwóch części – programatora oraz właściwiej części z mikrokontrolerem, który programujemy.

Przy podłączeniu przez USB w rzeczywistości zasilaliśmy i nasz układ, i programator. W urządzeniu zasilanym za pomocą baterii umieszczanie stale zasilanego programatora raczej nie jest dobrym rozwiązaniem. Na razie nie projektujemy własnych urządzeń, a programator jest nam potrzebny. Skupimy się więc na zużyciu energii przez nasz mikrokontroler, a pozostałe elementy pominiemy. Aby zmierzyć, ile prądu pobiera sam STM32L476RG, musimy odszukać na płytce zworkę JP6.

Zworka JP6 (IDD), dzięki której możemy zmierzyć pobór prądu mikrokontrolera

Zworka JP6 (IDD), dzięki której możemy zmierzyć pobór prądu mikrokontrolera

Ta zworka łączy zasilanie programatora z częścią zasilającą mikrokontroler. Jeśli w jej miejsce włączymy amperomierz, to będziemy mogli zmierzyć prąd pobierany tylko przez właściwą część układu. Gdybyśmy projektowali własne urządzenie zasilane bateryjnie, i tak nie umieszczalibyśmy w nim programatora. Teraz wystarczy, że będziemy wykonywali pomiar prądu przepływającego przez JP6.

Wyciągamy zworkę i w to miejsce podłączamy amperomierz

Wyciągamy zworkę i w to miejsce podłączamy amperomierz

Wykonujemy pomiar i uzyskujemy pierwszy wynik: 16,36 mA. Nasz mikrokontroler zasilany jest z 3,3 V, ale napięcie to doprowadzane jest do niego przez stabilizator LDO (zakładamy więc, że prąd na wejściu jest taki sam jak na wyjściu). Możemy przyjąć, że układ do pracy potrzebuje 16,36 mA * 5 V = 81,8 mW. Pobór mocy płytki Nucleo to około 400 mW, więc nasz mikrokontroler zużywa raptem niewiele ponad 20% pobieranej mocy.

Oczywiście wysoki pobór energii przez resztę płytki jest uzasadniony (programatorem i diodami). My skupimy się jednak na samym mikrokontrolerze. Nasz pierwszy wynik to 16,36 mA – potraktujemy go jako punkt wyjścia do dalszych eksperymentów.

Pierwszy pomiar prądu zużywanego przez mikrokontroler

Pierwszy pomiar prądu zużywanego przez mikrokontroler

Na początek policzmy, ile czasu pracowałby mikrokontroler, gdybyśmy zasilali go bezpośrednio z tego samego powerbanka, który wykorzystaliśmy we wcześniejszych obliczeniach. Dostarcza on 5 V, a nasz układ wymaga 3,3 V. Potrzebujemy więc układu obniżającego napięcie. Dla uproszczenia załóżmy, że podłączamy stabilizator liniowy (tego typu stabilizator jest na płytce Nucleo). W takiej sytuacji prąd pobierany z powerbanka jest w przybliżeniu równy prądowi dostarczanemu przez nasz mikrokontroler.

Jaki miałoby to wpływ na czas działania układu? Policzmy: 2000 mAh / 16,36 mA = 122 h. Czyli  samo odłączenie części z programatorem sprawiłoby, że układ mógłby być ładowany co około 5 dni, a nie codziennie. To spora poprawa, ale na pewno nie koniec możliwości.

Zmiana częstotliwości pomiarów

W dotychczasowym programie nasza dioda świeciła cały czas, co symulowało ciągły pomiar z czujnika. W rzeczywistości nie zawsze taki sposób pomiaru jest optymalny. Jeśli mierzymy np. temperaturę lub ciśnienie powietrza, to zmiany nie następują zbyt szybko i pomiar np. co minutę, a może nawet i co godzinę, w zupełności nam wystarczy.

Zmieńmy nasz kod, tak aby pomiar wykonywany był co 10 sekund. Dodatkowo przyjmijmy, że samo dokonanie pomiaru trwa 50 ms. Testowy kod będzie więc następujący:

Teraz nasza dioda zapala się na bardzo krótko co 10 s. Sprawdzamy pomiar prądu, podczas gdy układ jest w stanie spoczynku (pomiędzy pomiarami) – tym razem miernik pokazuje około 13,10 mA, a więc czas działania urządzenia wzrósł do ponad 6 dni. Oczywiście trzeba pamiętać, że co 10 s pobieramy większy prąd na dokonanie pomiaru (dla uproszczenia pomijamy to w tych rozważaniach).

Pomiar prądu przy zmniejszonej częstotliwości pomiarów

Pomiar prądu przy zmniejszonej częstotliwości pomiarów

Usypianie mikrokontrolera, gdy nic nie robi

Wywołanie funkcji HAL_Delay sprawia, że procesor nic nie robi, tylko czeka na upływ zadanego czasu. Można to sprawdzić, podglądając źródło tej funkcji. Nie trzeba dokładnie analizować jej działania, ale widać, że układ po prostu czeka, aż upłynie zadany czas.

Jak widzimy, deklaracja HAL_Delay zawiera atrybut weak, możemy więc zastąpić ją własnym kodem. Tym, co warto zmienić, jest zawartość pustej pętli while. Domyślny kod nic nie robi, ale przecież procesor wykonuje pustą pętlę z pełną prędkością, a ponieważ jest taktowany z zegara 80 MHz, to nawet takie nicnierobienie pochłania mnóstwo energii.

Zdefiniujmy zatem własną wersję funkcji opóźniającej. Dla przypomnienia: tworzymy własną wersję tej funkcji np. w pliku main.c, nie edytujemy domyślnego kodu HAL_Delay:

Zmiana polega wyłącznie na wywołaniu funkcji __WFI. Jest to w rzeczywistości makro wstawiające instrukcję asemblera procesora ARM o nazwie WFI (ang. wait for interrupt). Wykonanie tej instrukcji wstrzymuje wykonywanie programu aż do pojawienia się przerwania. Wszystkie moduły peryferyjne nadal działają, tylko mikrokontroler przestaje wykonywać program, więc pierwsze zgłoszone przerwanie obudzi procesor i wznowi wykonywanie kodu.

Biblioteka HAL konfiguruje zegar systemowy (tzw. SysTick), tak aby przerwanie pojawiało się co 1 ms (właśnie w tym przerwaniu zwiększana jest wartość zmiennej zwracanej przez HAL_GetTick). Oznacza to więc, że jeśli wywołamy __WFI, to i tak program zostanie wznowiony za co najwyżej 1 ms. Jednak przez ten czas procesor będzie uśpiony, zamiast marnować czas, wykonując pustą pętlę.

Wydaje się, że to mała zmiana? Kompilujemy i uruchamiamy program. Pobór energii spadł z 13,10 mA do – uwaga – 5,95 mA! Dzięki temu czas pracy naszego wyimaginowanego urządzenia pomiarowego wzrósł aż do dwóch tygodni.

Pomiar prądu przy zmienionej zawartości pętli while

Pomiar prądu przy zmienionej zawartości pętli while

Wyłączanie niepotrzebnych peryferii

W naszym projekcie wykorzystaliśmy domyślną konfigurację, która zakłada użycie modułu UART. Nasz program na razie z niego nie korzystał, ale sam moduł był cały czas włączony. Intuicja mogłaby nam podpowiadać, że nieużywane peryferie nie pobierają prądu. Oj, to błąd! Cofamy się do konfiguracji układu i wyłączamy moduł USART2.

Nowa wersja konfiguracji projektu z wyłączonym UART-em

Nowa wersja konfiguracji projektu z wyłączonym UART-em

Konfigurujemy od nowa projekt, kompilujemy program i mierzymy pomiar. Tym razem układ zamiast 5,95 mA pobiera już 5,49 mA. Zyskaliśmy więc kolejny dzień pracy na zasilaniu z powerbanka.

Pomiar prądu przy wyłączonym module UART

Pomiar prądu przy wyłączonym module UART

Wniosek jest prosty: programując rozwiązania energooszczędne, musimy pamiętać o wyłączaniu nieużywanych modułów (i o wielu innych rzeczach). Co więcej, nawet te, których używamy, nie zawsze muszą być włączone przez cały czas. Możemy zaoszczędzić sporo energii, włączając je tylko wtedy, gdy są faktycznie potrzebne. 

Obniżanie częstotliwości taktowania

Przetestujmy kolejną ważną zmianę, czyli częstotliwość taktowania. Wiele osób, widząc na opakowaniu informację o częstotliwości 80 MHz, zakłada, że to jedyna opcja warta uwagi. Sprawdźmy, co będzie, jeśli zamiast 80 MHz będziemy taktowali nasz mikrokontroler połową tej częstotliwości, czyli 40 MHz.

Zmiana konfiguracji taktowania na 40 MHz

Zmiana konfiguracji taktowania na 40 MHz

Tym razem pomiar wskazuje 2,86 mA. Jak widzimy, pobór prądu jest w przybliżeniu dwukrotnie niższy. Wynika to z prawie liniowej zależności poboru prądu od taktowania.

Pomiar prądu przy zmniejszonej częstotliwości taktowania układu

Pomiar prądu przy zmniejszonej częstotliwości taktowania układu

Idźmy dalej – zmieńmy teraz konfigurację na 4 MHz (nadal przy użyciu PLL). Jeśli wpiszemy wartość 4 MHz, to CubeMX prawdopodobnie ustawi zegary tak jak poniżej, tj. sygnał przejdzie przez blok PLL.

Zmiana konfiguracji taktowania na 4 MHz z PLL

Zmiana konfiguracji taktowania na 4 MHz z PLL

W takiej sytuacji pobór prądu to około 1,15 mA. Tylko po co mnożyć częstotliwość 4 MHz w bloku PPL, aby później dzielić ją w bloku AHB Prescaler przez 8, żeby znów otrzymać 4 MHz? Lepiej zmienić ręcznie konfigurację w taki sposób, aby źródłem sygnału wybranym na System Clock Mux był od razu MSI. Też uzyskamy wtedy 4 MHz, ale pominiemy blok PLL. 

Druga wersja konfiguracji dla 4 MHz

Druga wersja konfiguracji dla 4 MHz

Najnowszy wynik to 0,4 mA. Dla przypomnienia: zaczynaliśmy od 16,36 mA. Po drodze dokonaliśmy znacznego obniżenia częstotliwości taktowania naszego układu, ale naprawdę jest wiele zastosowań, gdy mikrokontroler wcale nie musi pędzić 80 MHz. Jesteśmy przyzwyczajeni do tego, że w świecie PC i nowoczesnych telefonów liczą się gigaherce, jednak w świecie systemów embedded bardzo często wystarczą nam nawet pojedyncze megaherce.

W teorii sprawiliśmy właśnie, że układ będzie działał przez 208 dni. Dla przypomnienia: zaczynaliśmy od tego, że będziemy ładować go codziennie... Zasilanie takiego układu z powerbanka może być już problematyczne, bo nasze źródło zasilania pewnie się nawet automatycznie wyłączy, bo nie wykryje poboru tak małego prądu.

Zmiany napięcia zasilania?

Dotychczas rozważaliśmy zasilanie naszego układu z powerbanka, który miał napięcie wyjściowe 5 V. Moglibyśmy zastosować jeszcze inne rozwiązanie (np. koszyk na 3 × AAA). Takie połączenie dawałoby nam napięcie znamionowe 4,5 V w przypadku baterii albo 3,6 V dla akumulatorków. W związku z tym, że jest to napięcie zbyt wysokie dla mikrokontrolera, konieczne byłoby użycie stabilizatora linowego lub przetwornicy DC/DC.

Podczas całego kursu zakładamy, że mikrokontroler jest zasilany napięciem 3,3 V. Okazuje się jednak, że nie jest to jedyne napięcie, z jakim nasz układ może pracować. Podobnie jak z częstotliwością taktowania (80 MHz to maksymalna wartość, ale nie jedyna dostępna), napięcie 3,3 V również nie jest jedyną możliwą wartością. W praktyce STM32L476 może pracować z napięciami od 1,71 V do 3,6 V

Fragment noty katalogowej używanego mikrokontrolera

Fragment noty katalogowej używanego mikrokontrolera

Dzięki temu możemy nasz układ zasilać bezpośrednio z dwóch połączonych szeregowo baterii, które dają napięcie nominalne 3 V, albo akumulatorów (2,4 V). Moglibyśmy nawet zasilać nasz układ z jednej baterii, np. CR2032. Jej pojemność to ~230 mAh, zatem bardzo mało w porównaniu z powerbankiem, ale nasz obecny układ, który pobiera 0,4 mA, mógłby na niej pracować przez około 24 dni.

Układ jest już tak energooszczędny, że mógłby przez wiele dni pracować na baterii CR2032

Układ jest już tak energooszczędny, że mógłby przez wiele dni pracować na baterii CR2032

Dywagacja na temat zmniejszania napięcia zasilania to był tylko przerywnik. Tym razem nie będziemy jednak robić nic z napięciem i jeszcze mocniej „przyciśniemy” mikrokontroler, aby układ był bardziej energooszczędny.

Pełne usypianie mikrokontrolera

Dotychczas pokazane metody ograniczania poboru prądu zatrzymywały wykonywanie programu na bardzo krótko (1 ms), ale nie wyłączały modułów peryferyjnych i źródeł taktowania. Teraz spróbujemy wyłączyć (prawie) wszystko poza zegarem RTC. Jego działanie omówiliśmy w poprzedniej części, a teraz pokażemy, jak można go wykorzystać do budzenia mikrokontrolera.

Nasz program będzie działał następująco: po uruchomieniu wykona pomiar – zakładamy, że jego wynik powinniśmy jakoś przetworzyć, chociaż w naszym uproszczonym przykładzie po prostu zapalimy na 50 ms diodę. Następnie będziemy ustawiali alarm RTC i zupełnie uśpimy mikrokontroler.

Zacznijmy od przetestowania samego usypiania. Mikrokontroler STM32L476RG oferuje wiele trybów uśpienia – więcej informacji o nich znajdziemy w dokumentacji układu. W największym skrócie różnią się one liczbą nadal działających modułów. Jako przykładu użyjemy tzw. trybu Standby, który wyłącza prawie wszystko poza RTC oraz BOR, czyli układem wykrywającym zbyt niskie napięcie zasilania.

Kolejne tryby pracy (fragment noty katalogowej używanego mikrokontrolera)

Kolejne tryby pracy (fragment noty katalogowej używanego mikrokontrolera)

Dzięki bibliotece HAL uśpienie mikrokontrolera jest bardzo proste, sprowadza się do wywołania funkcji HAL_PWR_EnterSTANDBYMode, która nawet nie przyjmuje żadnego argumentu (i nic nie zwraca).

W ramach testu można wstawić do programu wywołanie tej funkcji jeszcze przed pętlę while. Nie jest to zbyt użyteczne rozwiązanie (bo taki układ zostanie szybko uśpiony i „nigdy” sam się nie obudzi), ale można wtedy łatwo zmierzyć pobór prądu w pełnym uśpieniu. W naszym przypadku wynosił on około 1 µA (1 mikroapmer), czyli 0,000 001 A.

Układ w takiej wersji praktycznie nic nie robi, ale nadal działa w nim nasz zegar czasu rzeczywistego, który może obudzić układ na czas wykonania pomiaru. Wracamy do konfiguracji sprzętu w CubeMX i przechodzimy do Timers > RTC, a następnie zaznaczamy Activate Clock Source. Jednak to nie koniec, bo teraz z listy opcji opisanej jako WakeUp wybieramy opcję Internal WakeUp.

Następnie w poniższych opcjach ustawiamy wartość Wake Up Counter na 20 479. Skąd ta wartość? Moduł RTC jest taktowany zegarem o częstotliwości 32 768 Hz. Do tego mamy tutaj wybrany dzielnik przez 16 (widać to w polu Wake Up Clock, bo mamy tam „RTCCLK / 16”). Oznacza to, że zegar będzie pracował z częstotliwością 32 768 / 16, czyli 2048 Hz, a to oznacza, że wpisane tu ustawienia dadzą nam 10 s opóźnienia, bo (20 479 + 1) / 2048 = 10. Wyjaśnienia wymaga jedynie skąd wzięło się „+ 1”, licznik liczy od zera, więc zliczenie 100 impulsów wymagałoby wpisania wartości 99 itd.

Nowe ustawienia modułu RTC w STM32L4

Nowe ustawienia modułu RTC w STM32L4

W przypadku prawdziwego czujnika moglibyśmy zmienić dzielnik i wybrać np. 1 Hz oraz ustawić dużo większe opóźnienie, np. 30 min. Musimy jeszcze uruchomić przerwanie związane z budzeniem, czyli coś, co faktycznie wybudzi nasz mikrokontroler ze stanu uśpienia.

Temat przerwań zostanie omówiony w następnej części kursu, więc na ten moment ograniczmy się tylko do tego, że w miejscu, gdzie przed chwilą wpisywaliśmy informacje o RTC, przełączamy się na zakładkę NVIC Settings i zaznaczamy opcję o nazwie: RTC wake-up interrupt through EXTI line 20.

Aktywacja przerwania od modułu RTC w STM32L4

Aktywacja przerwania od modułu RTC w STM32L4

Teraz możemy zapisać projekt, wygenerować szablon kodu i zacząć pisać nasz program. Okazuje się to o wiele łatwiejsze, niż mogłoby się wydawać. Usypianie mikrokontrolera do tzw. trybu Standby wyłącza podtrzymywanie zawartości pamięci, więc po obudzeniu program będzie wykonywany „od początku”, zupełnie jak przy resecie mikrokontrolera.

Tym razem pętla główna programu nie będzie nawet używana, cały kod to pomiar i uśpienie układu.

Po uruchomieniu zobaczymy, że co około 10 s dioda symbolizująca wykonywanie pomiaru jest włączana na 50 ms. Jeśli podłączymy multimetr do tej wersji układu, zobaczymy, że przez pozostały czas pobór prądu jest minimalny i wynosi około 1 µA. Czyli tylko za pomocą kilku prostych trików programistycznych udało nam się obniżyć pobór prądu w momentach, gdy układ nie musi robić nic istotnego, z 16,36 mA do 0,0011 mA.

Finalny efekt – pobór 1 µA pomiędzy pomiarami z symulowanego czujnika

Finalny efekt – pobór 1 µA pomiędzy pomiarami z symulowanego czujnika

Uwzględnianie czasu pomiaru

Nasze dotychczasowe obliczenia koncentrowały się na poborze prądu między wykonywaniem pomiaru (czyli włączaniem diody). Jednak w miarę zmniejszania poboru prądu znaczenia nabiera to, ile energii zużywa sam pomiar. Spróbujmy policzyć, ile wynosi średni prąd pobierany zarówno podczas uśpienia układu, jak i pomiarów. Nasze dane są następujące:

  • Czas uśpienia: 10 s
  • Prąd w stanie uśpienia: 1 µA
  • Czas pomiaru: 50 ms
  • Prąd podczas pomiaru: 5 mA  

Możemy teraz obliczyć prąd średni pobierany przez układ. Po ujednoliceniu jednostek otrzymujemy:

I = (1 µA * 10000 ms + 5000 µA * 50 ms) / 10050 ms = 25,8 µA

Przy takim prądzie i pojemności baterii 230 mAh układ powinien pracować prawie rok. To bardzo dobry wynik, ale widać, jak możemy go jeszcze poprawić – do testów wykonywaliśmy „pomiar” co 10 s, ale może wystarczyłby co minutę?

I = (1 µA * 60 000 ms + 5000 µA * 50 ms) / 60 050 ms = 5,1 µA

Teraz nasz układ mógłby działać nawet 5 lat na jednej baterii CR2032 (jeśli nasza bateria „przeżyje” tyle lat, ale to już inny temat). Biorąc pod uwagę, że na początku układ ten (z ciągłym pomiarem) pobierał cały czas 16,36 mA, a teraz średnio pobiera 5,1 µA (0,0051 mA), to udało nam się poprawić początkowy wynik aż 3207 razy!

PCC – rozbudowany kalkulator zużycia energii

Dotychczas samodzielnie obliczaliśmy średnie zużycie prądu i szacowaliśmy czas pracy urządzenia przy zasilaniu z baterii. To bardzo cenne umiejętności, okazuje się jednak, że w STM32CubeIDE jest dostępne narzędzie, które może nam w tym pomóc.

PPC (Power Consumption Calculator) to zestaw narzędzi, który znajdziemy w STM32CubeMX, a dokładniej w zakładce Tools. Liczba dostępnych opcji może być w pierwszej chwili nieco przytłaczająca, ale to dość proste w użyciu narzędzie. W ramach treningu odtworzymy w nim nasz poprzedni projekt.

Zakładka Tools, czyli kalkulator PCC

Zakładka Tools, czyli kalkulator PCC

Ekran naszego kalkulatora podzielony jest na dwie części. Po lewej widzimy generator ustawień, a po prawej kroki symulacji oraz wyniki i wykres. Generator pozwala nam na bardzo szybkie edytowanie ustawień i eksperymentowanie z projektem, dzięki czemu od razu dosłownie widzimy, jaki pobór prądu będzie „konsumowała” dana konfiguracja układu.

Konfiguracja projektu w Power Consumption Calculator

Zacznijmy od omówienia kontrolek, które dostępne są po lewej stronie. Domyślnie ten rozbudowany kalkulator będzie przeliczał wszystkie wartości na żywo (bo aktywna jest opcja Auto Refresh On), można jednak to wyłączyć, klikając przycisk Disable Auto Refresh. Jeśli wyłączymy tę opcję, to zmiany w obliczeniach będą wprowadzane dopiero po naciśnięciu przycisku Generate RUN + STOP2. Zawsze możemy też wrócić do domyślnych ustawień – wystarczy kliknąć środkowy przycisk.

Przyciski sterujące pracą PCC

Przyciski sterujące pracą PCC

Poniżej, w sekcji Sequence Configuration, znajdują się opcje, dzięki którym możemy ustawić prędkość i tryb pracy dla obu stanów układu (aktywnego i uśpionego). W naszym przypadku w pierwszym kroku ustawiamy opcję RUN, czyli normalny tryb pracy. Poniżej, na suwaku, ustawiamy częstotliwość, z jaką taktujemy wtedy układ – w tym przypadku 4 MHz. Dalej musimy wybrać tryb pracy dla drugiego stanu, czyli stanu uśpienia – tutaj wybieramy tryb STANDBY.

Ustawienia trybów pracy

Ustawienia trybów pracy

Następnie musimy ustawić, ile będzie trwał pełny cykl pracy naszego układu. Projekt skonfigurowaliśmy tak, że program wykonuje się od nowa co 10 s, więc to właśnie jest nasz cykl. Wartość tę musimy podać w polu Sequence Time. Teraz ustawiamy to, ile czasu nasz mikrokontroler spędza w stanie pełnej wydajności. U nas było to 50 ms, ale generator pozwala na ustawianie wartości co 0,1 s, więc na razie ustawmy czas pracy na 0,1 s (za chwilę to zmienimy).

Ustawienia czasu pracy

Ustawienia czasu pracy

Jeśli wszystko ustawiliśmy poprawnie, nasz ekran powinien wyglądać mniej więcej tak jak poniżej, tylko wykres może mieć inną skalę. Aby wyglądał tak jak poniżej, trzeba zaznaczyć myszką obszar, który nas interesuje, a całość automatycznie się powiększy.

Zaktualizowane obliczenia w PCC

Zaktualizowane obliczenia w PCC

Najważniejsza jest jednak informacja pod wykresem. Program oszacował średni pobór prądu oraz czas pracy na baterii. Wyszło 41 lat i 2 miesiące. Dużo i kompletnie inaczej niż w naszych obliczeniach, a to dlatego, że nigdzie nie ustawiliśmy źródła zasilania. Klikamy więc opcję, która jest pod polem, gdzie wpisywaliśmy długość cyklu pracy. Domyślnie będzie ona nazwana zapewne Li-SOCL2, bo jest to typ akumulatora, który kalkulator wybrał automatycznie do obliczeń. Klikamy tę opcję, a następnie przycisk Change i wybieramy baterię CR2032.

Wybór źródła zasilania w PCC

Wybór źródła zasilania w PCC

System automatycznie uaktualni obliczenia i tym razem zobaczymy, że nasz układ teoretycznie będzie działał przez ponad 4 lata. Ten wynik nadal odbiega od naszych obliczeń. Nasz program działa jednak z pełną prędkością przez 50 ms, a nie 100 ms, które ustawiliśmy w generatorze. Na szczęście możemy to łatwo zmienić. Wyłączamy generator, klikając Disable Auto Refresh, i przyglądamy się tabeli, która jest widoczna po prawej stronie ekranu.

Zatrzymany PCC po zmienionym źródle zasilania

Zatrzymany PCC po zmienionym źródle zasilania

Generator utworzył dla nas dwa kroki symulacji: jeden dla aktywnej pracy procesora i jeden dla trybu uśpienia. Jak widzimy, pierwszy krok trwa 0,1 s i zakłada pobór prądu rzędu 550 µA. Aby wprowadzić wartości bliższe naszemu projektowi, klikamy dwa razy w ten wiersz, aby pojawiło się okno ustawień. Interesuje nas pole Step Duration. Generator wstawił tam 0,1, ale teraz możemy zmienić tę wartość na 0,05, czyli 50 ms.

Symulator oblicza tylko prąd pobierany przez mikrokontroler, nie jest w stanie sam „zgadnąć”, ile prądu pobierają inne elementy. W naszym przypadku tym dodatkowym elementem jest dioda LD2 symulująca nasz czujnik. W polu Additional Consumption wpisujemy więc przybliżony pobór prądu przez diodę, czyli 4,5 mA.

Edycja ustawień czasu cyklu i poboru prądu

Edycja ustawień czasu cyklu i poboru prądu

W dolnej części tego okna widać od razu ostrzeżenie o przekroczeniu dopuszczalnego prądu. Okazuje się, że wykonując nasze ręczne obliczenia, zupełnie „zapomnieliśmy” wziąć pod uwagę, że bateria typu CR2032 może dostarczać maksymalnie 3 mA. My błędnie założyliśmy 5 mA – jak widać, używanie tego kalkulatora ma swoje zalety, bo pozwala on na uniknięcie tego typu pomyłek. Pozostańmy jednak już przy tej baterii, aby zweryfikować nasze ręczne obliczenia.

Zatwierdzamy zmiany i spoglądamy na wykres oraz informacje, które są pod nim. Narzędzie wyliczyło, że bateria taka wystarczyłaby na 11 miesięcy, 27 dni i 9 godzin pracy urządzenia. Podobny wynik uzyskaliśmy podczas ręcznych obliczeń, czyli wszystko się zgadza.

Ostateczna wersja obliczeń wykonanych przez PCC

Ostateczna wersja obliczeń wykonanych przez PCC

Ciekawostka: dokładniejsze pomiary

Praca z układami energooszczędnymi nie jest łatwym zadaniem nie tylko ze względu na trudności w tworzeniu oprogramowania oraz elektroniki. Również testowanie układów i wykonywanie pomiarów sprawia więcej problemów. W typowych układach wystarczy zmierzyć pobór prądu i mamy wszystkie dane gotowe. Niestety, jak już widzieliśmy, w przypadku takich systemów pobór prądu może się bardzo często zmieniać.

Najlepiej byłoby mieć możliwość wykonywania wielu pomiarów, np. co milisekundę. Niestety typowy multimetr może być w takiej sytuacji niewystarczający. Istnieją oczywiście profesjonalne narzędzia pozwalające na wykonanie takich pomiarów, ale nie są to tanie rozwiązania. Osoby, które zainteresują się tematem energooszczędności, mogą jednak rozważyć zakup płytki X-Nucleo-LPM01A. Jest to rozszerzenie do płytek Nucleo, które zostało zaprojektowane przez STMicroelectronics.

X-Nucleo-LPM01A, czyli specjalny shield m.in. dla Nucleo

X-Nucleo-LPM01A, czyli specjalny shield m.in. dla Nucleo

Płytka jest wyposażona w złącze zgodne z Arduino, które znajdziemy również na Nucleo-L476RG. Po odpowiednim ustawieniu zworek możemy podłączyć naszą płytkę do X-Nucleo i wykonać nią pomiary poboru prądu. Co ważne, płytka X-Nucleo-LPM01A współpracuje z dedykowanym oprogramowaniem, które pozwala na wizualizowanie pomiarów.

Wyniki pomiarów dla projektu z tego artykułu

Wyniki pomiarów dla projektu z tego artykułu

Teraz możemy „zobaczyć”, jak działa nasz program. Widzimy momenty dokonywania pomiaru oraz przerwy, gdy układ jest usypiany. Możemy też odczytać, ile faktycznie pobieramy prądu podczas wykonywania pomiaru oraz ile czasu trwa ten etap.

Dokładna analiza działania programu dzięki X-Nucleo-LPM01A

Dokładna analiza działania programu dzięki X-Nucleo-LPM01A

Jak widzimy, czas 50 ms jest zgodny z naszymi założeniami, a faktyczny pobór prądu jest jednak nieco niższy niż przyjęte przez nas 5 mA. Pozostawiamy to jedynie jako ciekawostkę – płytka ta nie będzie potrzebna do wykonywania ćwiczeń z tego kursu.

Zadanie domowe

  1. Znajdź w dokumentacji mikrokontrolera informacje o wszystkich trybach oszczędzania energii i sprawdź, czym się różnią (pobór prądu, dostępne peryferie itd.).
  2. Sprawdź, jak zwiększy się prąd, gdy aktywujesz w układzie sześć wejść z pull-upem.
  3. Zapoznaj się dokładnie z kalkulatorem PCC. Sprawdź, jakie inne opcje można „wyklikać”, gdy wejdzie się w ręczną edycję poszczególnych kroków.

Podsumowanie – co powinieneś zapamiętać?

Tryby oszczędzania energii to obszerny temat, który w tym poradniku jedynie delikatnie nakreśliliśmy. Mamy jednak nadzieję, że sposób, w jaki opisaliśmy to zagadnienie, zachęci szersze grono czytelników do tego, aby zacząć eksperymentować w tym kierunku. Po tej części kursu powinniście wiedzieć, jak wykorzystać RTC do budzenia układu i jak korzystać z trybu Standby oraz z kalkulatora PCC.

Czy wpis był pomocny? Oceń go:

Średnia ocena 5 / 5. Głosów łącznie: 15

Nikt jeszcze nie głosował, bądź pierwszy!

Artykuł nie był pomocny? Jak możemy go poprawić? Wpisz swoje sugestie poniżej. Jeśli masz pytanie to zadaj je w komentarzu - ten formularz jest anonimowy, nie będziemy mogli Ci odpowiedzieć!

W kolejnej części kursu zajmiemy się przerwaniami sprzętowymi, czyli mechanizmem, który m.in. pozwoli znacznie usprawnić dotychczasową komunikację przez UART. Będzie to też ostatnia „spokojna” część tego kursu, bo później do naszego STM32L4 będziemy podłączać liczne moduły i czujniki!

Nawigacja kursu

Główny autor kursu: Piotr Bugalski
Współautor: Damian Szymański, ilustracje: Piotr Adamczyk
Oficjalnym partnerem tego kursu jest firma STMicroelectronics
Zakaz kopiowania treści kursów oraz grafik bez zgody FORBOT.pl

CR2032, kurs, kursSTM32L4, stm32l4, zasilanie

Trwa ładowanie komentarzy...