logo stNěkdy je třeba něco zpomalit, nebo je potřeba generování časových pulzů. V dnešním článku se podíváme na základní 16bit časovače TIM6 a TIM7 s jejichž pomocí budeme blikat diodami v nastavených intervalech.

STM32F4 disponuje hned čtrnácti časovači. S jejichž pomocí je možné realizovat spoustu funkcí a některé časovače již obsahují možnost realizace například PWM. My se ale podíváme na „basic timers“, které umožňují jen obyčejné časování. Na další časovače a pokročilejší hrátky se podíváme v některém z dalších článků.

Časovače TIM6 a TIM7 jsou naprosto shodné. Jedná se o 16bit časovače s 16bit předděličkou hodin a funkcí auto-reload. Při události přetečení nebo auto-reload může být generováno přerušení nebo signál pro DMA (o obojím se také v některém z dalších článků seznámíme).

Blokové schéma
Obr.1: Blokové schéma časovače TIM6 a TIM7

V podstatě se jedná o čítač čítající nahoru (upcounter). Čítač je 16. bitový, takže maximální rozsah je od 0 do 65535. Čítač čítá rychlostí systémových hodin pro APB1 dělenou 16bit předděličkou. Pokud vše v MCU taktujeme maximální taktem, tak pro APB1 je hodinový signál 84MHz. Při hodnotě předděličky 0x0000 je čítač časován také taktem 84MHz. Při nastavení předěličky na hodnotu 0x0001 je ale čítač časován už jen frekvencí 42MHz, při hodnotě 0x0002 frekvencí 28MHz atd.

Dále můžeme přečíst ale dokonce i zapsat hodnotu přímo do čítače. Využívá se toho, pokud chceme zkrátit dobu čítání do nějaké události. Ale ke zkrácení času čítání se dá použít auto-reload, což je nakonec praktičtější (ukážeme si na příkladu). Veškeré nastavené hodnoty jsou uplatněny až ve chvíli, kdy je generován signál UEV (update event – při přetečení nebo auto-reload).
 

Registry

TIMx_CR1 (control register)
Základní nastavení čítače. Jedná se o 16bit registr, ale použitelných je jen určitých 5 bitů.

  • Bit 7 – ARPE (určuje zda bude hodnota auto-reload registru přednačtena. Tedy jestli bude aktualizována ihned nebo až v dalším cyklu časovače (0 – ihned; 1 – v dalším cyklu))
  • Bit 3 – OPM (pokud je nastaven, čítač po přetečení nebo auto-reload znova nečítá. Bit CEN je resetován. V opačném případě čítač čítá pořád dokola)
  • Bit 2 – URS (v případě že zakážeme generování příznaků, nastavením toho bitu povolíme generování signálu pro aktualizaci hodnot čítače - UEV)
  • Bit 1 – UDIS (nastavením toho bitu zakážeme generování signálu UEV a generování příznaku. Nedojde tedy ani k přerušení pokud jej využíváme)
  • Bit 0 – CEN (bez nastavení toho bitu časovač neběží. Je automaticky resetován pokud je nastaven OPM (one-puls mode))

TIMx_CR2 (control register)
Je určen k nastavení triggru (pro použití v DA převodníku). Opět se jedná o 16bit registr ale použity jsou pouze 3 bity jejichž kombinací určujeme co bude generovat trigger signál.

  • 000 – Reset (trigger je generován nastavením bitu UG v registru TIMx_EGR)
  • 001 – Enable (triger je generován bitem CEN registru TIMx_CR1)
  • 010 – Update (triger je generován, pokud nastane UEV (při přetečení nebo auto-reload))

TIMx_DIER (DMA/Interrupt enable register)
16bit registr, kterým povolujeme generování signálů přerušení nebo žádosti o DMA. Ty jsou generovány vždy, pokud dojde k přetečení nebo auto-reload čítače.

  • Bit 8 – UDE (nastavením povolíme generování žádosti o DMA)
  • Bit 0 – UIE (nastavením povolíme generování přerušení)

TIMx_SR (status register)
16bit registr ve kterém je využit jen jediný bit :D (Bit 0 - UIF). Tento bit je vždy nastaven pokud dojde k přetečení nebo auto-reload. Je nutné jej ručně resetovat.

TIMx_EGR (event generation registr)
16bit registr kde je opět využit jen jeden bit (Bit 0 - UG). Jeho nastavením dojde ke generování UEV, při které je časovač aktualizován a resetován.

TIMx_CNT (counter)
16bit registr. Jedná se přímo o čítač a je možno z něj hodnotu číst i ji měnit a to i za běhu samotného čítače.

