Szukaj

Nowości

28 maj 2020
06 luty 2020
05 grudzień 2019
17 sierpień 2019

E-mail

janusz<at>krzoska.eu

STM32 - dynamiczne sterowanie częstotliwością zegara systemowego

 

Upraszczając nieco sprawę procesory STM32 mogą być taktowane z różnych źródeł, a między innymi z zewnętrznego kwarca (zazwyczaj 8MHz), poprzez pętlę PLL, w której można ustawiać mnożnik częstotliwości podstawowej. Dzięki temu np. przy mnożniku 9 i kwarcu 8MHz procesor jest taktowany częstotliwością 72MHz. Powoduje to, że procesor jest wydajny, ale za to "prądożerny". Oprócz mnożnika pętli PLL mamy tam do dyspozycji jeszcze kilka różnych podzielników i dzięki temu możnemy sobie ustawić częstotliwość z jaką będzie taktowany procesor.

Daje to programiście dosyć szerokie możliwości zmniejszania poboru prądu. Np w sytuacji kiedy z jednej strony zależy nam na małym prądzie, bo urządzenie zasilane jest z baterii (akumulatorów), a z drugiej potrzeba nam dużej szybkości wysyłania danych na wyświetlacz kolorowy LCD o rozdzielczości np 320x240 i jeszcze do tego obsługiwać panel dotykowy. Można więc tak napisać program, aby domyślnie procesorek taktowany był np zegarem 16MHz, a przy wejściu do obsługi wyświetlacza, lub panelu dotykowego częstotliwość była zwiększana do 72MHz. Po zakończeniu obsługi tych urządzeń z powrotem zegar można przestawić na 16MHz i resztę programu realizować już z tą prędkością.

Oczywiście układ zegarowy RCC w tych procesorkach jest znacznie bardziej skomplikowany i posiada kilka różnych sygnałów zegarowych sterujących różnymi częściami procesorka, ale w tym opisie zajmę się tylko głównym sygnałem zegarowym SYSCLK.

Wszelkie operacje na mnożnikach i dzielnikach związanych z PLL muszą być wykonywane przy wyłączony układzie PLL. Należy więc przed zmianą tych wartości  wyłączyć PLL, zmienić odpowiednie rejestry związane z PLL i dopiero wtedy można załączyć układ PLL z nowymi ustawieniami. Oczywiście przy każdej zmianie częstotliwości zegara należy odpowiednio przekonfigurować wszelkie układy czasowe typu Timer, SysTick itp.

Poniżej przykładowe funkcje cpu_slow_speed() i cpu_normal_speed().

 

 

// przestawienie zegara cpu na tryb wolny (16MHz)
// dla oszczednosci pradu
void cpu_slow_speed()
{
	ErrorStatus HSEStartUpStatus;
  	NVIC_InitTypeDef NVIC_InitStructure;

	TIM_Cmd(TIM1, DISABLE);								// wylaczenie timera

  	// Wylacz przerwanie od TIM1 (Capture)				// wylaczenie przerwan od timera zeby podswietlanie nie migalo
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;	// przy zmianie zegara cpu
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  	NVIC_Init(&NVIC_InitStructure);
  	// Wylacz przerwanie od TIM1 (Update)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  	NVIC_Init(&NVIC_InitStructure);

  	if(bklt_level<100)									// jezeli poziom podswietlania < 100%
  		GPIO_ResetBits(GPIOB,GPIO_Pin_5);				// to wylacz podswietlanie

    if (SysTick_Config(8000000 / 1000))				// tymczasowe skonfigurowanie SysTicka na 8MHz(HSE)
	{
		while(1)   IWDG_ReloadCounter();				// W razie bledu petla nieskonczona
	}

  	RCC_DeInit();											// Reset ustawien RCC
  	RCC_HSEConfig(RCC_HSE_ON);								// Wlacz HSE
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();				// Czekaj az HSE bedzie gotowy

  	if(HSEStartUpStatus == SUCCESS)
  		{
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	FLASH_SetLatency(FLASH_Latency_2);					// zwloka dla pamieci Flash
   		RCC_HCLKConfig(RCC_SYSCLK_Div1);					// HCLK = SYSCLK 1
    	RCC_PCLK2Config(RCC_HCLK_Div1);						// PCLK2 = HCLK
    	RCC_PCLK1Config(RCC_HCLK_Div2);						// PCLK1 = HCLK/2
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_2);// PLLCLK = 8MHz * 9 = 72 MHz
    	RCC_PLLCmd(ENABLE);									// Wlacz PLL
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);	// Czekaj az PLL poprawnie sie uruchomi
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);			// PLL bedzie zrodlem sygnalu zegarowego
    	while(RCC_GetSYSCLKSource() != 0x08);				// Czekaj az PLL bedzie sygnalem zegarowym systemu
  	}

	TIM_TimeBaseStructure.TIM_Period = 100;					// konfiguracja timera dla fcpu=16MHz
	TIM_TimeBaseStructure.TIM_Prescaler = 320;  			// fclk = 16MHz/320 = 50kHz
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	TIM_Cmd(TIM1, ENABLE);									// zalaczenie timera
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);	//					funcji alternatywnych portow GPIO

    if (SysTick_Config(16000000 / 1000))					// konfiguracja SysTicka dla 16MHz
	{
		while(1)   IWDG_ReloadCounter();					// W razie bledu petla nieskonczona
	}
    														// Wlacz przerwanie od TIM1 (Capture)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);
  															// Wlacz przerwanie od TIM1 (Update)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);

	cpu_speed=CPU_SPEED_SLOW;
}

 

