Search
Procvičení registry, paměti, rozšíření I/O - Skalický
Datasheet STM32F401
Referenční manuál STM32F401
Datasheet Nucleo F401RE
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 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++); } }
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 referenčním manuálu v sekci RCC.
RCC→CFGR
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) }
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 referenčním manuálu v sekci RCC.
M
P
N
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 referenčním manuálu v sekci Embedded Flash Memory.
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 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
IWDG→KR
0x5555
PR
RLR
0xCCCC
0xAAAA
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 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 }