TIMx_PSC (prescaler)
16bit registr do kterého uložíme vždy hodnotu, kterou chceme dělit hodinový signál.

TIMx_ARR (auto-reload register)
16bit registr. Do toho registru uložíme hodnotu, při které se má čítač resetovat.

 

Příklad

Teď si ukážeme jak jednu diodu rozblikáme. Dejme tomu že budeme chtít, aby oranžová dioda se vždy po jedné sekundě střídavě rozsvítila a zhasla. Jak nastavit GPIO pro rozsvícení diody už umíme. Takže povolíme časovač, například TIM6 a nastavíme hodnoty předděličky a auto-reload tak, aby jeden cyklus časovače trval přibližně jednu vteřinu.
 

/* Rreset casovace TIM6 */
RCC->APB1RSTR |= RCC_APB1RSTR_TIM6RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM6RST;
	
/* Povolení hodin pro casovac TIM6 */
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
	
/* Nastaveni */
/* Pozn.: casovac TIM6 je 16bit */
TIM6->CR1 |= TIM_CR1_ARPE;	// Povolení prednacteni auto-reload
TIM6->ARR = 65523;			// Auto-reload hodnota, pri ktere se ma citac resetovat
TIM6->PSC = 1281;			// Delicka hodinoveho signalu
TIM6->CR1 |= TIM_CR1_CEN;	// Spusteni casovace TIM6
TIM6->EGR = TIM_EGR_UG;		// Aktualizace (update event)
TIM6->SR = 0x0000;			// Vrácení status bitu na log 0

 

První řádky známe, resetujeme a povolujeme periferii TIM6. Dále v registru TIM6_CR1 povolíme přednačítání auto-reload.

A co že to vlastně je ten auto-reload? Čítač po resetu (po události UEV) čítá od nuly. Jakmile je jeho hodnota rovna hodnotě registru TIM6_ARR, dojde opět ke generování UEV a nastavení bitu UIF v registru TM6_SR. Čítač se resetuje a začíná opět od nuly. Pokud povolíme před načtení, a během běhu časovače změníme hodnotu auto-reload, čítač bude resetován ještě při staré hodnotě a až v dalším cyklu se použije nová hodnota auto-reload. Pokud přednačtení nepovolíme, je uplatněna změna této hodnoty okamžitě.

Takže dále zapíšeme hodnoty do registru auto-reload TIM6_ARR a do registru předděličky hodinového signálu TIM6_PSC.

Hodnoty jsou zvoleny tak, aby vzdálenost jednotlivých UEV byla přibližně jedna sekunda. Pokud víme, že základní hodinový signál má 84MHz, čítač se inkrementuje 84 milionkrát za sekundu. Takže snížíme frekvenci hodinového signálu a zjistíme, že se čítač za jednu sekundu inkrementuje přibližně 65523 krát. A právě při této hodnotě chceme, aby se časovač resetoval.

(1 / [CK_PSC / (TIMx_PSC + 1)]) * TIMx_ARR = Timer Cycle Time
(1 / [84e6 / (1281 + 1)]) * 65523 = 1,000005786 s

Příklad.1: Výpočet času mezi dvěma UEV, kde CK_PSC = 84000000, TIMx_PSC = 1281 a TIMx_ARR = 65523

Dále povolíme samotný běh časovače bitem CEN v registru TIM6_CR1 a celý jej resetujeme a aktualizujeme generováním UEV, a to tak, že zapíšeme logickou jedničku do registru TIM6_EGR. To nám ale vyvolá nejen událost UEV, ale i nastavení bitu UIF v registru TIM6_SR, proto jej nulujeme.

Nyní tedy můžeme napsat samotný kód pro blikání diody.
 

while(1)
{
	//Pokud dojde k UEV (update event) zmeni stav LED diody
	if(TIM6->SR & TIM_SR_UIF)	
	{
		if(GPIOD->ODR & GPIO_ODR_ODR_13)
			GPIOD->ODR &= ~GPIO_ODR_ODR_13;
		else
			GPIOD->ODR |= GPIO_ODR_ODR_13;
		TIM6->SR = 0x0000;					// Vrácení status bitu na log 0
	}
}

 

Cyklus while zajistí neustálé opakování podmínky. Podmínka je splněna, jakmile je nastaven bit UIF v registru TIM6_SR, neboť ten je nastaven, jen pokud dojde k UEV (tedy čítač dosáhl hodnoty auto-reload). Dále se ptáme, zda je dioda již rozsvícena, pokud ano, zhasneme ji, v opačném případě ji rozsvítíme.

