Időzítők, számlálók

A fejezet tartalma:
A beágyazott rendszerek időben kell, hogy reagáljanak a bekövetkező eseményekre, vagy előre megadott ütemezés szerint kell, hogy végezzék feladataikat (pl. adatgyűjtés/adatmegjelenítés azonos időközönként). Ebből következően a beágyazott rendszereknek képeseknek kell lennie az alábbiakra:
A fenti feladatok közben konfliktushelyzet is kialakulhat: például egy külső esemény akkor kér kiszolgálást, amikor éppen egy ütemezett feladatot végez a rendszer. Ez azt jelenti, hogy a fenti követelmények még egy újabbal bővülnek: a beágyazott rendszereknek meg kell tudni különböztetni a sürgős kiszolgálást igénylő eseményeket a kevésbé sürgős feladatoktól.

Ebből következik, hogy szükségünk van olyan eszközökre és módszerekre, amelyek lehetővé teszik a hatékony idő-alapú tevékenység végzését. Főbb elemei ennek az eszköztárnak a megszakítások, amelyeket a következő fejezetben tárgyalunk, valamint az időzítők/számlálók, amelyekkel ebben a fejezetben foglalkozunk.

A számláló (counter) olyan digitális áramkör, amellyel feszültségimpulzusokat tudunk leszámlálni. Ha a leszámlálandó impulzusok nem külső forrásból származnak, hanem ismert, állandó frekvenciájú jelet vezetünk a számláló bemenetére, akkor pedig időzítőről (timer) beszélünk, amely lehetővé teszi számunkra az idő mérését, illetve a feladatok ütemezését.

Ütemezést tulajdonképpen már a legelső LED villogtató projektünknél is használtunk, amikor delayMs(500) függvényhívással arra utasítottuk a mikrovezérlőt, hogy fél másodpercig várakozzon a LED állapotváltások között. Az ilyen várakoztatás roppant egyszerűen, üres programciklusokkal is megvalósítható, ám  a blokkoló jellege miatt nem  hatékony.  Komolyabb programjainkban nyilván azt szeretnénk majd, hogy a mikrovezérlő a várakozások során más feladattal is foglalkozhasson, s az időmérés a háttérben történjen. Ezzel el is érkeztünk a hardveres számlálók és időzítők használatához...

Számlálókat gyakran használnak a digitális áramkörökben. Ezek  többnyire sorbakötött bistabil billenőáramkörök, melyek mindegyike egy-egy bitnyi információt tárol. Egy n-bites számláló 2n-1 állapotot képes felvenni. Például az alábbi ábrán látható 4 bites számláló bináris ábrázolásban 0000-tól 1111-ig, azaz 0-tól 15-ig számol.


1. ábra: Bináris, 4-bites szinkron számláló

Az 1. ábrán látható, J-K billenőkörökből (flip-flop) kialakított számláló jellemzői:
A 4-bites szinkron számláló kimeneteinek jelalakja a 2. ábrán látható.

2. ábra: A 4-bites szinkron számláló kimeneteinek jelalakja

A fentihez hasonló áramkörökkel találkozunk a mikrovezérlők perifériái között, ám ezeknek a számlálóknak még további feladatot is el kell tudni látnia:
Bizonyos esetekben a számlálás irányának beállíthatósága is követelmény (fel- vagy lefelé számlálás, esetleg fel-le számlálás).

Számlálók felhasználási területei

Ahogy a bevezetőben is írtuk, a beágyazott rendszerekben a számlálókat többféle célra is használhatjuk. Néhány tipikus felhasználási terület:

Események számlálása

Képzeljük el, hogy az úton elhaladó járműveket akarjuk megszámlálni, vagy a gyári futószalagon a késztermékeket. Esetleg az időegységenkénti radioaktív bomlások számát szeretnénk meghatározni egy Geiger-Müller detektor segítségével (a fukushimai baleset után készült ilyen, okostelefonokhoz kapcsolható eszköz). Ezekben a feladatokban az a közös, hogy végeredményben a számláláshoz valamilyen érzékelőt (szenzor) kell kapcsolnunk a számláló bemenetére. Az alaphelyzetből való indításhoz egy törlési lehetőségre is szükség lehet (nullázzuk a számlálót).


3. ábra: Eseményszámláló

Késleltetések előállítása

Különféle berendezések vezérlésénél általános gyakorlat az, hogy valamilyen tevékenységet adott idő elteltével kell elindítani, vagy befejezni. A mosógéptől a kenyérsütőig számtalan példát lehetne erre sorolni. Az időzítés megvalósításához egy ismert frekvenciájú órajel generátort kapcsolunk a számláló bemenetére, így a számlálót időzítőként használjuk. Például az alábbi ábrán egy órajel generátor 1 Hz-es órajelet biztosít a számláló számára, a számláló tartalma így másodpercenként növekszik eggyel. Ha nulláról indítjuk a számlálást és egy digitális komparátor a számláló mindenkori tartalmát egy előre beírt számmal (esetünkben 60-bal) összehasonlítja, akkor az előre megadott idő elteltével (esetünkben 60 s múlva) lesz "1" a digitális komparátor kimenetének állapota - jelezve, hogy eltelt a megadott idő


4. ábra: Időzítőként használt számláló

Két esemény bekövetkezte között eltelt idő mérése

Előfordulhat olyan feladat, amelyben két esemény között eltelt időt kell megmérnünk. Ilyen esemény lehet például a CH-05 ultrahangos szenzor kimenő jelének fel-, illetve lefutó éle. Megmérve az ezek között eltelt időt, s ismerve a hang terjedési sebességét, meghatározhatjuk a céltárgy távolságát, ahonnan a hang visszaverődött. Kényelmes megoldás, ha az első esemény bekövetkeztekor (Indítás) nullázni tudjuk a számlálót, a második esemény bekövetkeztekor (Leállítás) pedig rögzítjük a számláló állását egy másik regiszterben. Ezt az utóbbi folyamatot egyébként bemeneti jelrögzítésnek (Input Caprire) hívja a szakirodalom.

A gyakorlatban néha nem egyszerű az indító és a leállító jel elkülönítése, vagy nem nullázható a számláló, mert több jelrögzítő csatorna is tartozik hozzá. Ilyenkor mindkét esemény bekövetkeztekor rögzítjük a számláló értékét, majd kiolvassuk és gyorsan eltároljuk (programmegszakításban). Végül a két érték különbségéből megkapjuk a kívánt értéket.
 

5. ábra: Bemeneti jelrögzítés

Időzítők/számlálók a MKL25Z128VLK4 mikrovezérlőben

A Freescale KL25 Sub_Family Reference Manual szerint az MKL25Z128VLK4 mikrovezérlő az alábbi számlálókkal, illetve időzítőkkel rendelkezik:

A SysTick időzítő

A Cortex-M processzorok tartalmaznak egy SysTick nevű időzítőt, ami egyszerű lehetőséget biztosít periodikus megszakítások keltésére. A SysTick időzítő egy 24 bites visszafelé számlálót tartalmaz, amely nullára futáskor automatikusan újratöltődik (a töltési érték programozható), s egy megszakítást generál (a 15. számú megszakítási vektor). A megszakítást kiszolgáló szoftver lehet egy operációs rendszer része (taszkváltás az időszelet lejártakor). Természetesen azokban az alkalmazásokban, ahol nem használunk operációs rendszert, a SysTick időzítő tetszőleges célra felhasználható (pl. LED villogtatás, vagy más feladat ütemezéséhez).

A SysTick megszakítások természetesen letilthatók, s akkor lekérdezéses módban is vizsgálhatjuk a számláló állapotát.

A SysTick időzítő regiszterei

A SysTick időzítő működését négy regiszter szabályozza. Ezek közül a negyedikkel (a kalibrációs regiszterrel) ebben a tananyagban mi nem foglalkozunk. Mivel projektjeink létrehozásánál becsatoljuk a CMSIS core definíciókat és függvényeket, ezért a SysTick használatánál a "CMSIS név" oszlopban megadott nevekkel hivatkozhatunk a regiszterekre (a definíciókat a core_cm0plus.h állomány tartalmazza).

1. táblázat: A SysTick modul regiszterkészlete
Cím
Regiszternév
CMSIS név
A regiszter funkciója
0xE000_E010
SYST_CSR SysTick->CTRL
Vezérlő és állapotjelző regiszter
0xE000_E014
SYST_RVR SysTick->LOAD Újratöltési érték regiszter
0xE000_E018
SYST_CVR SysTick->VAL Számláló pillanatnyi értéke
0xE000_E01C
SYST_CALIB SysTick->CALIB Kalibrációs regiszter
A SysTick->CTRL vezérlő és állapotjelző regiszter bitjeinek kiosztása az alábbi ábrán látható:

