====== 12. Úsporné režimy, PLL, Watchdog ====== ===== Cíle cvičení ===== * Seznámit se úspornými režimy na ARM procesorech * Snížit spotřebu na minimum * Nastavit maximální frekvenci jádra MCU (u F401 to je 84 MHz) * Ověřit vliv rychlosti a nastavení úsporných opatření na spotřebu MCU * Nastavit Watchdog tak, aby hlídal činnost procesoru ===== Podklady pro cvičení ===== {{ :courses:b2m37mam:labs:mam_2023-cviceni_12.pdf | Procvičení registry, paměti, rozšíření I/O - Skalický }} {{ :courses:b2m37mam:labs:stm32f401re.pdf | Datasheet STM32F401 }} {{ :courses:b2m37mam:stm32f401_refmanual.pdf | Referenční manuál STM32F401 }} {{ :courses:b2m37mam:nucleo_64_pins.pdf | Datasheet Nucleo F401RE}} ==== Sleep a Stop mode ==== Pro další snížení spotřeby je možné nastavit procesor do některého z úsporných módů. Nejvíce úsporné módy jsou STOP módy, kterých může být i více než 1. Zde záleží na složitosti procesoru. V těchto módech pracují pouze některé periferie a zbytek procesoru je uspaný (včetně jádra) a dá se dosáhnout spotřeby jednotky uA nebo i méně. Přehled těchto možností je v {{ :courses:b2m37mam:stm32f401_refmanual.pdf#page=78 | referenčním manuálu v sekci Power Controller}} včetně možností probuzení. void sleepModeInit(void){ RCC->APB1ENR |= RCC_APB1ENR_PWREN; SCB->SCR &= ~( SCB_SCR_SLEEPDEEP_Msk ); // low-power mode = sleep mode } void stopModeInit(void){ RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR &= ~PWR_CR_PDDS; PWR->CR |= PWR_CR_LPDS; PWR->CR |= PWR_CR_CWUF; PWR->CR |= PWR_CR_LPLVDS; PWR->CR |= PWR_CR_MRLVDS; SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; } void sleepModeEnter(void){ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; for(int i=0; i<16; i++){ GPIOA->MODER |= GPIO_MODE_ANALOG << i*2; GPIOB->MODER |= GPIO_MODE_ANALOG << i*2; GPIOC->MODER |= GPIO_MODE_ANALOG << i*2; GPIOD->MODER |= GPIO_MODE_ANALOG << i*2; GPIOE->MODER |= GPIO_MODE_ANALOG << i*2; GPIOH->MODER |= GPIO_MODE_ANALOG << i*2; } RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOBEN; RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOCEN; RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIODEN; RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOEEN; RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOHEN; PC13_initIRQ(); stopModeInit(); // sleepModeInit(); __WFI(); // enter low-power mode PA5_init(); } void PA5_init(void){ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; GPIOA->MODER &= ~GPIO_MODER_MODE5_Msk; GPIOA->MODER |= 0x01 << 10; } void PC13_initIRQ(void){ RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; GPIOC->MODER &= ~GPIO_MODER_MODE13_Msk; GPIOC->PUPDR |= 0x01 << GPIO_PUPDR_PUPD13_Pos; EXTI->IMR |= EXTI_IMR_IM13; EXTI->FTSR |= EXTI_FTSR_TR13; SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC; NVIC_EnableIRQ(EXTI15_10_IRQn); } void EXTI15_10_IRQHandler(void){ EXTI->PR |= EXTI_PR_PR13; } int main(void){ PC13_initIRQ(); PA5_init(); GPIOA->ODR ^= GPIO_ODR_OD5; for(volatile int i=0; i<500000; i++); GPIOA->ODR ^= GPIO_ODR_OD5; for(volatile int i=0; i<500000; i++); while (1) { sleepModeEnter(); GPIOA->ODR ^= GPIO_ODR_OD5; for(volatile int i=0; i<500000; i++); GPIOA->ODR ^= GPIO_ODR_OD5; for(volatile int i=0; i<500000; i++); } } ==== Rychlost procesoru 1 MHz ==== Výchozí hodinový signál pro činnost mikroprocesoru je 16 MHz, který zajišťuje interní RC oscilátor. V případě potřeby snížení spotřeby a tím také výpočetního výkonu, je možné změnit nastavení AHB děličky kmitočtu pro snížení frekvence procesoru. Toto nastavení provedete přes konfigurační registr ''RCC->CFGR''. Detailní diagram najdete v {{ :courses:b2m37mam:stm32f401_refmanual.pdf#page=93 | referenčním manuálu v sekci RCC}}. void CLK_init_1MHz(void){ // vyckej nez najede interni 16 MHz RC oscilator while(!(RCC->CR & RCC_CR_HSIRDY)); RCC->CFGR |= 0x0B << RCC_CFGR_HPRE_Pos; // AHB preddelicka na /16 (16/16 = 1 MHz) } ==== Rychlost procesoru 84 MHz ==== Výchozí hodinový signál pro činnost mikroprocesoru je 16 MHz, který zajišťuje interní RC oscilátor. Většina procesorů s jádrem ARM ale může pracovat i na vyšších kmitočtech v řádu stovek MHz nebo i jednotek GHz. Z důvodu omezené maximální frekvence externího hodinového signálu (u F401 v rozsahu 4--26 MHz) je potřeba využít interní násobičky frekvence v podobě PLL bloku, který je složen z děliček (bloky ''M'' a ''P'') a násobičky kmitočtu (blok ''N''). Detailní diagram a použití najdete v {{ :courses:b2m37mam:stm32f401_refmanual.pdf#page=93 | referenčním manuálu v sekci RCC}}. Z důvodu menší rychlosti vybavení dat z FLASH paměti, je potřeba zařadit do čtení programu čekání v podobě wait cyklů. V případě frekvence 84 MHz postačí nastavit 2 cykly. Další informace lze nalézt v {{ :courses:b2m37mam:stm32f401_refmanual.pdf#page=46 | referenčním manuálu v sekci Embedded Flash Memory}}. {{:courses:b2m37mam:labs:screenshot_2024-12-16_at_13.32.21.png| Distribuce a generovaní hodinového signálu v STM32F401}} void CLK_init_84MHz(void){ // nastav latenci na 2 wait cykly FLASH->ACR |= 2 << FLASH_ACR_LATENCY_Pos; // vyckej nez najede interni 16 MHz RC oscilator while(!(RCC->CR & RCC_CR_HSIRDY)); // nyni je na vsech periferiich 16MHz // nastaveni PLL na max. frekvence pro STM32F401 (84 MHz) // vymazat predchozi nastaveni RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLM | RCC_PLLCFGR_PLLN | RCC_PLLCFGR_PLLP | RCC_PLLCFGR_PLLQ); // nastav M v toleranci 1MHz - 2 MHz, 2 lepsi kvuli jitter efektu RCC->PLLCFGR |= ?? << RCC_PLLCFGR_PLLM_Pos; // nastav N v toleranci 192-432 MHz RCC->PLLCFGR |= ?? << RCC_PLLCFGR_PLLN_Pos; // nastav P mene nebo rovno 84 MHz RCC->PLLCFGR |= ?? << RCC_PLLCFGR_PLLP_Pos; // nastav Q na /7 (336/7 = 48MHz - mene nebo rovno 48 MHz) RCC->PLLCFGR |= 7 << RCC_PLLCFGR_PLLQ_Pos; // APB1 preddelicka na /2 (84/2 = 42 MHz - v toleranci do 42 MHz, APB2 ma max. 84MHz) RCC->CFGR |= 0x04 << RCC_CFGR_PPRE1_Pos; // zapnuti PLL RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); // vyckej na nastartovani PLL RCC->CFGR |= 0x02 << RCC_CFGR_SW_Pos; // vyber PLL jako zdroje kmitoctu while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // vyckej na prepnuti na PLL } ==== Watchdog ==== Watchdog slouží k hlídání činnosti procesoru, aby běžel v nekonečné smyčce a nikde se nezasekl (typicky při dělení nulou nebo čekání na timeout apod.). Pokud se tomu tak stane, měl by procesor zareagovat (např. restartem procesoru nebo vyřešením problému. Běžné použití watchdogu se používá právě k restartování zacykleného procesoru, aby se vrátil na začátek své činnosti. V mikroprocesoru lze nalézt několik typů, buď okénkový nebo nezávislý watchdog. V následujícím příkladu se zaměříme na nezávislý (independent watchdog), který má svoje specifické požadavky. Pro nastavení je potřeba zápisu speciální sekvence ''IWDG->KR'' * ''0x5555'' - povolení zápisu do ''PR'' a ''RLR'' * ''0xCCCC'' - spuštění WDG * ''0xAAAA'' - obnovení WDG, reset časového limitu, nastaví hodnotu čítače WDG na hodnotu v ''RLR'' Vstupní kmitočet pro čítání impulzů je odvozen od interního RC oscilátoru o frekvencí cca. 32 kHz (Low Speed Internal, LSI). Nastavením předděličky a reload registru se poté určuje maximální perioda restartu WDG. Doba do které je potřeba WDG restartovat je v rozmezí cca 125us - 32s. Detailnější informace naleznete v {{ :courses:b2m37mam:stm32f401_refmanual.pdf#page=415 | referenčním manuálu v sekci Independent Watchdog}}. void IWDG_init(void){ IWDG->KR = 0x5555; // enable writing into PR and RLR registers IWDG->PR = 0x02; // prescaler by /16 IWDG->RLR = 0x0FFF; // reset value for reload register, 0x0FFF = 4095 IWDG->KR = 0xCCCC; // start IWDT }