Nesmíme zapomenou ručně nulovat UIF, jinak by při opakování cyklu while byla podmínka opět splněna a dioda by sice blikala, ale velice vysokou rychlostí :D.

Ještě přidám příklad s využitím obou časovačů, TIM6 i TIM7, kdy každý je použit pro blikání jiné diody a časovač TIM7 má mírně zkrácenou dobu mezi dvěma UEV.
 

#include<stm32f4xx.h>

void GPIOD_conf(void)
{
	/* Reset portu GPID */
	RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIODRST;
	RCC->AHB1RSTR &= ~(RCC_AHB1RSTR_GPIODRST);
	/* Povoleni hodin pro GPIOD */
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
	
	/* Nastaveni modu */
	GPIOD->MODER |= GPIO_MODER_MODER15_0 | GPIO_MODER_MODER13_0;	//nastaveni pinu 15 a 13 jako výstupní
	/* Nastaveni max. frekvence */
	GPIOD->OSPEEDR = GPIO_OSPEEDER_OSPEEDR15_1;	//maximalni frekvence 100MHz
	
	return;
}

void TIM6_conf(void)
{
	/* Rreset casovace TIM6 */
	RCC->APB1RSTR |= RCC_APB1RSTR_TIM6RST;
	RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM6RST;
	
	/* Povolení hodin pro casovac TIM6 */
	RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
	
	/* Nastaveni */
	/* Pozn.: casovac TIM6 je 16bit */
	TIM6->CR1 |= TIM_CR1_ARPE;	// Povolení prednacteni auto-reload
	TIM6->ARR = 65523;					// Auto-reload hodnota, pri ktere se ma citac resetovat
	TIM6->PSC = 1281;						// Delicka hodinoveho signalu
	TIM6->CR1 |= TIM_CR1_CEN;		// Spusteni casovace TIM6
	TIM6->EGR = TIM_EGR_UG;			// Aktualizace (update event)
	TIM6->SR = 0x0000;					// Vrácení status bitu na log 0
	
	return;
}

void TIM7_conf(void)
{
	/* Rreset casovace TIM7 */
	RCC->APB1RSTR |= RCC_APB1RSTR_TIM7RST;
	RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM7RST;
	
	/* Povolení hodin pro casovac TIM7 */
	RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
	
	/* Nastaveni */
	/* Pozn.: casovac TIM7 je 16bit */
	TIM7->CR1 |= TIM_CR1_ARPE;	// Povolení prednacteni auto-reload
	TIM7->ARR = 65523;					// Auto-reload hodnota, pri ktere se ma citac resetovat
	TIM7->PSC = 500;						// Delicka hodinoveho signalu
	TIM7->SR = 0x0000;					// Vrácení status bitu na log 0
	TIM7->CR1 |= TIM_CR1_CEN;		// Spusteni casovace TIM7
	TIM7->EGR = TIM_EGR_UG;			// Aktualizace (update event)
	
	return;
}

int main(void)
{
	GPIOD_conf();
	TIM7_conf();
	TIM6_conf();
	
		
	while(1)
	{
		//Pokud dojde k UEV (update event) zmeni stav LED diody
		if(TIM6->SR & TIM_SR_UIF)	
		{
			if(GPIOD->ODR & GPIO_ODR_ODR_13)
				GPIOD->ODR &= ~GPIO_ODR_ODR_13;
			else
				GPIOD->ODR |= GPIO_ODR_ODR_13;
			TIM6->SR = 0x0000;					// Vrácení status bitu na log 0
		}
		//Pokud dojde k UEV (update event) zmeni stav LED diody
		if(TIM7->SR & TIM_SR_UIF)	
		{
			if(GPIOD->ODR & GPIO_ODR_ODR_15)
				GPIOD->ODR &= ~GPIO_ODR_ODR_15;
			else
				GPIOD->ODR |= GPIO_ODR_ODR_15;
			TIM7->SR = 0x0000;					// Vrácení status bitu na log 0
		}
	}
}

 

Závěr

Dnes jsme si ukázali jednoduchou práci s časovači TIM6 a TIM7. Někdy příště si diodami zablikáme znova, ale s využitím přerušení. Také si někdy příště povíme o ostatních časovačích, které nabízejí mnoho dalších vylepšení.


Projek ke stažení ZDE.


 

Komentáře  

 
0 # Karel 2014-05-24 08:59
Díky za tenhle seriál, paráda. :-)
Odpovědět | Odpovědět citací | Citovat