6.a ábra: A SysTick->CTRL vezérlő és állapotjelző regiszter bitkiosztása

ahol az egyes bitek szerepe a következő:
Megjegyzés: az a gyártótól függ, hogy a Clk Source bit nulla értékénél milyen alternatív órajelet biztosít, vagy hogy biztosít-e egyáltalán. A FRDM-KL25Z kártya MKL25Z128VLK4 mikrovezérlője esetében a rendszer órajel 16-tal leosztott frekvenciáját kapjuk meg Clk Source = 0 beállítással.

A SysTick->LOAD regiszterbe írjuk be azt a számot, ahonnan a visszaszámlálás kezdődik. Az N órajel időtartamú visszaszámláláshoz N-1-et kell írnunk ebbe a regiszterbe. Mivel a számláló 24 bites, ezért 0x000001 - 0xFFFFFF közötti értékeket írhatunk a SysTick->LOAD regiszterbe. Elvileg  nullát is írhatnánk bele, de annak nincs értelme, mert 1 -> 0 átmenetkor keletkezik a Count esemény...

A SysTick->VAL regiszterből olvashatjuk ki a számláló pillanatnyi értékét (amennyiben kíváncsiak vagyunk rá), illetve nullát írhatunk bele konfiguráláskor, hogy engedélyezéskor újratöltéssel kezdjünk.

A SysTick->CALIB regiszter implementációfüggő, a gyártótól függ, hogy mit valósít meg belőle. A regiszter bitkiosztása az alábbi ábrán látható.


6.b ábra: A SysTick->CALIB regiszter bitkiosztása

Az egyes bitek szerepe:
A FRDM-KL25Z kártya MKL25Z128VLK4 mikrovezérlője esetében a SysTick->CALIB regiszter kiolvasása nulla értéket ad vissza. Ez azt jelenti, hogy NOREF = 0, tehát a CPU frekvencián kívül más órajelet is választhatunk (jelesül a 16-tal leosztott rendszerórajelet), SKEW = 0, és TENMS = 0, tehát nincs megadva a 10 ms időzítéshez szükséges óraütések száma, így annak kerekítési hibája sincs.

Mintaprogramok SysTick használatához

Az alábbiakban három egyszerű programon keresztül mutatjuk be a SysTick időzítő használatát. Az elsőben a konfigurálást és a számláló pillanatnyi értékének kiolvasását mutatjuk be. A másodikban egy rövid (250 ms) késleltetéshez használjuk fel a SysTick időzítőt. A harmadik programban pedig azt mutatjuk be, hogyan használhatjuk fel a SysTick időzítőt tetszőleges hosszúságú időzítésre (a SysTick itt egy időegységnyi késleltetésre szolgál, s a köré szervezett for ciklus számlálja le az előírt számú időegységet).

Program5_1: SysTick konfigurálása

Ebben a programban szabadonfutó módba állítjuk a SysTick számlálót és a periodikusan kiolvasott számláló legfelső két bitjét megjelenítjük  az RGB LED zöld és piros színkomponensein, láthatóvá téve a számláló bitjeinek billegését. A megjelenítéshez a  számláló értékét 4 bináris helyiértékkel jobbra léptetjük, s ezt az értéket írjuk ki a B portra. Így a számláló 23. bitje a PTB19 lábra kötött zöld LED-et, a 22. bit pedig a PTB18-ra kötött piros LED-et vezérli.

1. lista: Program5_1/main.c listája
/* Program5_1
* LED-ek villogtatása a SysTick számláló felhasználásával.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* Megjegyzés: A CPU frekvencia 20.97 MHz a könyvben szereplő 41 MHz helyett!
*/

#include <MKL25Z4.H>

int main (void) {
int c;
//--- A GPIOB port konfigurálása ----
SIM->SCGC5 |= 0x0400; // Port B engedélyezése (bit 10)
PORTB->PCR[18] = 0x100; // PTB18 legyen GPIO (MUX = 1)
PORTB->PCR[19] = 0x100; // PTB19 legyen GPIO (MUX = 1)
PTB->PDDR |= 0xC0000; // PTB18 és PTB19 legyen kimenet (bit18 és 19=1)

//--- SysTick konfigurálása ---------
SysTick->LOAD = 0xFFFFFF; // Újratöltés érték legyen a maximális
SysTick->VAL = 0; // Kezdéshez alaphelyzetbe állunk
SysTick->CTRL = 5; // Engedélyezés, nincs interrupt, rendszer órajel a forrás

while (1) {
c = SysTick->VAL; // Számláló kiolvasása
PTB->PDOR = c >> 4; // legfelső két bit kijelzése
}
}

Program5_2: 250 ms késleltetés SysTick használatával

Ebben a programban 250 ms késleltetésnek megfelelő számú számlálási ciklust végeztetünk a SysTick számlálóval, s figyeljük a COUNT jelzőbit bebillenését.  A késleltetés leteltével átbillentjük a PTB18-ra kötött piros LED állapotát.

Az újratöltési érték kiszámításánál vegyük figyelembe, hogy esetünkben az alapértelmezett CPU frekvencia eltér a Mazidi könyvben megadott értéktől! Az újratöltési értéket úgy kapjuk meg, hogy a rendszer órajel másodpercenkénti periódusainak számát (kb. 20 970 000 1/s) megszorozzuk a kívánt időtartammal (itt most 250 ms = 0,25 s).

      Újratöltési érték = CPU freq. * időtartam = 20 970 000 1/s * 0,25 s = 5 242 500

Ne felejtsük el ellenőrizni, hogy az így kiszámolt érték belefér-e a 24 bites számlálóba, tehát az újratöltési érték nem lehet nagyobb 0xFFFFFF-nél, azaz 16 777 215-nél! Ez itt a fenti értéknél teljesül. Az újratöltési regiszterbe a fenti értéket eggyel csökkentve kell beírni. Ez itt most fölösleges finomkodás lenne, hiszen a belső RC oszcillátor eleve pontatlan, s a 20.97 MHz-es érték is kerekített érték.

2. lista: Program5_2/main.c listája
/* Program5_2
*
* A piros LED villogtatása a SysTick számláló felhasználásával.
* A program 250 ms-os lejáratra állítja a SysTick számlálót
* és a a piros LED állapotát átbillentjük, amikor a COUNT
* bit nullára futást jelez a SysTick->CTRL regiszterben.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
* Megjegyzés: A CPU frekvencia nálunk 20.97 MHz a könyvben szereplő 41 MHz helyett!
*/

#include <MKL25Z4.H>

int main (void) {
int c;
//--- A GPIOB port konfigurálása ----
SIM->SCGC5 |= 0x0400; // Port B engedélyezése (bit 10)
PORTB->PCR[18] = 0x100; // PTB18 legyen GPIO (MUX = 1)
PTB->PDDR |= 0x40000; // PTB18 legyen kimenet (bit18)

//--- SysTick konfigurálása ---------
SysTick->LOAD = 5242500; // Újratöltés érték 250 ms-hoz
SysTick->VAL = 0; // Kezdéshez alaphelyzetbe állunk
SysTick->CTRL = 5; // Engedélyezés, nincs interrupt, rendszer órajel
//--- végtelen ciklus ---------------
while (1) {
if(SysTick->CTRL & 0x10000) // Ha a COUNT jelző bebillent...
PTB->PTOR = 0x040000; // billentsük át a piros LED állapotát!
}
}
Megjegyzés: Vegyük észre, hogy a kezdeti konfigurálás után csak a COUNT jelző figyelésével kell foglalkoznunk, mivel a számláló újratöltése, a számlálás újrakezdése és a COUNT jelzőbit törlése is automatikusan történik!

Program5_3: A delayMs() újratervezése SysTick használatával

Ebben a programban a zöld LED-et villogtatjuk, 1000 ms-onként átbillentve az állapotát. A 24 bites SysTick számláló csak korlátozott időtartamú időzítést tesz lehetővé. Az előző programban bemutatott módon 1000 ms késleltetés már nem állítható elő. Ebben a programban megmutatjuk, hogyan használhatjuk fel a SysTick időzítőt tetszőleges hosszúságú időzítésre. Visszatérünk a delayMs() függvényhez, melynek belsejében most a SysTick időzítő segítségével állítunk elő egy időegységnyi késleltetést, s a köré szervezett for ciklus számlálja le az előírt számú időegységeket. Az időegységnek 1 ms-ot választunk, így az ennek megfelelő újratöltési érték a CPU frekvencia ezredrésze lesz:

    Újratöltési érték = CPU freq. * időtartam = 20 970 000 1/s * 0,001 s = 20 970
 

