Oszczędzanie energii w mikrokontrolerach STM32
Zmniejszenie poboru energii przez mikrokontroler można osiągnąć poprzez zmniejszenie częstotliwości jego taktowania (co wiąże się ze spowolnieniem działania) lub przez selektywne wyłączanie układy peryferyjnych wbudowanych w jego strukturę. Ten drugi mechanizm doskonale sprawdza się w mikrokontrolerach STM32, które są konstrukcyjnie przystosowane do selektywnego włączania i wyłączania większości wbudowanych bloków peryferyjnych.
Zaczniemy od omówienia dostępnych trybów oszczędnościowych, których działanie przetestowano na zestawie STM3210B–EVAL/A.
Sleep mode
Ten tryb oszczędzania energii jest integralną częścią rdzenia Cortex-M3, dlatego informacji na jego temat należy szukać nie w dokumentacji przygotowanej przez producenta mikrokontrolerów STM32, ale w opisie rdzenia przygotowanej przez firmę ARM.
W trybie sleep mode wyłączany jest rdzeń, natomiast wszystkie bloki peryferyjne i źródła sygnałów zegarowych są aktywne. Czas wyjścia z trybu uśpienia i powrót do normalnej pracy mikrokontrolera jest najkrótszy ze wszystkich dostępnych pośród trybów obniżonego poboru mocy. Natężenie prądu pobieranego ze źródła zasilania zależy przede wszystkim od częstotliwości pracy generatorów przebiegów taktujących i aktywnych bloków peryferyjnych. W skrajnym przypadku, gdy mikrokontroler pracuje z zegarem 72 MHz (włączone HSE i PLL) i są włączone wszystkie peryferia, mikrokontroler może pobierać blisko 15 mA. Drugim skrajnym przypadkiem jest praca rdzenia z taktowaniem 125 kHz przy wykorzystaniu wewnętrznego oscylatora HSI i po wyłączeniu wszystkich peryferiów. W takich warunkach mikrokontroler pobiera prąd o natężeniu nie przekraczającym 0,5 mA.
Zależnie od zastosowanych mechanizmów „usypiania” i „wybudzania” rozróżniane jest kilka rodzajów trybów pracy. Za sterowanie tymi trybami odpowiadają dwie instrukcje: WFI (Wait For Interrupt) oraz WFE (Wait For Event). Są to rozkazy asemblerowe, ich użycie w programie napisanym w C wymaga zastosowania podwójnego podkreślenia w roli przedrostka. Przykładowo: żeby wywołać instrukcję WFI należy zastosować zapis:
__WFI();
Sleep-on-exit
Wykorzystanie instrukcji WFI umożliwia między innymi wprowadzenie rdzenia w stan sleep-on-exit. Rdzeń w takim przypadku jest wprowadzany w tryb uśpienia dopiero po zakończeniu obsługi wszystkich przerwań. Wyjście z trybu jest wówczas, gdy w systemie zostanie wykryte żądanie obsługi przerwania. Fragment programu, który działa w opisany sposób przedstawiono na list. 1. Nadzór nad trybami uśpienia sprawowany jest przez NVIC, dlatego wykorzystano funkcje sterujące jego pracą.
List. 1. Fragment programu ilustrującego tryb oszczędzania energii sleep-on-exit
int main(void) { EXTI_InitTypeDef EXTI_InitStructure; RCC_Conf(); NVIC_Conf(); GPIO_Conf(); // Poinformowanie uC o zrodle przerwania GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource0); // Bedzie generowane przerwanie na zboczu opadajacym na EXTI_Line0 EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // Po obsluzeniu przerwania ponownie bedzie wprowadzony tryb uspienia NVIC_SystemLPConfig(NVIC_LP_SLEEPONEXIT, ENABLE); while (1); }
Przerwanie od wyprowadzenia PD0 mikrokontrolera, do którego podłączono przycisk, skonfigurowano do reakcji na zbocze opadające. Po jego pojawieniu rdzeń wyjdzie z trybu uśpienia i nastąpi wywołanie funkcji obsługi przerwania.
Funkcja obsługi przerwania wygląda następująco:
void EXTI9_5_IRQHandler(void) { EXTI_ClearITPendingBit(EXTI_Line9); GPIO_SetBits(GPIOC, GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9); delay_ms(2000); GPIO_ResetBits(GPIOC, GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9); }
Rdzeń wybudza się na czas około 2 sekund zaświecając cztery diody LED, gasi je i ponownie „usypia”.
Uruchomienie opisanego trybu w trakcie debugowania spowoduje przerwanie pracy debugera. Rdzeń nie pracuje, więc nie ma potrzeby poszukiwania błędów. Istnieje jednak możliwość takiej konfiguracji mikrokontrolera, że nawet w trybach obniżonego poboru mocy, połączenie debugera i mikrokontrolera nie zostanie zerwane. Do tego celu służy funkcja DBGMCU_Config().Przykładowo, jeśli wykorzystywany jest tryb uśpienia, to należy umieścić w programie linijkę:
DBGMCU_Config(DBGMCU_SLEEP, ENABLE);