Fakulta elektrotechniky a informatiky Univerzita Pardubice
Tvorba a programování mikrokontroléru s procesorem NIOS II Semestrální práce z předmětu Programovatelné logické obvody
Jméno: Jiří Paar Datum: 6. 2. 2010
Zadání Navrhněte a sestavte vlastní mikrokontrolér s procesorem NIOS II v obvodu FPGA řady Cyclone II. Funkci vyzkoušejte na vývojové desce DE2 od společnosti Altera.
Popis navrhovaného systému Výsledkem této práce jsou digitální hodiny včetně zobrazování data. Systém zároveň umožňuje nastavit všechny hodnoty. Celý systém se ovládá pouze pomocí čtveřice tlačítek. Dvěma tlačítky se určuje směr nastavování (+ nebo –), ostatní dvě tlačítka slouží pro výběr fáze nastavování. Fáze nastavování se provádí v následujícím pořadí:
Nastavení minut
Nastavení sekund
Nastavení roku
Nastavení měsíce
Nastavení hodin
Nastavení den v týdnu
Nastavení roku
Nastavení den v měsíci
Postup tlačítka „fáze plus“
Nastavení sekund
Nastavení měsíce
Nastavení den v měsíci
Nastavení minut
Nastavení den v týdnu
Nastavení hodin
Postup tlačítka „fáze minus“
V režimu nastavování vždy právě nastavovaná hodnota bliká s periodou, která je rovna 0,4s. Zároveň je možné dlouhým stiskem tlačítka spustit automatické opakování nastavení s periodou 0,25s u tlačítek pro změnu hodnoty a s periodou 0,5s pro tlačítka výběru fáze nastavování. Všechny údaje jsou zobrazovány na LCD displeji, který je součástí vývojového kitu. Pro indikaci stavu mikrokontroléru slouží i několik LED diod. Dioda LEDG8 bliká s frekvencí 1Hz v závislosti na změně času, bliká tedy jen, když je měřen čas, ne v režimu nastavování. Režim nastavování je signalizován svitem LED diody LEDR0. V případě chyby inicializace LCD displeje se rozsvítí LED dioda LEDR1, v tomto případě mikrokontrolér nevykonává již žádné jiné operace. Samotný mikrokontrolér je navržen tak, aby se při stisku libovolného tlačítka rozsvítila patřičná LED dioda. Jedná se o hardwarovou konfiguraci. Reset mikrokontroléru je vyveden na přepínač SW17.
Popis rozmístění ovládacích komponent:
Napájení
LCD displej Fáze nastavování minus
Změna času
RESET
Chyba přístupu Stav systému k LCD
Tlačítko minus (hodnoty)
Fáze nastavování plus Tlačítko plus (hodnoty)
Návrh mikrokontroléru s procesorem NIOS II Pro návrh byl použit vývojový nástroj Quartus II 9.1 Web Edition a zejména jeho součást SOPC Builder (Tools – SOPC Builder).
Tvorba mikrokontroléru v SOPC Builderu Po spuštění SOPC Builderu je potřeba zvolit název mikrokontroléru, v tomto případě byl pojmenován CPU. Také je potřeba zvolit jazyk (Verilog), ve kterém bude mikrokontrolér vytvořen. Aby bylo možné využít připravené periferie na vývojovém kitu DE2, je potřeba stáhnout a nainstalovat rozšiřující část pro SOPC Builder. Tento program je možné stáhnout z této stránky (soubor University Program Design Suite). Po nainstalování a importování do SOPC Builderu (Tools – Options… – IP Search Path) se zpřístupní některé periferie vývojového kitu DE2. Nové periferie budou importovány do nové záložky Univerzity Program jako nová knihovna. Pro samotnou tvorbu mikrokontroléru stačí vložit potřebné periferie včetně procesoru NIOS II. Celý návrh může vypadat např. takto:
Z nově naimportované knihovny jsou použity periferie SRAM/SSRAM Controller (sram_memory) a Charakter LCD 16x2 (lcd). Všechny vložené periferie jsou vhodně pojmenovány pro pozdější lepší přehlednost psaného programu. Typ procesoru byl nastaven na Nios II/s. Pro správnou funkci mikrokontroléru a hlavně procesoru NIOS II je potřeba přiřadit procesoru správný název paměti RAM pro položky Reset Vector a Exceprion Vektor. Ostatní nastavení procesoru jsou ponechány jako výchozí. Periferie pro řízení LCD displeje umožňuje jediné nastavení. Tímto nastavením je volba zobrazení kurzoru. Tato volba je nastavena na None. Port pio_ledr má pouze dva výstupy a slouží pro indikaci stavu zařízení. Port pio_ledg má pouze jediný výstup indikující změnu času. Port pio_key je pouze vstupní se čtyřmi vstupy. K tomuto portu jsou připojena tlačítka. Časovač timer je nastaven na trvalou hodnotu 10ms pro časování délky stisku tlačítek, blikání nastavovaných hodnot apod. Jeho velikost je nastavena na 32bit, je nastaven do režimu Fullfeatured, je tedy možné nastavovat periodu, spouštět a zastavovat časovač.
Po takto navrženém mikrokontroléru může být samotný mikrokontrolér vygenerován pomocí tlačítka Generate. Po úspěšném vygenerování je již možné SOPC Builder uzavřít.
Přiřazení vstupů a výstupů mikrokontroléru Po vygenerování je potřeba procesoru správně přiřadit vstupy a výstupy výsledného mikrokontroléru. To se provede tak, že se otevře soubor CPU.v a prostřednictvím File – Create/Update – Create VHDL Component Declaration Files for Current File se vygeneruje komponenta pro jazyk VHDL. Nyní je potřeba vytvořit nový soubor, který bude mít stejný název jako Top Level Entity. V tomto souboru se zvolí názvy vstupů a výstupů, které jsou totožné s názvy na vývojovém kitu DE2 (tyto názvy je nutné importovat prostřednictvím nabídky Assgnments – Import Assgnments…). Dále je potřeba do souboru vložit nově vytvořenou komponentu pro procesor a v sekci architekture je nutné procesoru správně přiřadit vstupy a výstupy. Také musí být správně přiřazena funkce pro LED diody LEDG, jak bylo popsáno v popisu systému. Celý výpis sekce architecture vypadá následovně: architecture SP_PLD_A of SP_PLD is COMPONENT CPU PORT
( clk_0 reset_n LCD_BLON_from_the_lcd LCD_DATA_to_and_from_the_lcd LCD_EN_from_the_lcd LCD_ON_from_the_lcd LCD_RS_from_the_lcd LCD_RW_from_the_lcd in_port_to_the_pio_key out_port_from_the_pio_ledg out_port_from_the_pio_ledr SRAM_ADDR_from_the_sram_memory SRAM_CE_N_from_the_sram_memory SRAM_DQ_to_and_from_the_sram_memory SRAM_LB_N_from_the_sram_memory SRAM_OE_N_from_the_sram_memory SRAM_UB_N_from_the_sram_memory SRAM_WE_N_from_the_sram_memory
: : : : : : : : : : : : : : : : : :
IN STD_LOGIC; IN STD_LOGIC; OUT STD_LOGIC; INOUT STD_LOGIC_VECTOR(7 DOWNTO 0); OUT STD_LOGIC; OUT STD_LOGIC; OUT STD_LOGIC; OUT STD_LOGIC; IN STD_LOGIC_VECTOR(3 DOWNTO 0); OUT STD_LOGIC; OUT STD_LOGIC_VECTOR(1 DOWNTO 0); OUT STD_LOGIC_VECTOR(17 DOWNTO 0); OUT STD_LOGIC; INOUT STD_LOGIC_VECTOR(15 DOWNTO 0); OUT STD_LOGIC; OUT STD_LOGIC; OUT STD_LOGIC; OUT STD_LOGIC );
END COMPONENT; signal negace_KEY : std_logic_vector(3 downto 0); begin cpuComp : CPU port map ( CLOCK_50, not(SW(17)), LCD_BLON, LCD_DATA, LCD_EN, LCD_ON, LCD_RS, LCD_RW, negace_KEY, LEDG(8), LEDR(1 downto 0), SRAM_ADDR, SRAM_CE_N, SRAM_DQ, SRAM_LB_N, SRAM_OE_N, SRAM_UB_N, SRAM_WE_N ); negace_KEY(0) negace_KEY(1) negace_KEY(2) negace_KEY(3)
<= <= <= <=
not(KEY(0)); not(KEY(1)); not(KEY(2)); not(KEY(3));
LEDG(0) LEDG(2) LEDG(4) LEDG(6)
<= <= <= <=
not(KEY(0)); not(KEY(1)); not(KEY(2)); not(KEY(3));
LEDR(17) <= SW(17); end;
Takto přiřazené vstupy a výstupy mikroprocesoru odpovídají tomuto blokovému schématu: CLOCK_50
clk_0
+
reset_n
pio_ledg 1
SW17 pio_ledr
LEDR17
2
LEDG0 LEDG2 LEDG4 LEDG6
NIOS II
KEY0 lcd 13 KEY1 pio_key KEY2
4 negace_key
sram_memory 39
KEY3
Zdrojový kód navrhovaného systému Vzhledem k faktu, že firma Altera poskytuje zdrojové soubory pro LCD displej psané v jazyce C, bude i celý projekt napsán v tomto jazyce. K tomu je potřeba si stáhnout a nainstalovat Nios II Embedded Design Suite z této stránky. Samotný projekt bude vytvořen v nově nainstalovaném programu Nios II 9.1 Software Build Tools for Eclipse. Po prvním spuštění je nutné si zvolit, ve které složce budou uloženy soubory s informacemi o novém projektu. Dále vytvoříme nový projekt prostřednictvím File – New – Nios II Application and BSP from Template. Do položky SOPC Information File name vložíme soubor vygenerovaný prostřednictvím SOPC Builderu. A nový projekt pojmenujeme. Po výše popsaných úkonech se ve skutečnosti vytvoří dva projekty, jeden je pojmenován stejně, jak jsme si ho pojmenovali, druhý se jmenuje podobně, jen má na konci připsáno _bsp. A právě v tomto projektu se vytvoří velice důležitý soubor system.h. Tento soubor obsahuje všechny potřebné informace o navrženém mikrokontroléru, např. bázové adresy PIO portů (PIO_LEDR_BASE, PIO_LEDG_BASE apod.), šířce jednotlivých portů, jejich nastaveném směru, informace o IRQ časovače atd. Bázové adresy mají stejné jméno, jaké jsme nastavili v SOPC
Builderu, pouze jsou doplněny o _BASE. Tento soubor je potřeba zkopírovat do adresáře základního projektu. Výše popsané projekty budou uloženy ve složce projektu programu Quartus II ve složce software. Celá problematika navrhovaného systému je rozdělena do několika modulů, tím se zpřehlední celkový návrh. Následuje popis a shrnutí všech použitých modulů: Název modulu Názvy souborů Popis modulu buttons buttons.c, buttons.h Obsahuje funkce pro obsluhu tlačítek. Obsahuje funkce pro zobrazení na LCD show show.c, show.h displeji. Obsahuje funkce pro obsluhu časovače, timer timer.c, timer.h včetně přerušení. Obsahuje definice a deklarace všech variables variables.c, variables.h globálních proměnných. Obsahuje také funkci pro inicializaci proměnných. main.c main Obsahuje hlavní funkci main() programu.
Základní obsluha periferií Před samotným popisem jednotlivých modulů je potřeba se seznámit se základní obsluhou periferií. Přístup k portům Přístup k portům je zajištěn pomocí několika registrů. Ty umožňují zápis a čtení hodnoty z portu, určit směr portu (vstupní nebo výstupní), povolit přerušení pro jednotlivé vstupy, určit, na kterém vstupu došlo ke změně, nastavit nebo vynulovat jednotlivé výstupy. Podrobný popis ukazuje následující tabulka: Popis bitů Offset Název registru R/W (n-1) … 2 1 0 R Čtení aktuálního stavu vstupů a výstupů data 0 W Zápis nové hodnoty na port Směr portu. Bit = 0 → vstup; direction 1 R/W bit = 1 → výstup interruptmask 2 R/W Povolení přerušení. Bit = 1 → povoleno. edgecapture 3 R/W Určuje, na kterém vstupu došlo ke změně. outset 4 W Určuje, který bit se má nastavit. outclear 5 W Určuje, který bit se má vynulovat. Periferie má svoji základní (bázovou) adresu (PIO_BASE), kterou lze určit z SOPC Builderu. Registr data se nachází na adrese PIO_BASE, registr direction se nachází na adrese PIO_BASE + 1, atd. S výhodou lze využít hlavičkový soubor altera_avalon_pio_regs.h. Ten obsahuje řadu maker pro zacházení s PIO porty. Např. makro IOWR_ALTERA_AVALON_PIO_DATA(base, data), které zapíše data na port s bázovou adresou portu base, nebo
IORD_ALTERA_AVALON_PIO_DATA(base), které naopak přečte z portu s bázovou adresou base
aktuální hodnotu. Práce s časovačem Podobně jako v případě PIO portu se časovač ovládá pomocí řady registrů. Jejich přehled pro 32bit. časovač ukazuje následující tabulka: Popis bitů Offset Název R/W 15 … 4 3 2 1 0 status RUN TO 0 R/W control STOP START CONT ITO 1 R/W periodl 2 R/W Perioda časovače periodh 3 R/W snapl 4 R/W (viz. popis níže) snaph 5 R/W Stejně jako v případě PIO portů, tak i pro práci s časovačem je připraven hlavičkový soubor altera_avalon_timer_regs.h. Tento soubor opět obsahuje řadu maker, např. pro zápis do registru status (IOWR_ALTERA_AVALON_TIMER_STATUS) nebo pro zápis do registru control (IOWR_ALTERA_AVALON_TIMER_CONTROL) apod. Registr status obsahuje informace o stavu časovače. Je-li bit TO nastaven, došlo k přetečení časovače. Nastavení bitu RUN ukazuje, že je časovač spuštěn. K vynulování bitu TO dojde zápisem nuly do registru status, bit RUN nebude ovlivněn. Registr control řídí časovač. Nastavení bitu ITO povoluje přerušení od časovače. Je-li nastaven a dojde k nastavení bitu TO je vyvoláno přerušení. Bit CONT určuje má-li časovač být spuštěn i po přetečení časovače. Nastavení bitu START způsobí spuštění časovače a nastavení bitu STOP jeho zastavení. Registry periodl a periodh určují periodu časovače. Zápis do některého z těchto registrů nebo vynulováním časovače dojde k zápisu hodnot těchto registrů do vnitřního časovače. Registry snapl a snaph slouží pro zjištění stavu vnitřního časovače. Zápisem do tohoto registru dojde ke zkopírování hodnoty z vnitřního časovače do registrů snapl a snaph. Následným čtením lze tuto hodnotu vnitřního časovače zjistit. Obsluha přerušení Pro práci s přerušením opět existuje hlavičkový soubor sys/alt_irq.h. Tento soubor zpřístupňuje funkce pro registraci funkce, která bude volána při vyvolání přerušení dané periferie. Ve skutečnosti existují tyto funkce dvě podle toho, jakou sadu funkcí pro obsluhu přerušení procesor podporuje. To, která funkce v dané situaci je možná rozhoduje direktiva ALT_ENHANCED_INTERRUPT_API_PRESENT. Tato direktiva je vytvořená také ve vytvořeném souboru system.h. Navržený procesor umožňoval užší sadu funkcí pro obsluhu přerušení, proto bude popsána pouze funkce přístupná v tomto režimu.
Tato funkce je popsána následovně: int alt_ic_isr_register( alt_u32 ic_id, alt_u32 irq, alt_isr_func isr, void *isr_context. void *flags); ic_id je identifikátor přerušení, irq je číslo IRQ, isr je název funkce, která má být volána při
vzniku přerušení, isr_context umožňuje předat přerušení nějaký parametr a flags je parametr pro pozdější využití. Při použití této funkce musí mít hlavička funkce pro obsluhu přerušení tento tvar: void isr(void *context) Parametr context bude stejný s parametrem isr_context funkce alt_ic_isr_register.
Dále je potřeba povolit globální přerušení. To se provede velmi jednoduše, nastavením bitu PIE registru status procesoru. K tomu lze využít funkci alt_irq_enable_all s parametrem 1 (bit PIE je nultý bit registru status). Obsluha LCD displeje Pro přístup k registrům existuje klasický hlavičkový soubor altera_up_avalon_character_lcd_regs.h. Ale existují také soubory altera_up_avalon_character_lcd.h a altera_up_avalon_character_lcd.c, které poskytují funkce např. pro zobrazení textu apod. Všechny tyto soubory je nutné zkopírovat do adresáře projektu z adresáře, kam jsme na začátku nainstalovali University Program. Funkce alt_up_character_lcd_open_dev(const char *name) získává přístup k samotnému LCD displeji. Tato funkce vrací ukazatel strukturu typu alt_up_character_lcd_dev. Jako parametr se funkci předává název LCD zařízení. V našem případě lze využít direktivu LCD_NAME ze souboru system.h. Pokud získání přístupu selže funkce místo ukazatele na strukturu vrátí NULL. Funkce alt_up_character_lcd_init(alt_up_character_lcd_dev *lcd) vymaže displej a nastaví kurzor na počátek (první řádek, první sloupec). Jako parametr je potřeba odkaz na strukturu, kterou vrátila předešlá funkce (tento parametr se vyskytuje i u ostatních funkcí). Funkce int alt_up_character_lcd_set_cursor_pos(alt_up_character_lcd_dev *lcd, unsigned x_pos, unsigned y_pos) nastaví kurzor na dané souřadnice. Proměnná x_pos může nabývat hodnot z intervalu 〈0,15〉 a y_pos z intervalu 〈0,1〉. Funkce vrací 0 při úspěšném provedení funkce (pouze se testuje správné zadání parametrů). Funkce void alt_up_character_lcd_string(alt_up_character_lcd_string *lcd, const char *ptr) zapíše text ptr na aktuální pozici kurzoru. Dále je možné měnit nastavení zobrazení kurzoru, zapisovat jednotlivé bajty namísto celého textu apod. (tyto funkce nebyly využity, proto nejsou popsány).
Popis zdrojového kódu jednotlivých modulů Jak již bylo v úvodu popsáno, výsledný zdrojový kód se skládá z řady modulů. Každý modul zastřešuje určitou problematiku. Modul variables Tento modul má nejjednodušší úlohu ze všech. Zajišťuje pouze definici a deklaraci globálních proměnných. Obsahuje pouze jedinou funkci void init_variables(), která nastavuje výchozí hodnoty všech proměnných. Např. nastavuje datum a čas na So. 1. 1. 2000 0:00:00, dále nastaví režim nastavování a vybere nastavování sekund. V poslední řadě zkusí získat přístup k LCD displeji zavoláním funkce alt_up_character_lcd_open_dev. Modul timers Tento modul zajišťuje činnost časovače. Obsahuje funkci void init_timer_isr() pro inicializaci časovače a pro registraci funkce pro volání přerušení. Funkce nejprve nastaví bity ITO, CONT a START v registru control časovače. Tím se povolí přerušení, nastaví se, aby časovač běžel neustále dokola, a spustí se. Dále se otestuje direktiva ALT_ENHANCED_INTERRUPT_API_PRESENT a zavolá se funkce alt_ic_isr_register() s parametry pro registraci funkce timer_interrupt(). Na konec se povolí globální přerušením zápisem hodnoty 1 do registru control procesoru prostřednictvím funkce alt_irq_enable_all(). Samotná funkce, která je volána jako přerušení od časovače má tuto hlavičku void timer_interrupt(void *context). Funkce nejprve vynuluje bit TO registru status časovače. Následně se nastaví bit is10ms registru stavSystemu jako indikace, že došlo k přetečení 10ms časovače. Následně se otestuje, v jakém režimu se systém nachází. Je-li v režimu nastavování (REZIM_NASTAVENI) přičte se číslo 1 k čítači delayHide. Pokud tento čítač má hodnotu 40 (perioda změny je 0,4s), vynuluje se a změní se hodnota bitu hide registru stavSystemu na opačnou (tento bit v podstatě kopíruje blikající znak při nastavování, hide == 1, znak nesvítí), jako indikaci změny bitu hide se nastaví bit changeHide rovněž registru stavSystemu. Na konec se vynuluje port PIO_LEDG, to má za následek, že dioda indikující změnu času nebude v režimu nastavování svítit.
Nachází-li se systém v normálním režimu (REZIM_NORMAL), přičte se hodnota 1 k čítači delay1s. Má-li tento čítač hodnotu 100, vynuluje se, zavolá se funkce void timer_tick() pro zvýšení hodnoty času a nastaví se první bit portu PIO_LEDG (nastal nový čas). Má-li čítač delay1s hodnotu 50, vynuluje port PIO_LEDG, dioda LEDG8 bude tedy 0,5s svítit a 0,5s bude zhasnuta. O ošetření změny času a následně změny data se stará již zmíněná funkce timer_tick(). Tato funkce nastaví bit novyCas registru stavSystemu jako indikaci nového času. Přičte hodnotu 1 k sekundám. Je-li napočítáno 60 a víc sekund, vynulují se a přičte se 1 k minutám, jsou-li minuty rovny nebo větší než 60, vynulují se a přičte se 1 k hodinám, jsou-li hodiny větší nebo rovny 24, vynulují se. Tím je zajištěno klasické počítání hodin v 24 hodinovém režimu. Je-li dosaženo hodnoty hodin větší nebo rovny 24, nastaví se bit novyDatum registru stavSystemu (indikace nového data), přičte se 1 k registru dayOfWeek, který určuje den v týdnu. Je-li hodnota registru dayOfWeek rovna 7, vynuluje se (počítání sedmi dnů v týdnu). Poté se zkontroluje hodnota registru day (den v měsíci) je-li rovna maximálnímu dnu v měsíci (zavoláním funkce unsigned char GetMaxDayOfMonth()), pokud je, nastaví se den v měsíci na hodnotu 1 a přičte se hodnota 1 k registru month (měsíc), pokud není přičte se 1 k registru day. Pokud se zvýšila hodnota registru month zkontroluje se, jestli je dosaženo 13 měsíce, pokud ano, je nastaven na hodnotu 1 a je přičtena 1 k registru year (rok).
Poslední funkce kterou tento modul obsahuje je již zmíněná funkce GetMaxDayOfMonth(). Tato funkce vrací hodnotu maximálního dne v měsíci. Tato hodnota je zjišťována podle měsíce a roku. Je-li přestupný rok (celočíselné dělení roku čtyřmi je bezezbytku) a měsíc je roven dvěma, je vrácena hodnota 29. V ostatních případech je vrácena hodnota z konstantního pole bajtů maxDayOfMonth, které obsahuje hodnoty maximálních možných dní v měsících. Modul buttons Tento modul zajišťuje správnou funkci tlačítek včetně časování doby stisku tlačítka. Jsou zde vytvořeny makra pro zjištění stavu tlačítek (JeStisknuteFazePlus(), JeStisknuteFazeMinus(), JeStisknutePlus(), JeStisknuteMinus()). Nejdůležitější funkcí je funkce void osetreni_tlacitek().
Ve funkci se nejprve otestuje stisk postupně všech tlačítek. Je-li nějaké tlačítko stisknuto, je otestováno, jestli je detekován stisk poprvé nebo není stisknuto jiné tlačítko (nastavení bitu lib_tlacitko registru stavTlacitek) pokud není, jsou v registru stavTlacitek nastaveny patřičné bity, bit lib_tlacitko a bit pro indikaci krátkého stisku příslušného tlačítka. A nastaví se opakování tlačítka na 0.5s. Tedy při prvním zjištění stisku tlačítka se uloží informace o jeho stisku a o stisku jakéhokoli tlačítka. Tím je zajištěno, že při stisku ještě dalšího tlačítka, se nebude na tento další stisk reagovat. Pokud není zjištěn stisk žádného tlačítka, jsou otestovány bity krátkého stisku (tlačítko bylo drženo kratší dobu než nastavenou při prvním zjištění stisku). Pokud je některý z bitů nastaven zavolá se funkce pro dané tlačítko. Na konec se vynulují všechny příznaky pro tlačítka. Pokud je tlačítko drženo delší dobu je postupně odčítána 1 od registru delayTlacitko. Až se hodnota tohoto čítače bude rovnat nule, zkontrolují se příznaky krátkého nebo dlouhého stisku tlačítka. Pokud je některý z těchto bitů patřičného tlačítko nastaven, je nastavena hodnota pouze pro dlouhý stisk (na uvolnění tlačítka se již nebude reagovat) a zavolá se patřičná funkce pro dané tlačítko. A nastaví se hodnota registru delayTlacitko na novou hodnotu podle daného tlačítka. Tedy při dlouhém stisku se bude vždy po vynulování registru delayTlacitko
provádět akce daného tlačítka. Pozn. pro správné časování je potřeba tuto funkci volat každých 10ms. Dílčími funkcemi tohoto modulu jsou funkce pro akce patřičných tlačítek (void button_plus(), void button_minus(), void button_faze_plus(), void button_faze_minus()).
Funkce button_plus() je platná pouze v režimu nastavování. V závislosti na fázi nastavování (registr fazeNastaveni) přičítá hodnotu 1 k danému registru a kontroluje, jestli není hodnota tohoto registru již příliš vysoká, pokud ano je nastavena na nejmenší možnou. Pouze při změně měsíce je navíc kontrolováno, jestli den v měsíci není větší než maximální možný (např. aby únor neměl 30 dní apod.), pokud je, je den v měsíci nastaven právě na maximální možný.
Funkce button_minus() je velice podobná funkci button_plus(). Také je platná pouze v režimu nastavování a v závislosti na fázi nastavování mění hodnoty daných registrů. Ovšem rozdíl je, že je-li hodnota daného registru rovna nejmenší možné hodnotě, je nastavena na maximální možnou a pokud není rovna, je od hodnoty registru odečteno číslo 1. Stejně jako ve funkci button_plus() je i zde ošetřen maximální den v měsíci při změně měsíce.
Funkce button_faze_plus() má za úkol patřičným způsobem měnit režim systému a případně fázi nastavování. Nachází-li se systém v normálním režimu, je zvolen režim nastavování a fáze nastavování je nastavena na NASTAVENI_SEK (nastavování sekund). Je-li již systém v režimu nastavování, je v závislosti na aktuální fázi nastavování nastavena další fáze. Je-li ovšem fáze nastavování na hodnotě NASTAVENI_YEAR (nastavování roku), je režim nastaven na normální a je vynulován čítač delay1s, což má za následek, že se počítání 1s spustí od začátku. Na konci funkce je nastaven bit hide pro zhasnutí nově nastavované hodnoty (v normálním režimu tento bit nemá význam), je vynulován čítač delayHide (znak bude zhasnutý 0,4s) a zavolají se funkce time_show() a date_show() pro případné zobrazení zhasnuté hodnoty a v režimu nastavování pro zhasnutí nově nastavované hodnoty na LCD displeji.
Funkce button_faze_minus() je téměř totožná s funkcí button_faze_plus() ovšem mění hodnoty přesně v opačném pořadí. Tedy je-li systém v normálním režimu je nastaven režim nastavování a je nastavena fáze na NASTAVENI_YEAR. Fáze nastavování se postupně mění v závislosti na aktuální fázi nastavování. Kromě fáze NASTAVENI_SEK, kde je zvolen normální režim a je vynulován čítač delay1s. Modul show Tento modul se stará o zobrazování hodnot na LCD displeji. Bere také v úvahu možnost obou režimů systému. Modul obsahuje pouze dvě již zmíněné funkce time_show() a date_show().
Funkce time_show() se stará o zobrazení času na LCD displeji. Na začátku je potřeba vymazat předešlý čas (zobrazit mezery). Poté se seskládá text času ve formátu 0:00:00, tedy hodiny jsou zobrazeny dle své hodnoty (pro menší než 10 nejsou desítky zobrazeny), minuty jsou doplněny na dvě číslice, stejně jako sekundy. Následně se nastaví kurzor LCD displeje na danou pozici (čas bude zobrazen na prvním řádku uprostřed) a text se zobrazí. Následně se zkontroluje, jestli je systém v režimu nastavování a má být nastavovaná hodnota zhasnuta (bit hide). Pokud ano jsou v závislosti na fázi nastavování vymazány hodnoty právě nastavované veličiny.
Funkce date_show() zajišťuje zobrazení data. Nejprve je nutné vymazat celý druhý řádek LCD displeje. Poté se opět poskládá text data (pole date_string). Podle dne v týdnu se do proměnné date_string uloží text dne v týdnu. Následně se přidají hodnoty dne v měsíci, měsíce a roku. Poté se vypočítá umístění (uprostřed – proměnná pocatek) textu na řádku, nastaví se kurzor LCD displeje na danou pozici a text se zobrazí. Následně se zkontroluje, jestli je systém v režimu nastavování a má být nastavovaná hodnota zhasnuta (bit hide), pokud ano, je v závislosti na fázi nastavování a na velikosti dané veličiny vymazána hodnota nastavované veličiny. Modul main Tento modul zahrnuje jedinou funkci main(), která zajišťuje funkčnost všech modulů.
Funkce main() je první funkcí, kterou volá překladač. Tedy tato funkce je spuštěna vždy po resetu. Po resetu je nutné inicializovat proměnné zavoláním funkce init_variables() a inicializovat časovač prostřednictví funkce init_timer_isr(). Funkce init_variables() inicializuje i přístup k LCD displeji, proto se ve funkci main() kontroluje, jestli byl opravdu přístup získán, pokud ne je nastaven druhý výstup portu PIO_LEDR (rozsvítí se LED dioda LEDR1) a program skončí v nekonečné smyčce, ve které se nic neprovádí. V případě, že přístup k LCD byl získán, vynuluje se LCD displej (funkce alt_up_character_lcd_init()) a zobrazí se čas a datum. Následuje nekonečná smyčka programu. V této smyčce se kontroluje, jestli došlo ke změně času (nastavení bitu novyCas registru stavSystemu), pokud ano, tento bit se vynuluje a zobrazí se nový čas prostřednictvím funkce time_show(). Následně se kontroluje, jestli došlo také ke změně data (nastavení bitu
novyDatum registru stavSystemu), pokud ano, je tento bit vynulován a zobrazí se nový datum
zavoláním funkce date_show(). Pozn. ke změně času může dojít pouze v normálním režimu. Následuje část programu, ve které se kontroluje, jestli došlo ke změně bitu hide nastavením bitu changeHide registru stavSystemu. Tento bit se může nastavit pouze v režimu nastavování a určuje, že se má zobrazit nebo zhasnout hodnota právě nastavované veličiny na LCD displeji. Při zjištění nastavení bitu changeHide se tento bit vynuluje a zkontroluje se, v jaké fázi nastavování se systém nachází. Pokud se nastavují hodnoty pro čas, je zobrazen čas zavoláním funkce time_show(), která se již postará o správné zobrazení času. Pokud se nenastavují hodnoty pro čas, mohou se nastavovat hodnoty pro datum, proto se zavolá funkce date_show(), která se postará o správné zobrazení data. Následuje kontrola bitu is10ms registru stavSystemu. Tento bit je nastaven každých 10ms v obsluze přerušení časovače. Je-li tento bit nastaven, vynuluje se a zavolá se funkce osetreni_tlacitek() pro obsluhu tlačítek. Tímto způsobem je zajištěno správné časování stisku tlačítek a není zbytečně zatěžována obsluha přerušení. Poslední částí nekonečné smyčky hlavního programu je ovládání nultého bitu výstupního portu PIO_LEDR. Tento bit je nastavován v případě, kdy se systém nachází v režimu nastavování a naopak je vynulováván v případě, že se systém nachází v normálním režimu. Tento bit je fyzicky připojen na LED diodu LEDR0. Tedy tato LED dioda svítí, je-li systém v režimu nastavování.