3. lista: Program5_3/main.c listája
/* Program5_3
*
* A zöld LED villogtatása a SysTick számláló felhasználásával.
* Ebben a programban a SysTick számlálót használjuk időzítésre
* a delayMs() függvényben.
*
* Esetünkben a rendszer órajel alapértelmezetten 20.97 MHz.
* SysTick 20970-től számlál visszafelé, ami 1 ms késleltetést ad.
* A programban szereplő 1000 késleltetési ciklus végeredményben
* 1 ms * 1000 = 1 sec időzítést eredményez, ennyi időnként váltjuk
* át a zöld LED állapotát. A zöld LED a PTB19 lábra csatlakozik.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
* Megjegyzés: Az alapértelmezett CPU frekvencia esetünkben 20.97 MHz
* a könyvben szereplő 41 MHz helyett!
*/

#include <MKL25Z4.H>
void delayMs(int n); // Késleltető függvény

int main (void) {
SIM->SCGC5 |= 0x400; // A GPIOB port engedélyezése
PORTB->PCR[19] = 0x100; // PTB19 legyen GPIO módban!
PTB->PDDR |= 0x080000; // PTB19 legyen kimenet!
while (1) {
delayMs(1000); // 1000 ms késleltetés
PTB->PTOR = 0x080000; // Átbillentjük a zöld LED állapotát
}
}

void delayMs(int n) {
int i;
SysTick->LOAD = 20970 - 1; // Újratöltési érték 1 ms késleltetéshez
SysTick->VAL = 0; // Számláló törlése
SysTick->CTRL = 0x5; // Engedélyezés, nincs interrupt, rendszer órajel
for(i = 0; i < n; i++) {
while((SysTick->CTRL & 0x10000) == 0); // A COUNT jelzőre várunk
}
SysTick->CTRL = 0; // SysTick leállítása
}

Általános célú időzítő/számláló modulok (TPM)

