Oszczędzanie energii w mikrokontrolerach STM32
Sleep-now
Działanie tego trybu polega na natychmiastowym wprowadzeniu rdzenia w tryb obniżonego poboru mocy. Można to zrobić zarówno za pomocą instrukcji WFI jak i WFE. Różnica polega na tym, że w pierwszym przypadku system będzie oczekiwał nadejścia przerwania, a w drugim zdarzenia. Czas potrzebny na wybudzenie rdzenia z trybu uśpienia w oczekiwaniu na zdarzenie (rozkaz WFE) jest prawie dwukrotnie krótszy i równy około 2 s. Wynika to z faktu, że nie jest potrzebny czas na obsługę przerwania. Nasuwa się wniosek, że wykorzystanie instrukcji WFE bardzo dobrze nadaje się do współpracy z zegarem czasu rzeczywistego RTC, którego zadaniem może być generowanie zdarzeń (alarmów).
List. 2. Program wybudzający mikrokontroler z trybu sleep-now co 30 sekund
int main(void) { EXTI_InitTypeDef EXTI_InitStructure; DBGMCU_Config(DBGMCU_SLEEP, ENABLE); RCC_Conf(); NVIC_Conf(); GPIO_Conf(); // Linia 17 jako zdarzenie (RTC) EXTI_StructInit(&EXTI_InitStructure); EXTI_InitStructure.EXTI_Line = EXTI_Line17; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); PWR_BackupAccessCmd(ENABLE); // Zezwolenie na dostep do Backup domain BKP_DeInit(); RCC_LSEConfig(RCC_LSE_ON); // Wlacz LSE while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); // Czekaj, az wystartuje RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // LSE zrodlem sygn. zeg. dla RTC RCC_RTCCLKCmd(ENABLE); // Wlacz taktowanie RTC RTC_WaitForSynchro(); // Czekaj na synchronizacje RTC z APB RTC_WaitForLastTask(); RTC_SetPrescaler(32768); // Zliczane beda impulsy co 1s RTC_WaitForLastTask(); while (1) { NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, ENABLE); // Najwyzszy priorytet NVIC_SETPRIMASK(); RTC_SetAlarm(RTC_GetCounter()+ 30); // Wybudzenie co 30s RTC_WaitForLastTask(); GPIO_ResetBits(GPIOC, GPIO_Pin_6); __WFE(); // Wait for event GPIO_SetBits(GPIOC, GPIO_Pin_6); delay_ms(2000); } }
W programie z list. 2 RTC skonfigurowano w taki sposób, aby wybudzać mikrokontroler co pół minuty. W trybie normalnej pracy świeci dioda LD1. Dodatkowo w tym przykładzie status aktualnego zadania osiąga najwyższy priorytet, a co za tym idzie NVIC nie przerwie jego wykonywania na rzecz obsługi przerwania. Można to zaobserwować, jeśli włączy się np. przerwanie od przycisku (PB9), a w funkcji obsługi przerwania umieści się fragment kodu, który będzie powodował widoczne efekty na płytce ewaluacyjnej.
Przykład takiej funkcji:
void EXTI0_IRQHandler(void) { EXTI_ClearITPendingBit(EXTI_Line0); GPIO_SetBits(GPIOC, GPIO_Pin_9); }
W celu sprawdzenia, czy omawiana aplikacja działa poprawnie można uruchomić program z list. 2, z dodaną na początku głównej, nieskończonej pętli następującą linijką:
NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, DISABLE);
Przerwania nie są blokowane, więc naciśnięcie przycisku w stanie aktywnym powinno spowodować zaświecenie diody LD4.
Deep-sleep
Ostatnim trybem obniżonego poboru mocy rdzenia jest „głębokie uśpienie”. Nie jest to kolejny oddzielny tryb. Uzyskuje się go łącząc tryb natychmiastowego uśpienia (sleep-now), lub z trybem uśpienia po obsłużeniu wszystkich przerwań (sleep-on-exit). Tryb aktywowany jest przez ustawienie bitu SLEEPDEEP w rejestrze kontrolnym zarządzania zasilaniem kontrolera przerwań NVIC.
Programista używający biblioteki API nie musi zagłębiać się w te szczegóły, wystarczy użyć odpowiedniej funkcji: jeżeli aplikacja wymaga wprowadzenia rdzenia w stan głębokiego uśpienia, to należy umieścić w kodzie dodatkowo linijkę:
NVIC_SystemLPConfig(NVIC_LP_SLEEPDEEP, ENABLE);
Konsekwencją aktywizacji Deep-sleep jest wyłączenie zegara taktującego rdzeń, łącznie z układem PLL. Pozwala to dalsze, w stosunku do poprzednich trybów, obniżenie poboru mocy. Negatywnym efektem zastosowania głębokiego uśpienia jest dłuższy czas potrzebny na całkowite wybudzenie i rozpoczęcie normalnej pracy. Jest to związane z tym, że pętla synchronizacji fazowej wymaga czasu, aby wygenerować stabilny sygnał zegarowy.
Tryb zatrzymania (stop mode)
Wykorzystuje on omówiony wcześniej tryb głębokiego uśpienia. Wyłączone zostają wszystkie sygnały zegarowe (układ PLL i oba szybkie oscylatory: HSI i HSE). Ponadto wewnętrzny regulator napięcia 1,8 V można wprowadzić kosztem późniejszego dłuższego startu systemu w stan obniżonego poboru mocy.
W stop mode nadal pracuje kilka bloków. Należy do nich między innymi układ niezależnego watchdoga. Należy brać to pod uwagę w aplikacjach wykorzystujących IWDG i pracujących w trybach obniżonego poboru mocy.
Jeżeli wcześniej nie zostaną wyłączone zegar czasu rzeczywistego i wolne oscylatory (LSE i LSI), to we wszystkich trybach obniżonego poboru mocy są w stanie aktywnej pracy.
List. 3. Sposób wprowadzenie mikrokontrolera w tryb stop
int main(void) { EXTI_InitTypeDef EXTI_InitStructure; DBGMCU_Config(DBGMCU_STOP, ENABLE); RCC_Conf(); NVIC_Conf(); GPIO_Conf(); // Linia 17 jako zdarzenie (RTC) EXTI_StructInit(&EXTI_InitStructure); EXTI_InitStructure.EXTI_Line = EXTI_Line17; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); PWR_BackupAccessCmd(ENABLE); // Zezwolenie na dostep do Backup domain BKP_DeInit(); RCC_LSEConfig(RCC_LSE_ON); // Wlacz LSE while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); // Czekaj, az wystartuje RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // LSE zrodlem sygn. zeg. dla RTC RCC_RTCCLKCmd(ENABLE); // Wlacz taktowanie RTC RTC_WaitForSynchro(); // Czekaj na synchronizacje RTC z APB RTC_WaitForLastTask(); RTC_SetPrescaler(32768); // Zliczane beda impulsy co 1s RTC_WaitForLastTask(); while (1) { RTC_SetAlarm(RTC_GetCounter()+ 10); // Wybudzenie co 10s RTC_WaitForLastTask(); GPIO_ResetBits(GPIOC, GPIO_Pin_6); PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE); // *** Ponowne wlaczenie PLL i HSE *** RCC_HSEConfig(RCC_HSE_ON); HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08); } // *** HSE i PLL wlaczone *** GPIO_SetBits(GPIOC, GPIO_Pin_6); delay_ms(2000); } }
Sposób wprowadzenie mikrokontrolera w tryb stop przedstawiono na list. 3. Ten prosty program ma zadanie wprowadzać mikrokontroler w tryb zatrzymania co ok. 2 sekundy, przy czym samo zatrzymanie trwa 10 sekund. Sygnalizacja normalnej pracy odbywa się przez zaświecenie diody LD1. Wybudzanie następuje pod wpływem alarmów generowanych za pomocą zegara czasu rzeczywistego. Po wyczyszczeniu flagi od zdarzenia od RTC następuje ustawienie czasu, po jakim ma być uformowany kolejny sygnał alarmu. W przykładzie mikrokontroler ma się wybudzać co 10 sekund, więc za każdym razem do aktualnej wartości licznika RTC należy dodać wartość 10. Osiągnięcie przez licznik otrzymanej w ten sposób liczby będzie skutkowało alarmem. Zadaniem kolejnej wywołanej funkcji PWR_EnterSTOPMode() jest właściwe wprowadzenie układu w stan zatrzymania. W programie, ze względu na brak specjalnych wymagań co do minimalnego czasu startu systemu, regulator napięcia wprowadzany jest w tryb low power.
W tym miejscu praca mikrokontrolera zostaje wstrzyma i od tego miejsca rozpocznie się po wybudzeniu. W związku z tym, że w trybie zatrzymania wyłączane są zarówno pętla synchronizacji fazowej jak i szybkie oscylatory, to po powrocie do normalnej pracy należy je ponownie włączyć. Jeżeli aplikacja korzysta z wewnętrznego oscylatora HSI i nie wykorzystuje układu PLL, to ponowna konfiguracja tych urządzeń nie jest potrzebna: system automatycznie startuje biorąc HSI za źródło swojego sygnału zegarowego.