Operácie

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

Zo stránky SensorWiki

StudentDVPS (diskusia | príspevky)
StudentDVPS (diskusia | príspevky)
 
(42 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:Duemilanove.jpg|400px|thumb|center|Vývojová doska ARDUINO DUEMILANOVE.]]
[[Súbor:MR7sd1.jpg|600px|center|thumb|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?


[[Súbor:MR7sd1.jpg|600px|center|thumb|Vývojová doska so 7 segmentovým displejom, bzučiakom a tlačidlami.]]
            '''''Procesor Atmel ATmega 328P - použitý na vývojovej doske ARDUINO DUEMILANOVE'''''


Katalógové listy
            [http://www.atmel.com/Images/doc8161.pdf
[[Médiá:Súbor.ogg]]


== 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 68: Riadok 70:




[[Súbor:Example.jpg]]
=== 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:
 
<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)


Pozn.: Názov obrázku musí byť jedinečný, uvedomte si, že Obr1.jpg už pred vami skúsilo
//Tlacidlo
nahrať už aspoň 10 študentov.
#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


=== Algoritmus a program ===
//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 ===


Zdrojový kód: [[Médiá:Serial.h|serial.h]] a [[Médiá:Pip.c|main.c]]
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.  


[[Médiá:MojProgram.c|program.c]]


=== Overenie ===
[[Súbor:SET_button.jpg|400px|thumb|center|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.


Nezabudnite napísať čosi ako užívateľský návod. Z neho by malo byť jasné čo program robí,
ako sa prejavuje a aké má užívateľské rozhranie (čo treba stlačiť, čo sa kde zobrazuje).
Ak ste namerali nejaké signály, sem s nimi. Ak je výsledkom nejaký údaj na displeji,
odfotografujte ho.


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.