Az  MKL25Z128VLK4 mikrovezérlő 3 db általános felhasználású időzítő/számláló egységgel rendelkezik, melyek elnevezése (TPM0, TPM1, illetve TPM2, általánosságban TPMx) a Timer/PWM Module név rövidítéséből ered. Részletes leírásuk a Freescale KL25 Sub_Family Reference Manual 31. fejezetében található. Ezek a modulok 16-bites számlálók, amelyek szabadonfutó, illetve modulo számlálóként tudnak felfelé számlálni. A "modulo" számlálás azt jelenti, hogy a számlálás 0-tól az időzítő MOD regiszterébe beírt számig tart, utána nullánál folytatódik. Lehetőség van a le/fel számlálásra is, amikor a számlálás 0-tól MOD-ig, majd MOD-től 0-ig tart. Ez a mód különösen az ún. szimmetrikus PWM jelek (Center-Aligned PWM mód) előállításhoz fontos. Mindegyik számlálóhoz több impulzus-szélesség modulációra (PWM), bemeneti jelrögzítésre (Input Capture), vagy digitális komparálásra (Output Compare használható csatorna is kapcsolódik. TPM0 6 db csatornával, TPM1 és TPM2 pedig két-két csatornával rendelkezik. A számlálók bemenetére választhatóan külső jelek fogadására alkalmas mikrovezérlő lábat vagy belső órajelet konfigurálhatunk. A számláló előtt egy max. 7 bites előosztót is használhatunk (1, 2, 4, 8, 16, 32, 64 vagy 128-szoros előosztással).


7. ábra: Egy TPM számláló/időzítő blokkvázlata

Egy TPM számláló/időzítő részegységének  felépítési vázlata a 7. ábrán látható. A főbb  összetevők: a bemeneti jelválasztó, az előszámláló, a modulo számláló, a modulo adatregiszter, valamint  a túlcsordulást jelző és megszakításkérő  áramkörök. A vezérlő bitek, illetve bitcsoportok szerepéről a konfigurálás kapcsán lesz szó.

A TPMx modulok regiszterei

A Freescale MKL25Z128VLK4 mikrovezérlő három TPM modulja hasonló regiszterkészlettel rendelkezik (az TPM0 modul több PWM csatornával rendelkezik, ezért ennek több regisztere van, mint a TPM1 és TPM2 moduloknak). A regiszterkészletek báziscímei:
Az alábbiakban a TPM0 egység regiszterkészletét ismertetjük röviden.

2. táblázat: A TPM0 időzítő modul regiszterkészlete
Regiszternév
A regiszter funkciója Cím
TPM0_SC Status and Control (állapotjelző és vezérlő regiszter)
4003_8000
TPM0_CNT Counter (számláló regiszter)
4003_8004
TPM0_MOD
Modulo (számlálási határ)
4003_8008
TPM0_C0SC
Channel 0 Status and Control (0. csatorna állapotjelző és vezérlő) 4003_800C
TPM0_C0V
Channel 0 Value (0. csatorna adatregiszter)
4003_8010
TPM0_C1SC Channel 1 Status and Control (1. csatorna állapotjelző és vezérlő) 4003_8014
TPM0_C1V Channel 1 Value (1. csatorna adatregiszter) 4003_8018
TPM0_C2SC Channel 2 Status and Control (2. csatorna állapotjelző és vezérlő) 4003_801C
TPM0_C2V Channel 2 Value (2. csatorna adatregiszter) 4003_8020
TPM0_C3SC Channel 3 Status and Control (3. csatorna állapotjelző és vezérlő) 4003_8024
TPM0_C3V Channel 3 Value (3. csatorna adatregiszter) 4003_8028
TPM0_C4SC Channel 4 Status and Control (4. csatorna állapotjelző és vezérlő) 4003_802C
TPM0_C4V Channel 4 Value (4. csatorna adatregiszter) 4003_8030
TPM0_C5SC Channel 5 Status and Control (5. csatorna állapotjelző és vezérlő) 4003_8034
TPM0_C5V Channel 5 Value (5. csatorna adatregiszter) 4003_8038
TPM0_STATUS
Capture and Compare Status (Jelrögzítés és komparálás státusza)
4003_8050
TPM0_CONF
Configuration (Konfiguráció)
4003_8084
Az egyszerű számlálási/időzítési feladatokhoz a felsoroltak közül csak az első három regiszterrel lesz dolgunk, a többi a jelrögzítés (Input Capture), összehasonlítás (Output Compare), illetve az impulzusszélesség-moduláció (PWM) módoknál kap szerepet.

A TPM0_SC állapotjelző és vezérlő regiszter bitkiosztása az alábbi ábrán látható:


8. ábra: A TPMx_SC regiszter bitkiosztása

A 32 bites regiszternek csak a legalsó 9 bitjét használjuk. A w1c jelzés arra utal, hogy a TOF jelzőbitet 1 írásával lehet törölni. Az egyes bitek szerepe:
A TPMx_CNT regiszter legalsó 16 bitje a számláló pillanatnyi állását tárolja. RESET vagy a regiszter írása törli a regiszter tartalmát. Hardveres nyomkövetésnél a számláló nem számlál, hacsak másképp nem konfiguráljuk a TPM0_CONF regiszter módosításával.

A TPMx_MOD regiszter legalsó 16 bitje a számlálási modulo (körülfordulási) értéket tartalmazza. Amikor a számláló elérte ezt az értéket, és újra inkrementál, akkor '1'-be billen a TOF jelzőbit. A számláló új értéke attól függ, hogy milyen számlálási módban vagyunk. Felfelé számlálásnál a MOD értéket a 0 követi, fel/le számlálás esetén pedig MOD-1 lesz a következő érték.

A TPMx modulok konfigurálása

Az alábbiakban a legegyszerűbb üzemmód (egyszerű időzítő, TOF figyelése lekérdezéses módban) beállításának lépéseit mutatjuk be.

1. A TPMx modul engedélyezése:
Mielőtt valamelyik TPMx időzítőt használatba vesszük, engedélyezni a működését a System Clock Gating Control 6 (SIM_SCGC6) nevű regiszter megfelelő bitjének '1'-be állításával az alábbi ábra szerint. A SIM_SCGC6 regiszter a Rendszer-integrációs Modulhoz (SIM) tartozik, részletes leírása a Freescale KL25 Sub_Family Reference Manual 12. fejezetében található.

9. ábra: A SIM_SCGC6 regiszter bitkiosztása

A TPM0, TPM1, TPM2 bitek valamelyikének '1'-be állítása engedélyezi az adott TPMx modul órajelét, ezzel lehetővé téve annak működését. Ez az órajel a rendszerbusz frekvenciája, s nem azonos azzal az órajellel, amit a számláló bemenetére kapcsolunk, ez csupán a regiszterek írását, olvasását, s a számláló működését biztosítja.

2. Az órajel kiválasztása: amikor a TPMx modult időzítőként kívánjuk használni, akkor ismert frekvenciájú órajelet kell biztosítani a számláló bemenetén. Az órajel kiválasztása az UART0 modul esetéhez hasonló módon történik: többféle lehetőség közül választhatunk a SIM rendszer-integrációs modul SIM_SOPT2 regiszterének konfigurálásával. Azonban a választott órajel rendelkezésre állása függ az MCG órajel generátor konfigurálásától.

3. táblázat: Az előre definiált órabeállítások esetén választható TPM órajelek
MCG mód MCGFLLCLK
MCGPLLCLK/2 OSCERCLK MCGIRCLK
0
20.97 MHz
----
---- 32,768 kHz
1
---- 48 MHz 8 MHz
----
2
---- ---- ---- 4 MHz
3
---- ---- 4 MHz
----
4
---- 48 MHz 8 MHz
----
A Keil MDK 5.20 ötféle előre definiált MCG konfigurációt támogat, melyek közül a 0-ás sorszámú az alapértelmezett. Ebben a beállításban a rendszer órajele alapértelmezetten a belső oszcillátorral és FLL áramkörrel előállított 20,97 MHz lesz, amit az MCGFLLCLK választásával (PLLFLLSEL = 0, TPMSRC = 01) kapcsolhatjuk a TPMx modulok órajel bemenetére (mindegyik TPMx időzítő modul ugyanazt a közös órajelet használja, amit az MGC egység és a SIM_SOPT2 regiszter beállításával konfigurálunk).


10. ábra: UART0 órajelének előállítása


11. ábra: A SIM_SOPT2 regiszter bitkiosztása

A SIM_SOPT2 regiszter bitcsoportjai közül TPMSRC[1:0] a TPMx modulok közös órajelének kiválasztására szolgál (00: órajel letiltva, 01: MCGFLLCLK vagy MCGPLLCLK/2, 10: OSCERCLK, 11: MCGIRCLK), a PLLFLLSEL bit az FLL vagy PLL áramkör kimenő órajelének kiválasztására szolgál (0: MCGFLLCLK, 1: MCGPLLCLK/2), s ez a bit más perifériák (pl. UART0) működésére is hatással van!

Megjegyzés: A Freescale MKL25Z128VLK4 mikrovezérlő órajelenek előállításáról és elnevezéseiről a KL25 Sub-Family Reference Manual 5. fejezetében, a SIM rendszer-integrációs modul regisztereinek szerepéről és bitkiosztásairól a 12. fejezetében, a többcélú órajel generátor (MCG)  felépítéséről és konfigurálásáról pedig a 24. fejezetben találunk részletes információt.

3. Tiltsuk le a TPMx modult a konfigurálás idejére: írjunk nullát a TPMx_SC regiszterbe!

4. Töltsük fel a  modulo regisztert: a megfelelő  értéket írjuk be a TPMx_MOD regiszterbe!

5. Töröljük a TOF jelzőbitet: bármilyen meglepő, a törléshez 1-et kell írnunk a TPMx_SC regiszter TOF bitjébe!

6. Töröljük a számláló regisztert: írjunk nullát a TPMx_CNT regiszterbe!

7. Engedélyezzük a számláló működését a megfelelő üzemmód beállításával együtt: egyszerű időzítéshez a TPMx_SC regiszter CMOD bitcsoportját állítsuk 01-be, a PS bitcsoportot pedig állítsuk be a kívánt előszámlálási aránynak megfelelően (000: 1x, 001: 2x, 010: 4x, ... 111: 127x leosztás)!

8. Az időzítéshez: várjunk addig, amíg a TOF jelzőbit '1' nem lesz!

Program5_4: időzítés TPM0-val

Ebben a program kék LED-et villogtatjuk, s a TPM0 időzítőt  használjuk a késleltetés előállításához. Esetünkben a rendszer órajel alapértelmezetten 20.97 MHz.  A TPM0 16 bites számlálója legfeljebb 65536/20 970 000 Hz = kb. 3,125 ms késleltetést biztosít számunkra.  A programban ezért egy for ciklusban 160 darab túlcsordulást várunk  meg, mielőtt átváltanánk a kék LED állapotát. Végeredményben  tehát 3,125 ms * 160 = 500 ms telik el két állapotváltás között.  A kék LED a PTD1 lábra csatlakozik, ezt a lábat GPIO kimenetnek kell konfigurálnunk.

4. lista: Program5_4/main.c listája
/* Program5_4
*
* Ebben a program kék LED-et villogtatjuk, s a TPM0 időzítőt
* használjuk a késleltetés előállításához.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
* Megjegyzés: Az alapértelmezett CPU frekvencia esetünkben 20.97 MHz
* a könyvben szereplő 41 MHz helyett!
*/

#include <MKL25Z4.H>

int main (void) {
int i;

SIM->SCGC5 |= 0x1000; // A D port engedélyezése
PORTD->PCR[1] = 0x100; // PTD1 pin legyen GPIO módban!
PTD->PDDR |= 0x02; // PTD1 legyen kimenet!

SIM->SCGC6 |= 0x01000000; // A TPM0 időzítő engedélyezése
SIM->SOPT2 |= 0x01000000; // MCGFLLCLK legyen az órajel!
TPM0->SC = 0; // Konfiguráláshoz letiltjuk az időzítőt
TPM0->MOD = 0xFFFF; // Maximális modulo érték (65536-1)
TPM0->CNT = 0; // A számláló törlése
TPM0->SC |= 0x80; // TOF törlése
TPM0->SC |= 0x08; // Számláló engedélyezése (CMOD=01, PS=000)

while (1) {
for(i = 0; i < 160; i++) {
while(!(TPM0->SC&0x80));// TOF-ra várunk...
TPM0->SC |= 0x80; // TOF törlése
}
PTD->PTOR = 0x02; // Kék LED (PTD1) átbillentése
}
}

Program5_5: A TPM0 előosztójának használata

Ebben a program is a kék LED-et villogtatjuk, s a TPM0 időzítőt  használjuk a késleltetés előállításához. A késleltetési idő megnyújtásához azonban most nem for ciklushoz folyamodunk (több túlcsordulás kivárásával), hanem a bemenő jel frekvenciáját osztjuk le a TPM0 előosztójának bekapcsolásával. A maximális, 128-szoros leosztás és a maximális modulo érték 65536 * 128 / 20 970 000 Hz = kb. 400 ms késleltetést biztosít számunkra.  Végeredményben  tehát 400 ms telik el két állapotváltás között.  A kék LED a PTD1 lábra csatlakozik, ezt a lábat GPIO kimenetnek kell konfigurálnunk.

5. lista: Program5_5/main.c listája
/* Program5_5
*
* Ebben a program kék LED-et villogtatjuk, s a TPM0 időzítőt
* használjuk a késleltetés előállításához.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*/

#include <MKL25Z4.H>

int main (void) {
SIM->SCGC5 |= 0x1000; // A D port engedélyezése
PORTD->PCR[1] = 0x100; // PTD1 pin legyen GPIO módban!
PTD->PDDR |= 0x02; // PTD1 legyen kimenet!

SIM->SCGC6 |= 0x01000000; // A TPM0 időzítő engedélyezése
SIM->SOPT2 |= 0x01000000; // MCGFLLCLK legyen az órajel!
TPM0->SC = 0; // Konfiguráláshoz letiltjuk az időzítőt
TPM0->SC = 0x07; // Az előszámláló leosztása 128 legyen!
TPM0->MOD = 0xFFFF; // Maximális modulo érték (65536-1)
TPM0->CNT = 0; // A számláló törlése
TPM0->SC |= 0x80; // TOF törlése
TPM0->SC |= 0x08; // Számláló engedélyezése (CMOD=01)

while (1) {
while(!(TPM0->SC&0x80)); // TOF-ra várunk...
TPM0->SC |= 0x80; // TOF törlése
PTD->PTOR = 0x02; // Kék LED (PTD1) átbillentése
}
}

Program5_6: A TPM1 időzítő használata

Ez a program lényegeben megegyezik a Program5_5 mintapéldával, ebben is a kék LED-et villogtatjuk, de most a TPM1 időzítőt  használjuk a késleltetés előállításához. A programban a TPM0 -> TPM1 átnevezések mellett ügyeljünk arra, hogy a SIM->SCGC6 regiszterben is egy másik bitet kell '1'-be állítanunk (lásd a 9. ábrán)!

6. lista: Program5_6/main.c listája
/* Program5_6
*
* Ebben a programban a TPM1 időzítőt használjuk.
* Ettől eltekintve megegyezik a Program5_5 mintapéldával.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*/

#include <MKL25Z4.H>

int main (void) {
SIM->SCGC5 |= 0x1000; // A D port engedélyezése
PORTD->PCR[1] = 0x100; // PTD1 pin legyen GPIO módban!
PTD->PDDR |= 0x02; // PTD1 legyen kimenet!

SIM->SCGC6 |= 0x02000000; // TPM1 időzítő engedélyezése
SIM->SOPT2 |= 0x01000000; // MCGFLLCLK legyen a TPM órajel!

TPM1->SC = 0; // TPM1 letiltása a konfiguráláshoz
TPM1->SC = 0x07; // Az előszámláló leosztása: 128
TPM1->MOD = 0xFFFF; // Maximális modulo érték (65536-1)
TPM1->CNT = 0; // A számláló törlése
TPM1->SC |= 0x80; // TOF törlése
TPM1->SC |= 0x08; // Számláló engedélyezése (CMOD=01)

while (1) {
while(!(TPM1->SC&0x80)); // TOF-ra várunk...
TPM1->SC |= 0x80; // TOF törlése
PTD->PTOR = 0x02; // Kék LED (PTD1) átbillentése
}
}

Program5_7: Hosszabb időzítés kisfrekvenciás órajellel

A TPM modul regisztereinek írása és a belső működése ugyan a rendszerbusz órajelével szinkronizált, de a számláló bemenetére aszinkron órajel is kapcsolható. Ezt a lehetőséget használjuk ki az alábbi példában, ahol a 32,768 kHz-es MCGIRCLK órajelet választjuk ki TPM órajelnek, s így az előzőeknél jóval hosszabb időzítéseket is előállíthatunk.

A Program5_7 mintapéldában a kék LED-et villogtatjuk, a TPM0 időzítőt  használjuk a késleltetés előállításához, s az MCGIRCLK órajelet választjuk ki TPM órajelnek. A TPM0 időzítő előosztóját 32-szeres leosztásra konfiguráljuk, így 1024 számláló inkrementálás 1 s késleltetésnek felel meg. A 16 bites számlálóval így kb. 1 ms - 64 s közötti időzítéseket állíthatunk be (most a ms valójában 1/1024 s időzítést jelent, ennek egész számú többszöröseit állíthatjuk be). A programban szereplő 5120-1 modulo beállítással tehát azt érjük el, hogy a LED állapotváltásai között 5 s telik el.

A Mazidi könyv Program5_7 mintapéldája egy kompatibilitási problémát is felszínre hozott: az újabb kiadású (v2.50) RTE Device Startup másképp inicializálja az MKL25Z4 mikrovezérlőket, mint a korábbi kiadás. Ez nem csupán a CPU frekvenciában jelent eltérést (20.97 MHz a v1.x kiadásokban szereplő 41.94 MHz helyett), hanem abban is, hogy a többcélú órajel generátor alapértelmezett beállításában nincs engedélyezve az MCGIRCLK használata.

Ennek a problémának a megoldására két lehetőség is van:
  1. Ne hagyatkozzunk az alapértelmezésre, hanem explicit módon adjuk meg a kiválasztott, előre definiált órajel generátor beállítást, akkor is, ha a 0. konfigurációt választjuk! Ehhez a system_MKL25Z4.h állományba egyszerűen szúrjuk be a #define CLOCK_SETUP 0 sort.
  2. Magunk is engedélyezhetjük az az MCGIRCLK használatát, ha az MCG->C1 regiszter "Internal Reference Clock Enable" (IRCLKEN) bitjét  '1'-be állítjuk.
Az alábbi programban az utóbbi megoldást mutatjuk be (lásd az MCG->C1 |= 2; sort!).

7. lista: Program5_7/main.c listája
/* Program5_7
*
* Kék LED villogtatása TPM0 időzítéssel, 32 kHz-es órajellel.
*
* Ebben a programban a 32.768 kHz-es belső oszcillátor jelét
* használjuk fel, 32-szeres előosztóval, tehát 1024 inkrementálás
* jelent 1 s időzítést, így a 16 bites számlálóval több másodperces késleltetés
* is könnyen realizálható. A programban szereplő 5 * 1024 = 5120 inkrementálással
* 5 s idő telik el a kék LED (PTD1) két állapotváltása közben.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*/

#include <MKL25Z4.H>

int main (void) {
MCG->C1 |= 2; // Enables IRCLK for use as MCGIRCLK
SIM->SCGC5 |= 0x1000; // A D port engedélyezése
PORTD->PCR[1] = 0x100; // PTD1 pin legyen GPIO módban!
PTD->PDDR |= 0x02; // PTD1 legyen kimenet!

SIM->SCGC6 |= 0x01000000; // A TPM0 időzítő engedélyezése
SIM->SOPT2 |= 0x03000000; // MCGIRCLK legyen a TPM órajel

TPM0->SC = 0; // Konfiguráláshoz letiltjuk az időzítőt
TPM0->SC = 0x05; // Előosztási arány 32 legyen
TPM0->MOD = 5120 - 1; // 0 - 5119-ig számlálunk (5 sec)
TPM0->SC |= 0x80; // TOF törlése
TPM0->SC |= 0x08; // Számláló engedélyezése (CMOD=01)

while (1) {
while(!(TPM0->SC&0x80)); // TOF-ra várunk...
TPM0->SC |= 0x80; // TOF törlése
PTD->PTOR = 0x02; // Kék LED (PTD1) átbillentése
}
}

A TPM modulokhoz kapcsolódó csatornák

Ahogy a TPM modulok bevezetőjében említettük, az  MKL25Z128VLK4 mikrovezérlő 3 db általános felhasználású időzítő/számlálójának mindegyikéhez több olyan "csatorna" (kiegészítő áramkör) is kapcsolódik, amelyek impulzus-szélesség modulációra (PWM), bemeneti jelrögzítésre (Input Capture), vagy digitális komparálásra (Output Compare használhatók. A TPM0 időzítő 6 db ilyen csatornával,  TPM1 és TPM2 pedig két-két csatornával rendelkezik. A TPM modulok és a csatornák kapcsolatát az alábbi ábra szemlélteti.


12. ábra: A TPM időzítő modulok és a hozzá kapcsolódó csatornák blokkvázlata

TPMx_CnV - a csatorna adatregisztere

Mindegyik csatornához eljut a TPM modulo számláló regiszterének pillanatnyi értéke, s mindegyik csatorna  rendelkezik egy-egy 16 bites írható/olvasható adatregiszterrel (a fenti ábrán a C0V, ..., CNV) amely az alábbi lehetőségeket nyújtja:
Az adatregiszterek TPMx_CnV megnevezésében az x a TPM modul sorszámára (x = 0, 1, 2), az n pedig az adott modulon belül a csatorna sorszámára utal (n = 0..5, ha x = 0, illetve n = 0 vagy 1, ha x = 1 vagy 2).

TPMx_CnSC - a csatorna állapotjelző és vezérlő regisztere

Azt, hogy az adott csatorna milyen módban működjön, a hozzá tartozó vezérlő regiszterben állíthatjuk be. A csatorna állapotjelző és vezérlő (Status and Control) regiszterek általános neve TPMx_CnSC, ahol x és n jelentése ugyanaz, mint az adatregisztereknél. A TPMx_CnSC regiszter bitkiosztása az alábbi ábrán látható. A 32 bites regiszternek csak az alsó 8 bitje van használatban.


13. ábra:  TPMx_CnSC regiszter bitkiosztása

Az egyes bitek szerepe:
A beállítható üzemmódok és a választható élek, illetve kimeneti szintek az alábbi táblázatból olvashatók ki.

4. táblázat: A TPM IC/OC/PWM csatornák beállítható üzemmódjai
CPWMS
MSB:MSA
ELSB:ELSA
Üzemmód
Konfiguráció
x
00
00
nincs
csatorna letiltva
x
01/10/11
00
komparálás szoftveres vezérléssel
nincs hardveres kimenetvezérlés
0
00
01
Bemeneti jelrögzítés
(Input Capture)


rögzítés felfutó élnél
10
rögzítés lefutó élnél
11
rögzítés mindkét élnél
01


01
kimenetvezérlés digitális összehasonlítással
(Output Compare)


kimenet átbillentése egyezéskor
10
kimenet törlése egyezéskor
11
kimenet '1'-be állítása egyezéskor
10
10
Él igazított jelű PWM

kimenet törlése egyezéskor
x1
kimenet '1'-be állítása egyezéskor

11
10
impulzus módú kimenetvezérlés digitális összehasonlítással (Output Compare)
negatív impulzus egyezéskor
x1
pozitív impulzus egyezéskor

1

10
10
Középre igazított jelű PWM

pozitív impulzus jelalak
x1
negatív impulzus jelalak

TPMx_STATUS - a TPMx modul állapotjelző regisztere

A TPMx_STATUS állapotjelző regiszter nem valamelyik csatornához tartozik, hanem a TPMx időzítő modulhoz,  s benne kigyűjtve megtalálható valamennyi csatorna CHF jelzőbitjének másolata és a TPMx számláló TOF túlcsordulásjelző bitje is. A TPMx_STATUS regiszter bitkiosztása az alábbi ábrán látható. A 32 bites regiszternek csak az alsó 8 bitje van használatban. A w1c bejegyzések arra utalnak, hogy mindegyik jelzőbit '1' beírásával törölhető. A működés szempontjából mindegy, hogy a jelzőbiteket ebben a gyűjtőregiszterben figyeljük/töröljük, vagy az egyes csatornaregiszterekben külön-külön. Több jelzőbit együttes figyelése nyilván ebben a gyűjtőregiszterben egyszerűbb, kényelmesebb.


14. ábra: A TPMx_STATUS regiszter bitkiosztása

A csatornák kivezetései

Mindegyik TPMx modul mindegyik csatornájához külön kivezetés rendelhető, s általában egynél több lehetőség közül választhatunk. Például a TPM0 modul CH0 csatornájához a PTA3, PTC1, PTD1 vagy a PTE24 kivezetések közül választhatunk. A választás a megfelelő PORTx_PCRn portvezérlő regiszter MUX bitcsoportjába írt számmal történik (Alt3 vagy Alt4 funkciót kell választanunk, ahogy az alábbi táblázat, illetve a FRDM-KL25Z Pinouts dokumentum jelzi). A hardverütközések kiszűrésével a FRDM-KL25Z kártya esetében az alábbi lehetőségek közül választhatunk:

5. táblázat: A TPMx_CHn időzítő csatornákhoz választható kivezetések (zárójelben az Alt funkció sorszáma)
Időzítő
Csatorna
Kivezetés
TPM0
CH0
PTC1 (4), PTD1 (4)
CH1
PTA4 (3), PTC2 (4), PTD1 (4)
CH2
PTA5 (3), PTC3 (4), PTD2 (4), PTE29 (3)
CH3
PTC4 (4), PTD3 (4), PTE30 (3)
CH4
PTC8 (3), PTD4 (4), PTE31 (3)
CH5
PTC9 (3), PTD5 (4)
TPM1
CH0
PTA12 (3), PTB0 (3), PTE20 (3)
CH1
PTA13 (3), PTB1 (3), PTE21 (3)
TPM2
CH0
PTA1 (3), PTB2 (3), PTB18 (3), PTE22 (3)
CH1
PTA2 (3), PTB3 (3), PTB19 (3), PTE23 (3)

Az Output Compare mód

Ha egy csatorna Output Compare (kimenetvezérlés digitális összehasonlítással) módban van (MSB:MSA = 01 vagy 11), amikor a hozzá tartozó TPMx_CNT számláló felfelé számlál, akkor a számlálás 0-tól indul, s a számláló pillanatnyi értéke minden léptetéskor összehasonlításra kerül a csatorna TPMx_CnV adatregiszterének tartalmával.   Ha egyezés van, akkor a csatorna CHF jelzőbitje '1'-be áll, s a csatornához rendelt kimenet állapota megváltozik az ELSb:ELSa-ban kiválasztott módon. A számlálás folytatódik a TPMx_MOD regiszterben megadott értékig, majd a számláló túlcsordul és újrakezdi a számlálást.

MSB:MSA = 01 módban az alábbi kimenetvezérlési lehetőségek közül választhatunk:
Megjegyzések:
MSB:MSA = 11 módban a kimenet állapota csak egy órajel idejére változik meg (órajelen itt a TPMx_CNT számláló bemenő jelét kell érteni). Ekkor az alábbi kimenetvezérlési lehetőségek közül választhatunk:

Program5_8: kimenet billegtetése Output Compare használatával

Ebben a példaprogramban a TPM0_CH1 csatornát használjuk Output Compare módban periodikus kimenő jel előállítására a PTD1 kimeneten (kék LED). Az MCGFLLCLK (20.97 MHz)  órajelet választjuk TPM órajelnek, melyet az előosztóval 128-szorosan leosztunk.  A TPM0_CH1 csatornát Output Compare Toggle (átbillentés) módba állítjuk  és a PTD1 kimenetet (kék LED) vezéreljük vele.  Minden alkalommal, amikor TPM0_CNT és TPM0_C1V megegyeznek,  a kimenet állapota átbillen, s a programban TPM0_C1V értékét  32766-vel megnöveljük, így a következő egyezés  32766 * 128 / 20 970 000 Hz = kb. 200 ms múlva lesz. A program tehát a kék LED-et villogtatja kb. 2.5 Hz frekvenciával (felváltva 200 ms "BE", majd 200 ms "KI" állapot váltakozik).

A konfigurálás lépései:
  1. Engedélyezzük a D portot (SIM->SCGC5: bit12 =  1)
  2. A PTD1 kivezetés TPM0CH1 (Alt4) funkcióját választjuk  (PORTD->PCR[1]: MUX = 4)
  3. Engedélyezzük a TPM0 modult (SIM->SCGC6: bit24 = 1)
  4. Konfiguráljuk a TPM modulok közös órajelét (SIM->SOPT2: TPMSRC = 01,  PLLFLLSEL = 0)
  5. Letiltjuk a TPM0 modul számlálóját a konfigurálás idejére (TPM0->SC = 0)
  6. Beállítjuk TPM0 előszámlálóját (TPM0_SC: PS = 111)
  7. Beállítjuk TPM0 modulo regiszterét (TPM0_MOD = 0xFFFF)
  8. Beállítjuk a TPM0_C1SC vezérlő regiszterben az OC toggle módot (MSB:MSA=01, ELSB:ELSA=01)
  9. Töröljük a TPM0_C1SC regiszterben a CHF jelzőt
  10. Beállítjuk az első késleltetési ciklust (TPM0_C1V = 32766)
  11. Elindítjuk a számlálót (TPM0_SC: CMOD = 01) 
A végtelen ciklusban ismételt lépések:
  1. Várunk a TPM0 CH1 csatorna CHF jelzőjére (TPM0_C1SC: CHF = 1 eseményre várunk)
  2. Töröljük TPM0 CH1 csatorna CHF jelzőjét (TPM0_C1SC: a CHF = 1 beírás töröl)
  3. Új időpont beállítása (TPM0_C1V értékét növeljük meg 32766-tal)
Megjegyzés: Nem számít, ha a TPM0_C1V regiszter a megnöveléskor túlcsordul, hiszen ugyanúgy túlcsordul a TPM0_CNT számláló is (mindkettő 16 bites).

8. lista: Program5_8/main.c listája
/* Program5_8
*
* Ez a program a TPM0_CH1 OC csatornát használja
* periodikus kimenő jel előállítására. Az MCGFLLCLK (20.97 MHz)
* órajelet választjuk TPM órajelnek. Az előosztót 128-szoros leosztásra állítjuk.
* A TPM0_CH1 csatornát Output Compare Toggle (átbillentés) módba állítjuk
* és a PTD1 kimenetet (kék LED) vezéreljük vele.
* Minden alkalommal, amikor TPM0_CNT és TPM0_C1V megegyeznek,
* a kimenet állapota átbillen, s a programban TPM0_C1V értékét
* 32766-vel megnöveljük, így a következő egyezés
* 32766 * 128 / 20 970 000 Hz = kb. 200 ms múlva lesz.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*/

#include <MKL25Z4.H>

int main (void) {
SIM->SCGC5 |= 0x1000; // A D port engedélyezése
PORTD->PCR[1] = 0x400; // PTD1 legyen TPM0CH1 kimenet

SIM->SCGC6 |= 0x01000000; // A TPM0 időzítő engedélyezése
SIM->SOPT2 |= 0x01000000; // MCGFLLCLK legyen a TPM órajel

TPM0->SC = 0; // Konfiguráláshoz letiltjuk az időzítőt
TPM0->SC = 0x07; // Előosztási arány 128 legyen
TPM0->MOD = 0xFFFF; // Maximális modulo érték
TPM0->CNT = 0; // A számláló törlése
TPM0->CONTROLS[1].CnSC = 0x14; // OC Toggle mód
TPM0->CONTROLS[1].CnSC |= 0x80; // CHF jelző törlése
TPM0->CONTROLS[1].CnV = 32766; // 200 ms múlva legyen egyezés
TPM0->SC |= 0x08; // TPM0 számláló indítása

while (1) {
while(!(TPM0->CONTROLS[1].CnSC & 0x80)) { } // CHF jelzőre várunk
TPM0->CONTROLS[1].CnSC |= 0x80; // CHF jelző törlése
TPM0->CONTROLS[1].CnV += 32766; // 200 ms múlva újabb egyezés
}
}

Mintaprogram: OC_flash

A Kimenet átbillentése (toggle) módnál valamivel bonyolultabb a Kimenet törlése egyezéskor (clear output on match), illetve a Kimenet beállítása egyezéskor (set output on match) üzemmódok használata és működése. Ezekkel lényegében szabályozott szélességű impulzusokat tudunk előállítani. Amikor ezen az üzemmódok valamelyikét beállítjuk, a kimenet a választott állapot ellenkezőjére áll be (Kimenet törlése választásakor '1'-be, Kimenet beállítása választásakor pedig 0-ba), majd a számláló és az adatregiszterben előírt érték egyezésekor ellenkező állapotba vált.

Ha ismételni akarjuk az impulzust, akkor először le kell tiltanunk az Output Compare módot, meg kell várni ennek érvényesülését (TOF flag vagy számláló változásának figyelésével), majd újra kell konfigurálnunk az OC módok valamelyikét.

Az alábbi programban a TPM0 modul CH1 csatornáját használjuk kb. 100 ms szélességű impulzusok keltésére Output Compare módban,  s a PTD1 kivezetést (kék LED) villogtatjuk vele. Ahhoz, hogy az  impulzus elegendően hosszú (100 ms) lehessen, alacsony  frekvenciájú órajelet, a 32.768 kHz-es belső oszcillátor jelét  használjuk fel TPM órajelként. A következő villantáshoz megvárjuk a szabadonfutó módban számláló TPM0_CNT túlcsordulását, így a villantások között 2 s telik el. A közös anódú RGB LED vezérlése negatív logikájú jelekkel történik, ezért a Kimenet beállítása módot kell használnunk.

A program végtelen ciklusában két esemény bekövetkeztére várunk:

9. lista: OC_flash/main.c listája

/* OC_flash
*
* Kék LED villantás TPM0_CH0 OC időzítéssel, 32 kHz-es órajellel.
*
* Ebben a programban a TPM0 modul CH1 csatornáját használjuk
* adott szélességű impulzusok keltésére Output Compare módban,
* s a PTD1 kivezetést (kék LED) vezéreljük vele. Ahhoz, hogy az
* impulzus elegendően hosszú (100 ms) lehessen, alacsony
* frekvenciájú órajelet, a 32.768 kHz-es belső oszcillátor jelét
* használjuk fel TPM órajelként.
*/

#include <MKL25Z4.H>

int main (void) {
MCG->C1 |= 2; // IRCLK lehessen MCGIRCLK forrás
SIM->SCGC5 |= 0x1000; // A D port engedélyezése
PORTD->PCR[1] = 0x400; // PTD1 pin legyen FTM0_CH1 módban!

SIM->SCGC6 |= 0x01000000; // A TPM0 időzítő engedélyezése
SIM->SOPT2 |= 0x03000000; // MCGIRCLK legyen a TPM órajel

TPM0->SC = 0; // Konfiguráláshoz letiltjuk az időzítőt
TPM0->MOD = 0xFFF0; // Szabadonfutó mód, periódus = 2 sec
TPM0->SC |= 0x80; // TOF törlése
TPM0->SC |= 0x08; // Számláló engedélyezése (CMOD=01)
TPM0->CONTROLS[1].CnSC = 0x1C; // OC, Set high
TPM0->CONTROLS[1].CnSC |= 0x80; // CHF jelző törlése
TPM0->CONTROLS[1].CnV = 3276; // 100 ms múlva legyen egyezés

while (1) {
if(TPM0->STATUS&0x02) { // CHF1-re vártunk...
TPM0->STATUS |= 0x02; // CHF1 törlése
TPM0->CONTROLS[1].CnSC = 0; // Csatorna letiltása
}
if(TPM0->SC&0x80) { // TOF-ra vártunk...
TPM0->SC |= 0x80; // TOF törlése
TPM0->CONTROLS[1].CnV = 3276; // 100 ms múlva legyen egyezés
TPM0->CONTROLS[1].CnSC = 0x1C; // OC, Set high
}
}
}

Input Capture - bemeneti jelrögzítés

Bemeneti jelrögzítés módban egy I/O kivezetésen keresztül vezetjük be azt a jelet, melynek átmenetei időpontját rögzíteni kívánjuk. Amikor egy esemény bekövetkezik, a TPMx_CNT számláló állása átmásolásra és eltárolásra kerül a csatorna TPMx_CnV regiszterében, míg a számláló tovább számlál. A számláló értékének rögzítésekor a csatorna CHF jelzőbitje is bebillen.

A bemeneti jelrögzítés konfiguráláshoz a csatorna TPMx_CnSC vezérlő regiszterében az MSB:MSA biteket 00-ra kell állítani, a TPMx_CHn bemenet ilyenkor él-érzékeny (edge sensitive) állapotban működik. Az ELSB és ELSA bitekkel állítjuk be, hogy felfutó, lefutó, vagy mindkét él bekövetkeztekor rögzítsük a számláló állapotát (bővebben lásd a 4. táblázatnál). A TPMx_CHn bemenet kiválasztása és konfigurálása ugyanúgy történik, mint az Output Compare módnál.

Program5_9: periódusidő meghatározása bemeneti jelrögzítéssel

Ebben a programban bemeneti  jelrögzítés módban használjuk a TPM2_CH0 csatornát, periodikus hullámforma periódusidejének mérésére. TPM órajel gyanánt az alapértelmezett (20.97 MHz)   MCGFLLCLK  órajelet használjuk, amelyet a TPM előszámlálója segítségével néggyel leosztunk.  Így TPM2 számlálója 20.97 MHz / 4 = 5 242 500 Hz frekvenciájú jelet számlál.

A TPM2 modul 0. csatornáját bemeneti jelrögzítésre konfiguráljuk  és a mikrovezérlő PTA1 kivezetését használjuk bemenetként, így  az UART0 bejövő jeleit vizsgálhatjuk vele, ha a bitrátát 9600  bps-re vagy kisebbre állítjuk. Amikor a PTA1 bemenetén felfutó él érzékelhető, a TPM2_CNT számláló pillanatnyi  értéke átmásolódik a TPM2_C0V regiszterbe és a CHF jelzőbit  '1'-be áll.
 
A program a CHF bit bebillenésére vár, majd meghatározza a jelenlegi  és a legutóbbi esemény között eltelt időt. Az eredmény 11..9 bitjeit  kijelezzük az RGB LED-en. A LED színe változik, ha a frekvencia  10 kHz alatt van (nagyobb frekvenciák esetén egy periódus alatt az  impulzusok száma nem éri el a 9. bitet).

Használjuk a Termite (vagy más, hasonló) terminál emulátor programot egy karaktersorozat kiküldésére, majd figyeljük az eredményt!

Megjegyzés: Bitenként változó jelsorozathoz küldjünk A55AA55A... karaktereket!

A bemeneti jelrögzítés konfigurálásának lépései:
  1. Engedélyezzük a A portot (SIM->SCGC5: bit9 =  1)
  2. A PTA1 kivezetés TPM2CH0 (Alt4) funkcióját választjuk  (PORTA->PCR[1]: MUX = 3)
  3. Engedélyezzük a TPM2 modult (SIM->SCGC6: bit26 = 1)
  4. Konfiguráljuk a TPM modulok közös órajelét (SIM->SOPT2: TPMSRC = 01,  PLLFLLSEL = 0)
  5. Letiltjuk a TPM2 modul számlálóját a konfigurálás idejére (TPM2->SC = 0)
  6. Beállítjuk TPM2 előszámlálóját (TPM2_SC: PS = 010)
  7. Beállítjuk TPM2 modulo regiszterét (TPM2_MOD = 0xFFFF)
  8. Beállítjuk a TPM2_C0SC vezérlő regiszterben az IC up módot (MSB:MSA=00, ELSB:ELSA=01)
  9. Töröljük a TPM2_C0SC regiszterben a CHF jelzőt
  10. Elindítjuk a számlálót (TPM2_SC: CMOD = 01) 
A végtelen ciklusban ismételt lépések:
  1. Várunk TPM2 CH0 csatorna CHF jelzőjére (TPM2_C0SC: CHF = 1 eseményre várunk)
  2. Töröljük TPM2 CH0 csatorna CHF jelzőjét (TPM2_C0SC: a CHF = 1 beírás töröl)
  3. A jelenlegi és az előző eseménynél eltárolt TPM2_C0V értékek különbségét képezzük, s az RGB LED-en megjelenítjük annak 11..9 bitjeit.
10. lista: Program5_9/main.c listája
/* Program5_9
*
* Ez a program a TPM2_CH0 csatornát használja bemeneti
* jelrögzítés módban periodikus hullámforma periódusidejének mérésére.
*
* A program a CHF bit bebillenésére vár, majd meghatározza a jelenlegi
* és a legutóbbi esemény között eltelt időt. Az eredmény 11..9 bitjeit
* kijelezzük az RGB LED-en. A LED színe változik, ha a frekvencia
* 10 kHz alatt van (nagyobb frekvenciák esetén egy periódus alatt az
* impulzusok száma nem éri el a 9. bitet).
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*/

#include <MKL25Z4.H>

int main (void) {
unsigned short then = 0;
unsigned short now = 0;
unsigned short diff;

//--- A GPIO kivezetések konfigurálása az RGB LED-hez ----
SIM->SCGC5 |= 0x400; // B port engedélyezése
SIM->SCGC5 |= 0x1000; // D port engedélyezése
PORTB->PCR[18] = 0x100; // PTB18 GPIO módba állítása
PTB->PDDR |= 0x40000; // PTB18 kimenet legyen
PORTB->PCR[19] = 0x100; // PTB19 GPIO módba állítása
PTB->PDDR |= 0x80000; // PTB19 kimenet legyen
PORTD->PCR[1] = 0x100; // PTD1 GPIO módba
PTD->PDDR |= 0x02; // PTD1 kimenet legyen

//--- Timer konfigurálás -----------------------------------
SIM->SCGC5 |= 0x0200; // A port engedélyezés
PORTA->PCR[1] = 0x0300; // PTA1 TPM2CH0 módba állítása

SIM->SCGC6 |= 0x04000000; // TPM2 engedélyezés
SIM->SOPT2 |= 0x01000000; // MCGFLLCLK legyen a TPM órajel

TPM2->SC = 0; // Időzítő letiltása konfiguráláshoz
TPM2->SC = 0x02; // Előszámláló: /4
TPM2->MOD = 0xFFFF; // Szabadonfutó mód
TPM2->CONTROLS[0].CnSC = 0x04; // IC mód, felfutó élre
TPM2->SC |= 0x08; // Számláló engedélyezés

while (1) {
while(!(TPM2->CONTROLS[0].CnSC & 0x80)); // CHF jelzőre várunk
TPM2->CONTROLS[0].CnSC |= 0x80; // CHF törlése
now = TPM2->CONTROLS[0].CnV; // A rögzített érték kiolvasása
diff = now - then; // Az időtartam számítása
then = now; // Az új érték lesz a vonatkoztatási pont

//--- LED-e beállítása az időtartam 11..9 bitjei szerint
diff = diff >> 9;
if (diff & 1) // Piros LED beállítása bit9 szerint
PTB->PCOR = 0x40000;// LED = be
else
PTB->PSOR = 0x40000;// LED = ki
if (diff & 2) // Zöld LED beállítása bit10 szerint
PTB->PCOR = 0x80000;
else
PTB->PSOR = 0x80000;
if (diff & 4) // Kék LED beállítása bit11 szerint
PTD->PCOR = 0x02;
else
PTD->PSOR = 0x02;
}
}

Eseményszámlálás

A TPMx modul számlálóként működik, ha a TPMx_SC vezérlő regiszter CMOD bitcsoportjában (bináris) 10-et írunk. Ebben az üzemmódban a számláló a kívülről érkező jel felfutó éleire számol. A bejövő jelet a mikrovezérlő a TPM órajelhez szinkronizálja, ezért a külső jelek számlálása esetén is konfigurálnunk kell a közös TPM órajelet.

Ahogy a 7. ábrán is látható, a szinkronizált jel a TPMx modul előosztójára kerül, s a továbbiakban a számlálás ugyanúgy történik, mint a belső órajelek esetében. Amikor a számláló eléri a modulo regiszterben beállított felső határt, a TPMx_SC regiszterben bebillen a TOF jelzőbit, s a számlálás nulla értékről folytatódik.

Bemenetválasztás

Külső események számlálásához az MKL25Z128VLK4 mikrovezérlő három TPM moduljához egyidejűleg legfeljebb két bemenet választható ki: egy TPM_CLKIN0 és egy TPM_CLKIN1 bemenet.  Elvileg nyolc I/O kivezetés közül választhatunk, de a FRDM kártyán hardverütközések miatt csak az alábbi táblázatban vastagon szedett négy bemenet használható erre a célra. Konfiguráláskor engedélyezni kell a megfelelő portot (pl. C port), s a kivezetéshez tartozó portvezérlő regiszterben az Alt4 módot kell beállítani a MUX bitcsoportban.

6. táblázat: az MKL25Z128VLK4 mikrovezérlő eseményszámláláshoz használható bemenetei
TPM_CLKIN0
TPM_CLKIN1
PTA18
PTA19
PTB16
PTB17
PTC12
PTC13
PTE29
PTE30
A bemenetválasztás következő lépése az, hogy a rendszer-integrációs modul SIM_SOPT4 regiszterében be kell állítani, hogy a használni kívánt TPMx modul melyik bemenetet használja. A SIM_SOPT4 regiszter 26. bitje
TPM2-re, a 25. bit TPM1-re, a 24. bit pedig TPM0-ra vonatkozik. Ha a bit  0, akkor a TPM_CLKIN0 bemenetet rendeljük az adott TPMx modulhoz. Ha pedig a bit 1, akkor a TPM_CLKIN1 bemenetet használjuk.



15. ábra: A SIM_SOPT4 regiszter bitkiosztása


Megjegyzés: A FRDM-KL25Z kártya mikrovezérlője 3,3 V-os tápfeszültséggel működik, ezért a bemenetekre 3,6 V-nál nagyobb feszültséget nem szabad kapcsolni, mert a mikrovezérlő károsodhat!

Program5_10: Külső események számlálása

A programban külső eseményeket számlálunk a PTC12 kivezetés, mint TPM_CLKIN0 bemenet használatával. TPM órajel gyanánt az alapértelmezett (20.97 MHz)   MCGFLLCLK  órajelet használjuk. A TPM0 modul külső órajel bemenetét a mikrovezérlő PTC12 kivezetéséhez rendeljük, amelyet TPM_CLKIN0 módba konfigurálunk.
A számlálót rendszeresen kiolvassuk, s a kiolvasott pillanatnyi értékének legalsó három bitjét kijelezzük az RGB LED-en.

Megjegyzés: Ha összekötjük a FRDM-KL25Z kártya csatlakozóin a PTA1 és PTC12 pontokat, akkor a számítógépről küldött karakterek (UART0 RX) felfutó éleit számoltathatjuk.

11. lista: Program5_10/main.c listája
/* Program5_10
*
* Külső események számlálása a PTC12 bemenet használatával.
* Az MCGFLLCLK (20.97 MHz) órajelet használjuk a TPM modulok
* órajelének, s a TPM0 modul számlálójával a PTC12 (TPM_CLKIN0)
* A TPM0 modult a PTC12 (TPM_CLKIN0) bemenethez rendeljük, s a
* bejövő jel felfutó éleit számláljuk. A számláló kiolvasott
* pillanatnyi értékének legalsó három bitjét kijelezzük az RGB LED-en.
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*/

#include <MKL25Z4.H>

int main (void) {
unsigned short count;
//--- GPIO kimenetek inicializálása az RGB LED-hez -----
SIM->SCGC5 |= 0x400; // B port engedélyezése
SIM->SCGC5 |= 0x1000; // D port engedélyezése
PORTB->PCR[18] = 0x100; // PTB18 GPIO módba állítása
PTB->PDDR |= 0x40000; // PTB18 kimenet legyen
PORTB->PCR[19] = 0x100; // PTB19 GPIO módba állítása
PTB->PDDR |= 0x80000; // PTB19 kimenet legyen
PORTD->PCR[1] = 0x100; // PTD1 GPIO módba
PTD->PDDR |= 0x02; // PTD1 kimenet legyen

//--- TPM0 és PCT12 inicializálása ---------------------
SIM->SCGC5 |= 0x0800; // C port engedélyezése
PORTC->PCR[12] = 0x400; // PTC12 legyen TPM_CLKIN0 bemenet

SIM->SOPT4 &= ~0x01000000; // TPM_CLKIN0 bemenetet használjuk
SIM->SCGC6 |= 0x01000000; // TPM0 engedélyezése
SIM->SOPT2 |= 0x01000000; // MCGFLLCLK legyen a TPM órajel
TPM0->SC = 0; // Időzítő letiltása konfiguráláshoz
TPM0->SC = 0x80; // Előszámláló /1 és TOF törlés
TPM0->MOD = 0xFFFF; // Szabadonfutó mód
TPM0->CNT = 0; // Számláló törlése
TPM0->SC |= 0x10; // Számlálás indítása külső órajellel

while (1) {
count = TPM0->CNT; // Számláló kiolvasása

//--- LED-ek beállítása a számláló legalsó bitjei szerint
if (count & 1) // Piros LED beállítása bit0 szerint
PTB->PCOR = 0x40000; // LED = be
else
PTB->PSOR = 0x40000; // LED = ki

if (count & 2) // Zöld LED beállítása bit1 szerint
PTB->PCOR = 0x80000;
else
PTB->PSOR = 0x80000;

if (count & 4) // Kék LED beállítása bit2 szerint
PTD->PCOR = 0x02;
else
PTD->PSOR = 0x02;
}
}

Felhasznált anyagok és ajánlott olvasmányok: