Operácie

Odpočítavacie hodiny na riadenie diskusie: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentDVPS (diskusia | príspevky)
StudentDVPS (diskusia | príspevky)
 
(53 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 32: Riadok 32:
== Analýza ==
== Analýza ==


V tejto časti popíšete ako idete daný problém riešiť. Uvediete sem aj všetky potrebné technické údaje,
7-segmentový displej bude riadený procesorom ATMEL ATMEGA 328P, ktorý je súčasťou vývojovej dosky ARDUINO DUEMILANOVE. Táto doska má 14 digitálnych vstupno/výstupných pinov, z ktorých môžem šesť použiť ako PWM výstupy, šesť analógových vstupov a 16 MHz kryštál. Všetky vstupy/výstupy sú zreteľne označené na doske. Pre pripojenie je možné využiť napájací konektor a USB pripojenie.
ktoré potrebné na úspešné vyriešenie projektu. Napríklad:
K tejto doske je pripojená vývojová doska so 7-segmentovým displejom podľa schémy zapojenia uvedenej v ''Popise riešenia''.  
 
Ostatné informácie o vývojovej doske ARDUINO DUEMILANOVE podrobne vypísané na nasledujúcej adrese http://arduino.cc/en/Main/arduinoBoardDuemilanove.


* popis komunikačnej zbernice (i2c, 1-wire, RS-232 a pod.)
* obrázok zapojenia vývodov použitej súčiastky
* odkaz na katalógový list
* priebehy dôležitých signálov


'''POTREBNÉ HARDVÉROVÉ VYBAVENIE:'''
'''POTREBNÉ HARDVÉROVÉ VYBAVENIE:'''
Riadok 44: Riadok 42:
             '''''Vývojová doska ARDUINO DUEMILANOVE'''''
             '''''Vývojová doska ARDUINO DUEMILANOVE'''''


[[Súbor:Duemilanove.jpg|300px|thumb|center|Vývojová doska ARDUINO DUEMILANOVE.]]
            '''''Vývojová doska so 7-segmentovým displejom, bzučiakom a tlačidlami'''''
[[Súbor:MR7sd1.jpg|600px|center|thumb|Vývojová doska so 7 segmentovým displejom, bzučiakom a tlačidlami.]]


[[Súbor:Duemilanove.jpg|400px|thumb|center|Vývojová doska ARDUINO DUEMILANOVE.]]


'''KATALÓGOVÉ LISTY POUŽITÝCH KOMPONENTOV:'''


            '''''Vývojová doska so 7-segmentovým displejom'''''


            '''''7-segmentový displej'''''


[[Súbor:MR7sd.jpg|400px|thumb|Vývojová doska so 7 segmentovým displejom, bzučiakom a tlačidlami.]],[[Súbor:MR7sd.jpg|400px|thumb|Vývojová doska.]]
            [http://www.alldatasheet.com/datasheet-pdf/pdf/220980/BRIGHT/BQ-M514RD.html?
 
            '''''Procesor Atmel ATmega 328P - použitý na vývojovej doske ARDUINO DUEMILANOVE'''''
 
            [http://www.atmel.com/Images/doc8161.pdf


== Popis riešenia ==
== Popis riešenia ==
Sem opíšete ako konkrétne ste problém vyriešili. Začnite popisom pripojenia k procesoru
(nezabudnite na schému zapojenia!) a zdôraznite ktoré jeho periférie ste pritom využili.


'''Schéma zapojenia 7-segmentovky'''
'''Schéma zapojenia 7-segmentovky'''
Riadok 65: Riadok 70:




[[Súbor:Example.jpg]]
=== Algoritmus a program ===


Pozn.: Názov obrázku musí byť jedinečný, uvedomte si, že Obr1.jpg už pred vami skúsilo
Riadiaci algoritmus pre procesor ATmega 382P bol vytvorený v programe AVR Studio 6.0. Následne pomocou USB pripojenia vývojovej dosky ARDUINO DUEMILANOVE bol súbor *.hex  nahraný rýchlosťou 57 600 baudov prostredníctvom softvéru XLOADER do procesora.
nahrať už aspoň 10 študentov.  




Zdrojový kód pre našu aplikáciu vyzerá nasledovne:


=== Algoritmus a program ===
<source lang="c">
#define F_CPU 16000000UL //Takt procesora 16MHz
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
 
//Makra pre operacie s bitmi
#define bit(m) ((1<<m))
#define bit_get(p,m) ((p) & bit(m))
#define bit_set(p,m) ((p) |= bit(m))
#define bits_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~bit(m))
#define bits_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= bit(m))
 
//Registre portov
#define DDR(x) (*(&x - 1))
#define PIN(x) (*(&x - 2))
 
//Casovac0 pouzity pre zobrazovanie znakov na multiplexovanej 7 segmentovke
#define ZOBRAZENIE_START() TCNT0+=55;bit_set(TCCR0B,CS01) //Delicka=8, perioda prerusenia 100us
#define ZOBRAZENIE_STOP() bit_clear(TCCR0B,CS01);TCNT0=0 //Zastavenie casovaca
 
//Casovac1 pouzity pre vypocet sekund
#define CASOVAC_START() TCNT1+=3035;bit_set(TCCR1B,CS12) //Delicka=64, perioda prerusenia 1s
#define CASOVAC_STOP() bit_clear(TCCR1B,CS12);TCNT1=0 //Zastavenie casovaca
 
//v Bootloaderi je povolenie tychto pinov (RX,TX) pre nas program ich vsak nepotrebujeme
#define UART_DISABLE() bits_clear(UCSR0B,bit(RXEN0)|bit(TXEN0))
 
//7segmentovka pinout
#define SEG_PORT PORTD
#define SEG_a 0
#define SEG_b 1
#define SEG_c 2
#define SEG_d 3
#define SEG_e 4
#define SEG_f 5
#define SEG_g 6
#define SEG_dp 7
 
#define SEG_DIGIT_PORT PORTB
#define SEG_DIGIT_A1 3
#define SEG_DIGIT_A2 2
#define SEG_DIGIT_A3 1
#define SEG_DIGIT_A4 0
 
#define SEG_INIT() bits_set(DDR(SEG_PORT),(bit(SEG_a)|bit(SEG_b)|bit(SEG_c)|bit(SEG_d)|bit(SEG_e)|bit(SEG_f)|bit(SEG_g)|bit(SEG_dp)));bits_set(DDR(SEG_DIGIT_PORT),
(bit(SEG_DIGIT_A1)|bit(SEG_DIGIT_A2)|bit(SEG_DIGIT_A3)|bit(SEG_DIGIT_A4)))
 
//Bzuciak
#define BUZ_PORT PORTB
#define BUZ_PIN 5
 
#define BUZ_INIT() bit_clear(BUZ_PORT,BUZ_PIN);bit_set(DDR(BUZ_PORT),BUZ_PIN)
 
//Tlacidlo
#define PB_PORT PORTB
#define PB_PIN 4
 
#define PB_INIT() bit_set(PB_PORT,PB_PIN);bit_clear(DDR(PB_PORT),PB_PIN)
 
//Premenne su typu volatile, co znamena ze ich kompilator skompiluje a hodnoty sa v obsluhach preruseni budu spravne spracovavat
volatile int8_t sec_jed,sec_des,min_jed,min_des; //znamienkove 8 bitove premenne obsahujuce hodnoty casomiery
volatile uint16_t delay,dlho; //neznamienkove 16 bitove premenne
volatile uint8_t kratko; //neznamienkova 8bitova premenna pre funkciu tlacidla
 
//Fukncia zobrazujuca cisla ja zvolenom mieste(sekundy, minuty..)
void cisla_segment(uint8_t segment,uint8_t cislo)
{
SEG_DIGIT_PORT |= 0x0F - (1<<segment);  //Vypne segmenty okrem toho, ktory sa prave zobrazuje
switch (cislo)
{
case 0: SEG_PORT=0xC0; //cislo 0
break;
case 1: SEG_PORT=0xF9; //cislo 1
break;
case 2: SEG_PORT=0xA4; //cislo 2
break;
case 3: SEG_PORT=0xB0; //cislo 3
break;
case 4: SEG_PORT=0x99; //cislo 4
break;
case 5: SEG_PORT=0x92; //cislo 5
break;
case 6: SEG_PORT=0x82; //cislo 6
break;
case 7: SEG_PORT=0xF8; //cislo 7
break;
case 8: SEG_PORT=0x80; //cislo 8
break;
case 9:SEG_PORT=0x90; //cislo 9
break;
case 10: SEG_PORT=0x7F; //bodka
break;
// default: SEG_PORT=0xBF; //pomlcka
// break;
}
SEG_DIGIT_PORT &= ~(1<<segment); //Zapne aktualny segment
//zvysenie svietivosti pomocou predlzenia doby aktivneho segmentu
for (uint8_t i=0;i<100;i++)
{
segment++; //Pouzitie lokalnej premennej pre vytvorenie pauzy
}
}
 
//Funkcia ovladajuca dlzku tonu bzuciaka
void bzuciak(uint16_t dlzka)
{
delay=0;
bit_set(BUZ_PORT,BUZ_PIN); //Zapnutie bzuciaka
while(delay<dlzka);         //Pauza
bit_clear(BUZ_PORT,BUZ_PIN);         //Vypnutie bzuciaka
}


Uveďte stručný popis algoritmu, v akom jazyku a verzii vývojového prostredia ste ho vytvorili.
//Funkcia zobrazujuca aktalne nastaveny cas pocitadla
Je vhodné nakresliť aspoň hrubú štruktúru programu napríklad vo forme vývojového diagramu.
void cas(void)
Rozsiahly program pre lepšiu prehľadnosť rozdeľte do viacerých súborov.
{
cisla_segment(SEG_DIGIT_A2,10); //Zobrazenie bodky medzi minutami a sekundami
cisla_segment(SEG_DIGIT_A4,sec_jed); //Jednotky sekund
cisla_segment(SEG_DIGIT_A3,sec_des); //Desiatky sekund
cisla_segment(SEG_DIGIT_A2,min_jed); //Jednotky minut
cisla_segment(SEG_DIGIT_A1,min_des); //Desiatky minut
if(TCCR1B)                         //Ak casomiera bezi, testuju sa nasledovne podmienky
{
if (min_des == 0 && min_jed == 0 && sec_des > 0 && sec_jed > 0  ) //Ak je aktualny cas mensi ako minuta cislice budu blikat
{
if (TCNT1<10000) //Tato podmienka zaruci aby sa bliknutie vykonalo iba raz za sekundu
{
bit_set(SEG_DIGIT_PORT,SEG_DIGIT_A1); //Vypnutie prveho znaku postacuje kedze je to multiplexovana segmentovka
_delay_ms(20);
}
}
if (min_des == 0 && min_jed == 0 && sec_des == 0 && sec_jed > 0  ) //Pri poslednych 10 sekundach cislice blikaju
{
if (TCNT1<5000) //Rychlejsie prebliknutie pri poslednych 10 sekundach
{
bit_set(SEG_DIGIT_PORT,SEG_DIGIT_A1); //Vypnutie prveho znaku
_delay_ms(10);
}
}
}
}


Vyberte podstatné časti zdrojového kódu, použite na to prostredie ''source'':
//Funkcia na obsluhu tlacidla
uint8_t tlacidlo()
{
uint8_t akt_stav=0;
for(uint8_t i=0;i<5;i++) //Odstranenie zakmitov
{
akt_stav = !bit_get(PIN(PB_PORT),PB_PIN); //Citanie logickeho stavu na pine tlacidla, pricom hodnota sa neguje
}
if (akt_stav) //Ak je tlacidlo stlacene inkrementuju sa premenne pre indikaciu dlheho a kratkeho stlacenia
{
kratko++; //8bit premenna = hodnoty 0-255
dlho++; //16bit premenna = hodnoty 0-65535
}
else dlho=0; //ak sa tlacidlo pustilo premenna indikujuca dlhe stlacenie sa vynuluje
return akt_stav; //Vratenie vysledneho stavu tlacidla(stlacene/nestlacene)
}


<source lang="c">
//Nastavovanie casomiery
/* A nezabudnite zdroják hojne komentovať  */
void nastavenie_casu(void)
{
sec_jed=0,sec_des=0,min_jed=0,min_des=1; //Globalne premenne obsahujuce hodnoty prednastavenej casomiery
ZOBRAZENIE_START(); //Spustenie casovaca pre zobrazenie znakov na segmentovke
CASOVAC_STOP(); //Pripadne zastavenie pocitadla sekund
//Cyklus na inkrementaciu a dekrementaciu casomiery po sekundach
do
{
//Cyklus inkrementacie casomiery po sekundach
do
{
if (tlacidlo()&& !kratko)                        //V pripade ze je tlacidlo stlacene kratko (je stlacene a hodnota kratko pretecie
                                                                          raz => 0) casomiera sa inkrementuje o jednu sekundu
{
sec_jed++;
}
if (sec_jed>9) //Ak je viac ako 9 sekund,
{
sec_jed=0; //Jednotky sekund sa vynuluju a
sec_des++; //Desiatky sekund sa inkrementuju o jedno
}
if (sec_des>5) //Ak je viac ako 59 sekund,
{
sec_des=0; //Desiatky sekund sa vynuluju a
min_jed++; //Jednotky minut sa inkrementuju o jedno
}
if (min_jed>9) //Ak je viac ako 9 minut,
{
min_jed=0; //Jednotky minut sa vynuluju a
if (min_des<6) //V pripade ze je pocet desiatok minut mensi ako 6,
{
min_des++; //Desiatky minut inkrementuju o jedno
}
else min_des=0; //Inak sa desiatky nuluju
}
} while (!(tlacidlo()&& dlho>2000)); //V pripad stlacenia tlacidla dlhsie ako 2s sa ukoncuje cyklus inkrementacie
dlho=0; //hodnota sa nuluje v pripade, ze je tlacidlo stlacene dlhsie ako 2s aby nedoslo
                                                                        k nechcenemu ukonceniu cyklu
bzuciak(100);         //Pre uzivatela sa vysle kratky signal bzuciakom potvrdenie ukoncenia cyklu
//Cyklus dekrementacie casomiery po sekundach
do
{
if (tlacidlo()&& !kratko)         //V pripade ze je tlacidlo stlacene kratko (je stlacene a hodnota kratko pretecie raz => 0)
{
sec_jed--; //casomiera sa dekrementuje o jednu sekundu
}
if (sec_jed < 0) //Ak je jednotiek sekund menej ako nula
{
sec_des--; //Tak sa desiatky sekund dekrementuju
sec_jed = 9; //Pricom sa jednotky nastavia na 9
if (sec_des < 0) //Ak je desiatok sekund menej ako nula nastavia sa na hodnotu 5
{
sec_des = 5;
min_jed--; //Pricom sa jednotky minut dekrementuju o jedno
if (min_jed<0) //V pripade ze su jednotky minut mensie ako nula,
{
min_jed=9; //nastavia sa jednotky minut na hodnotu 9 a desiatky sa dekrementuju o jedno
min_des--;
if(min_des<0) //Ak su desiatky minut mensie ako je zostavaju na nule
{
min_des=0;
}
}
}
}
} while (!(tlacidlo()&& dlho>2000)); //Pre ukoncenie cyklu dekrementacie je potrebne tlacidlo drzat dlhsie ako 2s
bzuciak(100); //Kratkym tonom je uzivatel oboznameny s ukoncenym cyklu dekrementacie
} while (!(tlacidlo()&& dlho>2000)); //Neustalym drzanim tlacidla sa dostavame von z cykla inkrementacie a dekrementacie
bzuciak(300); //Dlhym tonom je oznamene ukoncenie cykla a start casomiery
CASOVAC_START(); //Start casovaca s periodou 1s
}


int main(void) {
int main(void)
{
//Inicializacne makra
UART_DISABLE(); //Vypnutie alternativnej funkcie pinov(UARTu=RX,TX) PD0 a PD1
SEG_INIT(); //Nastavenie vystupov multiplexovanej 7 segmentovky
BUZ_INIT(); //Nastavenie vystupu bzuciaka
PB_INIT(); //Nastavenie vstupu tlacidla
//Nastavenie masiek preruseni pre pouzite casovace
bit_set(TIMSK0,TOIE0);
bit_set(TIMSK1,TOIE1);
bit_set(TIMSK2,TOIE2);
//Start casovaca s delickou 32 a periodou prerusenia 100us
TCNT2 += 5;
bits_set(TCCR2B,(bit(CS21)|bit(CS20)));
//Globalne povolenie preruseni
sei();
//Po inicializacii sa spusta proces nastavenia casu casomiery
nastavenie_casu();
      
      
     printf("Hello, World!\n");
while(1)         //Nekonecna slucka
     return(0);
     {
if (sec_des == 0 && sec_jed == 0 && min_des == 0 && min_jed == 5) //Pri 5. minute sa spusti kratky ton bzuciaka
{
if (TCNT1<6000)bzuciak(250);
}
if (sec_des == 0 && sec_jed == 0 && min_des == 0 && min_jed == 1) //Pri 1. minute sa spusti dlhsi ton bzuciaka
{
if (TCNT1<10000)bzuciak(500);
}
if (sec_des == 0 && sec_jed == 0 && min_des == 0 && min_jed == 0) //Ked sa odpocet skonci,
{
bit_set(SEG_DIGIT_PORT,SEG_DIGIT_A1); //Segmentovka sa vypne
ZOBRAZENIE_STOP();
bzuciak(5000); //Bzuciak vydava dlhy ton
CASOVAC_STOP(); //Casovac sekund sa vypne
nastavenie_casu(); //Proces nastavovania casomiery zacina odznova
}
else if (sec_jed < 0) //Ak je jednotiek sekund menej ako nula
{
sec_des--; //Tak sa desiatky sekund dekrementuju
sec_jed = 9; //Pricom sa jednotky nastavia na 9
if (sec_des < 0)         //Ak je desiatok sekund menej ako nula nastavia sa na hodnotu 5
{
sec_des = 5;
min_jed--; //Pricom sa jednotky minut dekrementuju o jedno
if (min_jed<0) //V pripade ze su jednotky minut mensie ako nula,
{
min_jed=9; //nastavia sa jednotky minut na hodnotu 9 a desiatky sa dekrementuju o jedno
min_des--;
if(min_des<0) min_des=0; //Ak su desiatky minut mensie ako je zostavaju na nule
}
}
}
     }
}
 
//Obsluhy preruseni
ISR(TIMER0_OVF_vect)
{
TCNT0+=55; //perioda 100us
cas();
}
ISR(TIMER1_OVF_vect)
{
TCNT1+=3035; //perioda 1s
sec_jed--;
}
ISR(TIMER2_OVF_vect)
{
TCNT2+=5; //perioda 500us
delay++;
}
}
</source>
</source>


Nezabudnite však nahrať aj kompletné zdrojové kódy vášho programu!
=== Overenie ===
 
Po pripojení odpočítavacích hodín k zdroju napájania prostredníctvom USB alebo konektora "JACK" sa na displeji rozsvieti čas 10:00. Tento čas je možné pomocou tlačidla SET pod 7-segmentovým displejom nastaviť na ľubovoľnú hodnotu.
Ak chceme čas na displeji nastaviť na vyššiu hodnotu ako je predvolená je potrebné stláčať tlačidlo ''SET'' v krátkych intervaloch.


Zdrojový kód: [[Médiá:Serial.h|serial.h]] a [[Médiá:Pip.c|main.c]]


[[Médiá:MojProgram.c|program.c]]
[[Súbor:SET_button.jpg|400px|thumb|center|Tlačidlo ''SET''.]]


=== Overenie ===


Nezabudnite napísať čosi ako užívateľský návod. Z neho by malo byť jasné čo program robí,
V prípade potreby nastavenia nižšej hodnoty je nutné tlačidlo ''SET'' pridržať po dobu asi 2 sekúnd. Po krátkom pípnutí pustite tlačidlo a začnite ho stláčať v krátkych intervaloch - hodnota na displeji sa bude zmenšovať.
ako sa prejavuje a aké má užívateľské rozhranie (čo treba stlačiť, čo sa kde zobrazuje).
Po nastavení žiadanej hodnoty pridržte tlačidlo ''SET'' po dobu piatich sekúnd. Po dlhšom pípnutí sa začne odpočítavanie.
Ak ste namerali nejaké signály, sem s nimi. Ak je výsledkom nejaký údaj na displeji,
 
odfotografujte ho.  
Posledných 5 minút je užívateľovi oznámených krátkym pípnutím. Pred odpočítavaním poslednej minúty je počuť dva-krát krátke bliknutie a displej počas zostávajúceho času bliká. Nakoniec zaznie dlhé pípnutie a displej je znovu nastavený na hodnotu 10:00.
 
V prípade problémov je možné odpočítavacie hodiny resetovať pomocou tlačidla ''RESET''. Hodiny sa znovu nastavia na hodnotu 10:00.
 


Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.
[[Súbor:RESET_button.jpg|400px|thumb|center|Tlačidlo ''RESET''.]]




[[Category:AVR]] [[Category:DVPS]]
[[Category:AVR]] [[Category:DVPS]]

Aktuálna revízia z 19:53, 10. január 2014

Autori: Maros Rybarik, Matus Lukac
Študijný odbor: Aplikovaná mechatronika 2. Ing. (2013)

Zadanie

Na konferencii je často potrebné ustrážiť, aby rečník neprekročil vymedzený časový rozsah. Naprogramujte veľkoplošný 7-segmentový LED displej tak, aby od prednastavenej hodnoty odpočítaval minúty smerom nadol, pričom pri zvyšných 5 minútach nenápadne pípne, pri poslednej minúte pípne 2x výraznejšie a po uplynutí času pípne raz dlho. Posledná minúta odpočítavania by mohla byť sprevádzaná aj blikaním displeja. Ovládanie tlačidlom ŠTART a +/- na zmenu počiatočnej hodnoty.

K zapojeniu treba vypracovať dokumentáciu, popis programu, schému zapojenia displeja a riadiacej jednotky. Ako bonus doplňte riadenie intenzity displeja na základe vonkajšieho osvetlenia.

Literatúra:

Analýza

7-segmentový displej bude riadený procesorom ATMEL ATMEGA 328P, ktorý je súčasťou vývojovej dosky ARDUINO DUEMILANOVE. Táto doska má 14 digitálnych vstupno/výstupných pinov, z ktorých môžem šesť použiť ako PWM výstupy, šesť analógových vstupov a 16 MHz kryštál. Všetky vstupy/výstupy sú zreteľne označené na doske. Pre pripojenie je možné využiť napájací konektor a USB pripojenie. K tejto doske je pripojená vývojová doska so 7-segmentovým displejom podľa schémy zapojenia uvedenej v Popise riešenia.

Ostatné informácie o vývojovej doske ARDUINO DUEMILANOVE sú podrobne vypísané na nasledujúcej adrese http://arduino.cc/en/Main/arduinoBoardDuemilanove.


POTREBNÉ HARDVÉROVÉ VYBAVENIE:

            Vývojová doska ARDUINO DUEMILANOVE
Vývojová doska ARDUINO DUEMILANOVE.


            Vývojová doska so 7-segmentovým displejom, bzučiakom a tlačidlami
Vývojová doska so 7 segmentovým displejom, bzučiakom a tlačidlami.


KATALÓGOVÉ LISTY POUŽITÝCH KOMPONENTOV:


            7-segmentový displej
            [http://www.alldatasheet.com/datasheet-pdf/pdf/220980/BRIGHT/BQ-M514RD.html?
            Procesor Atmel ATmega 328P - použitý na vývojovej doske ARDUINO DUEMILANOVE
            [http://www.atmel.com/Images/doc8161.pdf

Popis riešenia

Schéma zapojenia 7-segmentovky


Schema zapojenia 7 segmentového displeja.


Algoritmus a program

Riadiaci algoritmus pre procesor ATmega 382P bol vytvorený v programe AVR Studio 6.0. Následne pomocou USB pripojenia vývojovej dosky ARDUINO DUEMILANOVE bol súbor *.hex nahraný rýchlosťou 57 600 baudov prostredníctvom softvéru XLOADER do procesora.


Zdrojový kód pre našu aplikáciu vyzerá nasledovne:

#define F_CPU 16000000UL	//Takt procesora 16MHz
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>

//Makra pre operacie s bitmi
#define bit(m)			((1<<m))
#define bit_get(p,m)	((p) & bit(m))
#define bit_set(p,m)	((p) |= bit(m))
#define bits_set(p,m)	((p) |= (m))
#define bit_clear(p,m)	((p) &= ~bit(m))
#define bits_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m)	((p) ^= bit(m))

//Registre portov
#define DDR(x)			(*(&x - 1))
#define PIN(x)			(*(&x - 2))

//Casovac0 pouzity pre zobrazovanie znakov na multiplexovanej 7 segmentovke
#define ZOBRAZENIE_START()	TCNT0+=55;bit_set(TCCR0B,CS01)		//Delicka=8, perioda prerusenia 100us
#define ZOBRAZENIE_STOP()	bit_clear(TCCR0B,CS01);TCNT0=0		//Zastavenie casovaca

//Casovac1 pouzity pre vypocet sekund
#define CASOVAC_START()		TCNT1+=3035;bit_set(TCCR1B,CS12)	//Delicka=64, perioda prerusenia 1s
#define CASOVAC_STOP()		bit_clear(TCCR1B,CS12);TCNT1=0		//Zastavenie casovaca

//v Bootloaderi je povolenie tychto pinov (RX,TX) pre nas program ich vsak nepotrebujeme
#define UART_DISABLE() bits_clear(UCSR0B,bit(RXEN0)|bit(TXEN0)) 

//7segmentovka pinout
#define SEG_PORT PORTD
#define SEG_a	0
#define SEG_b	1
#define SEG_c	2
#define SEG_d	3
#define SEG_e	4
#define SEG_f	5
#define SEG_g	6
#define SEG_dp	7

#define SEG_DIGIT_PORT	PORTB
#define SEG_DIGIT_A1	3
#define SEG_DIGIT_A2	2
#define SEG_DIGIT_A3	1
#define SEG_DIGIT_A4	0

#define SEG_INIT()	bits_set(DDR(SEG_PORT),(bit(SEG_a)|bit(SEG_b)|bit(SEG_c)|bit(SEG_d)|bit(SEG_e)|bit(SEG_f)|bit(SEG_g)|bit(SEG_dp)));bits_set(DDR(SEG_DIGIT_PORT),
(bit(SEG_DIGIT_A1)|bit(SEG_DIGIT_A2)|bit(SEG_DIGIT_A3)|bit(SEG_DIGIT_A4)))

//Bzuciak
#define BUZ_PORT	PORTB
#define BUZ_PIN		5

#define BUZ_INIT()	bit_clear(BUZ_PORT,BUZ_PIN);bit_set(DDR(BUZ_PORT),BUZ_PIN)

//Tlacidlo
#define PB_PORT		PORTB
#define PB_PIN		4

#define PB_INIT()	bit_set(PB_PORT,PB_PIN);bit_clear(DDR(PB_PORT),PB_PIN)

//Premenne su typu volatile, co znamena ze ich kompilator skompiluje a hodnoty sa v obsluhach preruseni budu spravne spracovavat
volatile int8_t sec_jed,sec_des,min_jed,min_des;		//znamienkove 8 bitove premenne obsahujuce hodnoty casomiery
volatile uint16_t delay,dlho;					//neznamienkove 16 bitove premenne 
volatile uint8_t kratko;					//neznamienkova 8bitova premenna pre funkciu tlacidla

//Fukncia zobrazujuca cisla ja zvolenom mieste(sekundy, minuty..)
void cisla_segment(uint8_t segment,uint8_t cislo)
{
	SEG_DIGIT_PORT |= 0x0F - (1<<segment);  //Vypne segmenty okrem toho, ktory sa prave zobrazuje
	
	switch (cislo)
	{
		case 0: SEG_PORT=0xC0;		//cislo 0
			break;
		case 1:	SEG_PORT=0xF9;		//cislo 1
			break;
		case 2:	SEG_PORT=0xA4;		//cislo 2
			break;
		case 3:	SEG_PORT=0xB0;		//cislo 3
			break;
		case 4:	SEG_PORT=0x99;		//cislo 4
			break;
		case 5: SEG_PORT=0x92;		//cislo 5
			break;
		case 6: SEG_PORT=0x82;		//cislo 6
			break;
		case 7: SEG_PORT=0xF8;		//cislo 7
			break;
		case 8: SEG_PORT=0x80;		//cislo 8
			break;
		case 9:SEG_PORT=0x90;		//cislo 9
			break;
		case 10: SEG_PORT=0x7F;		//bodka
			break;
// 		default: SEG_PORT=0xBF;		//pomlcka
// 			break;
	}
	SEG_DIGIT_PORT &= ~(1<<segment);		//Zapne aktualny segment
	
	//zvysenie svietivosti pomocou predlzenia doby aktivneho segmentu
	for (uint8_t i=0;i<100;i++)
	{
		segment++;				//Pouzitie lokalnej premennej pre vytvorenie pauzy
	}
	
}

//Funkcia ovladajuca dlzku tonu bzuciaka
void bzuciak(uint16_t dlzka)
{
	delay=0;
	
	bit_set(BUZ_PORT,BUZ_PIN);		//Zapnutie bzuciaka
	while(delay<dlzka);		        //Pauza
	bit_clear(BUZ_PORT,BUZ_PIN);	        //Vypnutie bzuciaka		
}

//Funkcia zobrazujuca aktalne nastaveny cas pocitadla
void cas(void)
{
	cisla_segment(SEG_DIGIT_A2,10);		//Zobrazenie bodky medzi minutami a sekundami	
	cisla_segment(SEG_DIGIT_A4,sec_jed);	//Jednotky sekund
	cisla_segment(SEG_DIGIT_A3,sec_des);	//Desiatky sekund
	cisla_segment(SEG_DIGIT_A2,min_jed);	//Jednotky minut
	cisla_segment(SEG_DIGIT_A1,min_des);	//Desiatky minut
				
	if(TCCR1B)						                         //Ak casomiera bezi, testuju sa nasledovne podmienky
	{
		if (min_des == 0 && min_jed == 0 && sec_des > 0 && sec_jed > 0  )	//Ak je aktualny cas mensi ako minuta cislice budu blikat
		{
			if (TCNT1<10000)						//Tato podmienka zaruci aby sa bliknutie vykonalo iba raz za sekundu
			{
				bit_set(SEG_DIGIT_PORT,SEG_DIGIT_A1);			//Vypnutie prveho znaku postacuje kedze je to multiplexovana segmentovka
				_delay_ms(20);
			}
		
		}
		if (min_des == 0 && min_jed == 0 && sec_des == 0 && sec_jed > 0  )	//Pri poslednych 10 sekundach cislice blikaju
		{
			if (TCNT1<5000)							//Rychlejsie prebliknutie pri poslednych 10 sekundach
			{
				bit_set(SEG_DIGIT_PORT,SEG_DIGIT_A1);			//Vypnutie prveho znaku
				_delay_ms(10);
			}
		
		}	
	}	
	
}

//Funkcia na obsluhu tlacidla
uint8_t tlacidlo()
{
	uint8_t akt_stav=0;	
	
	for(uint8_t i=0;i<5;i++)					//Odstranenie zakmitov
	{
		akt_stav = !bit_get(PIN(PB_PORT),PB_PIN);		//Citanie logickeho stavu na pine tlacidla, pricom hodnota sa neguje
	}		
	
	if (akt_stav)							//Ak je tlacidlo stlacene inkrementuju sa premenne pre indikaciu dlheho a kratkeho stlacenia	
	{
		kratko++;						//8bit premenna = hodnoty 0-255
		dlho++;							//16bit premenna = hodnoty 0-65535
	}
	else dlho=0;							//ak sa tlacidlo pustilo premenna indikujuca dlhe stlacenie sa vynuluje
	
	return akt_stav;						//Vratenie vysledneho stavu tlacidla(stlacene/nestlacene)
}

//Nastavovanie casomiery
void nastavenie_casu(void)
{
	sec_jed=0,sec_des=0,min_jed=0,min_des=1;			//Globalne premenne obsahujuce hodnoty prednastavenej casomiery
	ZOBRAZENIE_START();						//Spustenie casovaca pre zobrazenie znakov na segmentovke
	CASOVAC_STOP();							//Pripadne zastavenie pocitadla sekund
	
	//Cyklus na inkrementaciu a dekrementaciu casomiery po sekundach
	do 
	{
		//Cyklus inkrementacie casomiery po sekundach
 		do 
		{
			if (tlacidlo()&& !kratko)                        //V pripade ze je tlacidlo stlacene kratko (je stlacene a hodnota kratko pretecie 
                                                                           raz => 0) casomiera sa inkrementuje o jednu sekundu
			{
				sec_jed++;
			}
		
			if (sec_jed>9)					//Ak je viac ako 9 sekund,
			{
				sec_jed=0;				//Jednotky sekund sa vynuluju a
				sec_des++;				//Desiatky sekund sa inkrementuju o jedno
			}
			if (sec_des>5)					//Ak je viac ako 59 sekund,
			{
				sec_des=0;				//Desiatky sekund sa vynuluju a
				min_jed++;				//Jednotky minut sa inkrementuju o jedno
			}	
			if (min_jed>9)					//Ak je viac ako 9 minut,
			{
				min_jed=0;				//Jednotky minut sa vynuluju a
				if (min_des<6)				//V pripade ze je pocet desiatok minut mensi ako 6,
				{
					min_des++;			//Desiatky minut inkrementuju o jedno
				}
				else min_des=0;				//Inak sa desiatky nuluju
			}
		} while (!(tlacidlo()&& dlho>2000));			//V pripad stlacenia tlacidla dlhsie ako 2s sa ukoncuje cyklus inkrementacie 
		dlho=0;							//hodnota sa nuluje v pripade, ze je tlacidlo stlacene dlhsie ako 2s aby nedoslo 
                                                                        k nechcenemu ukonceniu cyklu 
		bzuciak(100);					        //Pre uzivatela sa vysle kratky signal bzuciakom potvrdenie ukoncenia cyklu
		
		//Cyklus dekrementacie casomiery po sekundach
		do 
		{
			if (tlacidlo()&& !kratko)		        //V pripade ze je tlacidlo stlacene kratko (je stlacene a hodnota kratko pretecie raz => 0)
			{
				sec_jed--;				//casomiera sa dekrementuje o jednu sekundu
			}
		
			if (sec_jed < 0)				//Ak je jednotiek sekund menej ako nula
			{
				sec_des--;				//Tak sa desiatky sekund dekrementuju
				sec_jed = 9;				//Pricom sa jednotky nastavia na 9
				if (sec_des < 0)			//Ak je desiatok sekund menej ako nula nastavia sa na hodnotu 5
				{
					sec_des = 5;						
					min_jed--;			//Pricom sa jednotky minut dekrementuju o jedno
					if (min_jed<0)			//V pripade ze su jednotky minut mensie ako nula,
					{
						min_jed=9;		//nastavia sa jednotky minut na hodnotu 9 a desiatky sa dekrementuju o jedno
						min_des--;
						if(min_des<0)		//Ak su desiatky minut mensie ako je zostavaju na nule
						{
							min_des=0;
						}
					}
				}
			}
		} while (!(tlacidlo()&& dlho>2000));			//Pre ukoncenie cyklu dekrementacie je potrebne tlacidlo drzat dlhsie ako 2s
		bzuciak(100);						//Kratkym tonom je uzivatel oboznameny s ukoncenym cyklu dekrementacie
	} while (!(tlacidlo()&& dlho>2000));				//Neustalym drzanim tlacidla sa dostavame von z cykla inkrementacie a dekrementacie
	
	bzuciak(300);							//Dlhym tonom je oznamene ukoncenie cykla a start casomiery
	CASOVAC_START();						//Start casovaca s periodou 1s
}

int main(void)
{
	//Inicializacne makra
	UART_DISABLE();							//Vypnutie alternativnej funkcie pinov(UARTu=RX,TX) PD0 a PD1
	SEG_INIT();							//Nastavenie vystupov multiplexovanej 7 segmentovky
	BUZ_INIT();							//Nastavenie vystupu bzuciaka
	PB_INIT();							//Nastavenie vstupu tlacidla
	
	//Nastavenie masiek preruseni pre pouzite casovace
	bit_set(TIMSK0,TOIE0);
	bit_set(TIMSK1,TOIE1);
	bit_set(TIMSK2,TOIE2);
	
	//Start casovaca s delickou 32 a periodou prerusenia 100us
	TCNT2 += 5;
	bits_set(TCCR2B,(bit(CS21)|bit(CS20)));
	
	//Globalne povolenie preruseni
	sei();
	
	//Po inicializacii sa spusta proces nastavenia casu casomiery
	nastavenie_casu();
    
	while(1)								        //Nekonecna slucka
    {
		if (sec_des == 0 && sec_jed == 0 && min_des == 0 && min_jed == 5)	//Pri 5. minute sa spusti kratky ton bzuciaka
		{
			if (TCNT1<6000)bzuciak(250);			
		}
		
		if (sec_des == 0 && sec_jed == 0 && min_des == 0 && min_jed == 1)	//Pri 1. minute sa spusti dlhsi ton bzuciaka
		{
			if (TCNT1<10000)bzuciak(500);
		}
		
		if (sec_des == 0 && sec_jed == 0 && min_des == 0 && min_jed == 0)	//Ked sa odpocet skonci,
		{
			bit_set(SEG_DIGIT_PORT,SEG_DIGIT_A1);				//Segmentovka sa vypne
			ZOBRAZENIE_STOP();
			bzuciak(5000);							//Bzuciak vydava dlhy ton
			CASOVAC_STOP();							//Casovac sekund sa vypne
			nastavenie_casu();						//Proces nastavovania casomiery zacina odznova
		}
		else if (sec_jed < 0)							//Ak je jednotiek sekund menej ako nula
		{
			sec_des--;							//Tak sa desiatky sekund dekrementuju
			sec_jed = 9;							//Pricom sa jednotky nastavia na 9
			if (sec_des < 0)					        //Ak je desiatok sekund menej ako nula nastavia sa na hodnotu 5
			{
				sec_des = 5;
				min_jed--;						//Pricom sa jednotky minut dekrementuju o jedno
				if (min_jed<0)						//V pripade ze su jednotky minut mensie ako nula,
				{
					min_jed=9;					//nastavia sa jednotky minut na hodnotu 9 a desiatky sa dekrementuju o jedno
					min_des--;
					if(min_des<0) min_des=0;			//Ak su desiatky minut mensie ako je zostavaju na nule
				}
			}
		}
		
    }
}

//Obsluhy preruseni
ISR(TIMER0_OVF_vect)
{
	TCNT0+=55;		//perioda 100us
	cas();
}
ISR(TIMER1_OVF_vect)
{
	TCNT1+=3035;		//perioda 1s
	sec_jed--;
}
ISR(TIMER2_OVF_vect)
{
	TCNT2+=5;		//perioda 500us 
	delay++;
}

Overenie

Po pripojení odpočítavacích hodín k zdroju napájania prostredníctvom USB alebo konektora "JACK" sa na displeji rozsvieti čas 10:00. Tento čas je možné pomocou tlačidla SET pod 7-segmentovým displejom nastaviť na ľubovoľnú hodnotu. Ak chceme čas na displeji nastaviť na vyššiu hodnotu ako je predvolená je potrebné stláčať tlačidlo SET v krátkych intervaloch.


Tlačidlo SET.


V prípade potreby nastavenia nižšej hodnoty je nutné tlačidlo SET pridržať po dobu asi 2 sekúnd. Po krátkom pípnutí pustite tlačidlo a začnite ho stláčať v krátkych intervaloch - hodnota na displeji sa bude zmenšovať. Po nastavení žiadanej hodnoty pridržte tlačidlo SET po dobu piatich sekúnd. Po dlhšom pípnutí sa začne odpočítavanie.

Posledných 5 minút je užívateľovi oznámených krátkym pípnutím. Pred odpočítavaním poslednej minúty je počuť dva-krát krátke bliknutie a displej počas zostávajúceho času bliká. Nakoniec zaznie dlhé pípnutie a displej je znovu nastavený na hodnotu 10:00.

V prípade problémov je možné odpočítavacie hodiny resetovať pomocou tlačidla RESET. Hodiny sa znovu nastavia na hodnotu 10:00.


Tlačidlo RESET.