A CPU órajelének beállítása 

Az előző projektben eléggé el nem ítélhető módon nem foglalkoztunk a CPU órajelének beállításával, a késleltetést is csak találomra, for ciklusok szervezésével állítottuk be, pedig a projektbe becsatolt függvénykönyvtárak sokkal "professzionálisabb" lehetőségeket kínálnak! Írjuk át most a LED villogtató programot úgy, hogy a CPU ismert frekvenciájú (pl. 48 MHz) órajellel működjön, a késleltetéshez pedig a Cortex-M0 mikrovezérlők beépített Systick időzítőjét használjuk!

A projekt létrehozása az előző oldalon bemutatott módon történhet, a felkínált könyvtári modulok közül  most is csak a GPIO-t kell kézzel kiválasztanunk, a többi szükséges modul automatikusan kijelölésre kerül. A main.c állományba az alábbi listán látható forráskódot másoljuk be!

1. lista: A 02_Blinky projekt main.c állományának listája
// 02_Blinky: Blinks the red LED connected to Pin 10 of Port A
// pin should be in LOW state to drive the LED
//
// This is a slightly modified version of the Smpl_GPIO_LED.c example program
// written by Richard Kuo, National Taiwan Ocean University
// Link: http://sourceforge.net/projects/numicronulbnuc1/
//

#include "NUC1xx.h"
#include "Driver\DrvGPIO.h"
#include "Driver\DrvSYS.h"

int main (void)
{
UNLOCKREG(); // unlock register for programming
DrvSYS_Open(48000000); // set System Clock to run at 48MHz
LOCKREG(); // lock register from programming
DrvGPIO_Open(E_GPA, 10, E_IO_OUTPUT); // GPA10 pin set to output mode
DrvGPIO_SetBit(E_GPA, 10); // GPA10 pin output Hi to turn off LED

while (1) // forever loop to keep flashing LED
{
DrvGPIO_ClrBit(E_GPA, 10); // output Low to turn on LED
DrvSYS_Delay(250000); // 250 ms delay
DrvGPIO_SetBit(E_GPA, 10); // output Hi to turn off LED
DrvSYS_Delay(250000); // 250 ms delay
}
}
A CPU órajelének frekvenciáját legegyszerűbben a SYS periféria kezelő programmodul DrvSYS_Open() függvényével állíthatjuk be. Paraméterként a kívánt órajel frekvenciát kell megadni, Hz-ben. A gyári könyvtárak 25 - 50 MHz közötti értékek megadását engedélyezik. A fenti példában mi 48 MHz-et írtunk elő, ez a kártyán található 12 MHz-es kvarc jeléből PLL (fáziscsatolt hurok) segítségével könnyen előállítható. A hardver korlátozott lehetőségei miatt (csak egész értékű szorzókat és osztókat használhatunk) azonban nem állítható elő tetszőleges órajel. A ténylegesen beállított órajel frekvenciáját a DrvSYS_GetPLLClockFreq( ) függvény hívásával ellenőrizhetjük.

Ügyeljünk arra, hogy a rendszer működését alapvetően befolyásoló védett regiszterek tartalmának felülírása csak akkor lehetséges, ha előtte feloldottuk a regiszterek írásvédelmét. A feloldást kétféle módon is végezhetjük: vagy a NUC1xx.h állományban definiált UNLOCKREG()  nevű makró, vagy pedig a DRV_SYS.c állományban definiált DrvSYS_UnlockProtectedReg() függvény meghívásával. Végeredményben ugyanazt csinálja mind a kettő: az 0x59, 0x16, 0x88 bűvös számsort tölti be a 0x5000_0100 címen található REGWRPROT regiszterbe. A regiszterek beállítása után az írásvédelmet úgy állíthatjuk vissza, hogy nullát írunk a REGWRPROT regiszterbe a LOCKREG() makró, vagy DrvSYS_LockProtectedReg() függvény meghívásával.

Az általános célú ki/bemenet (GPIO) beállítása és ki-be kapcsolgatása ugyanúgy történik, ahogy az első példában is mutattuk:
 DrvGPIO_Open(E_GPA, 10, E_IO_OUTPUT); // GPA10 pin set to output mode
A függvény első paramétere a portot (esetünkben az A portról van szó), a második paraméter a port bitjét (0-15), a harmadik paraméter pedig az adott bithez tartozó kivezetés üzemmódját és az adatáramlás irányát (pl. digitális kimenet) adja meg. Bővebben lásd: NUC100 Series Driver Reference Guide v1.05 5. fejezetében!

A késleltetést itt a Cortex-M0 mikrovezérlők Systick számlálóját használó DrvSYS_Delay() függvénnyel oldottuk meg, amelynek mikroszekundumban kell megadni a kívánt várakozási időt. Arra vigyáznunk kell, hogy 335000-nél nagyobb értéket ne adjunk meg!

Magyarázat: a Systick periféria egy 24 bites számláló, amely a kiválasztott órajelet (alaphelyzetben a CPU max. 50 MHz-es órajelét) számlálja. A számlálás a beírt értéktől kezdve visszafelé történik, s amikor a számláló eléri a nullát, bebillen a Systick nullára futás jelzőbitje (a 0xE000_E010 címen található SysTick->CTRL regiszter  COUNTFLAG bitje). Ez a bit akkor billen be, amikor a visszafelé számláló 1-ről nullára billen. 

A programunkban nem engedélyeztük a programmegszakítást, ezért a COUNTFLAG jelzőbitet a DrvSYS_Delay() függvény lekérdezéses (polling) módban vizsgálja, így dönti el azt, hogy letelt-e már a kívánt késleltetési idő. Ez nem túl hatékony módszer, hiszen a blokkoló típusú várakozás nem teszi lehetővé sem azt, hogy a CPU közben más, hasznos tevékenységgel töltse az idejét, sem azt, hogy közben energiatakarékos módban szundikáljon.Tanulságos lesz megnézni a DrvSYS.c állományban a függvény forráskódját!

2. lista: A DrvSYS_Delay() függvény listája
//*****************************************************************************
//
//! \brief Use the SysTick to generate the delay time and the UNIT is in us. The
//! SysTick clock source is from HCLK.
//!
//! \param us Delay time. The Max value is 335000
//!
//! \return None
//
//*****************************************************************************
void DrvSYS_Delay(uint32_t us)
{
SysTick->LOAD = us * CyclesPerUs;
SysTick->VAL = (0x00);
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;

/* Waiting for down-count to zero */
while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk == 0);
}

A SysTick_CTRL_CLKSOURCE_Msk és SysTick_CTRL_ENABLE_Msk makrókat a core_cm0.h állomány definiálja (lényegében a Systick->CTRL regiszter BIT2 és BIT0 bitjeinek beállításhoz használjuk ezeket). Ugyanitt van definiálva a SysTick_CTRL_COUNTFLAG_Msk makró is, amely a SysTick->CTRL regiszter COUNTFLAG bitjét választja ki (BIT16). Ez a bit akkor vált 1-be, amikor a beállított késleltetési idő letelt.