// Przestawienie zegara cpu na tryb szybki (72MHz)
// np dla szybkiego wykonania operacji na wyswietlaczu
void cpu_normal_speed()
{
	ErrorStatus HSEStartUpStatus;
  	NVIC_InitTypeDef NVIC_InitStructure;

	TIM_Cmd(TIM1, DISABLE);									// wylaczenie timera
	if(bklt_level<100)										// jezeli poziom podswietlania < 100%
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);					// to wylacz podswietlanie

  															// Wylacz przerwanie od TIM1 (Capture)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  	NVIC_Init(&NVIC_InitStructure);
  															// Wylacz przerwanie od TIM1 (Update)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  	NVIC_Init(&NVIC_InitStructure);

    if (SysTick_Config(8000000 / 1000))					// Konfiguracja SysTicka dla 8MHz(HSE)
	{
		while(1)   IWDG_ReloadCounter();					// W razie bledu petla nieskonczona
	}

	TIM_TimeBaseStructure.TIM_Period = 100;					// konfiguracja Timera dla 8MHz(HSE)
	TIM_TimeBaseStructure.TIM_Prescaler = 160;  			//fclk = 8MHz/160 = 50kHz
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  	RCC_DeInit();											// Reset ustawien RCC
  	RCC_HSEConfig(RCC_HSE_ON);								// Wlacz HSE
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();				// Czekaj az HSE bedzie gotowy

  	if(HSEStartUpStatus == SUCCESS)
  		{
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	FLASH_SetLatency(FLASH_Latency_2);					// zwloka dla pamieci Flash
   		RCC_HCLKConfig(RCC_SYSCLK_Div1);					// HCLK = SYSCLK 1
    	RCC_PCLK2Config(RCC_HCLK_Div1);						// PCLK2 = HCLK
    	RCC_PCLK1Config(RCC_HCLK_Div2);						// PCLK1 = HCLK/2
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);// PLLCLK = 8MHz * 9 = 72 MHz
    	RCC_PLLCmd(ENABLE);									// Wlacz PLL
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);	// Czekaj az PLL poprawnie sie uruchomi
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);			// PLL bedzie zrodlem sygnalu zegarowego
    	while(RCC_GetSYSCLKSource() != 0x08);				// Czekaj az PLL bedzie sygnalem zegarowym systemu
  	}
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);	//					funcji alternatywnych portow GPIO

    if (SysTick_Config(72000000 / 1000))					// Konfiguracja SysTicka dla 72MHz
	{
		while(1)   IWDG_ReloadCounter();					// W razie bledu petla nieskonczona
	}

	TIM_TimeBaseStructure.TIM_Period = 100;					// konfiguracja Timera dla 72MHz
	TIM_TimeBaseStructure.TIM_Prescaler = 1440;  			//fclk = 72MHz/1440 = 50kHz
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
    TIM_Cmd(TIM1, ENABLE);									// zalaczenie Timera
  															// Wlacz przerwanie od TIM1 (Capture)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);
  															// Wlacz przerwanie od TIM1 (Update)
  	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);

  	cpu_speed=CPU_SPEED_NORMAL;
}

 

 

Podane procedury są oczywiście przykładowe, ale myślę, że można za ich pomoca zrozumieć jak dynamicznie sterować częstotliwością zegara taktującego STM32.

Oczywiście sterowanie zegarem nie wyklucza stosowania trybów obniżonego poboru mocy procesora.

 

Free Joomla! template by L.THEME