Odpočítavacie hodiny na riadenie diskusie: Rozdiel medzi revíziami
Zo stránky SensorWiki
(38 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 32: | Riadok 32: | ||
== Analýza == | == 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:''' | '''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: | [[Súbor:MR7sd1.jpg|600px|center|thumb|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. | [http://www.atmel.com/Images/doc8161.pdf | ||
== Popis riešenia == | == Popis riešenia == | ||
'''Schéma zapojenia 7-segmentovky''' | '''Schéma zapojenia 7-segmentovky''' | ||
Riadok 67: | Riadok 70: | ||
=== 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) | |||
//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) { | 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++; | |||
} | } | ||
</source> | </source> | ||
=== 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. | |||
[[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. | |||
[[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:
- Ako pracuje 7-segmentový displej
- Princíp multiplexu
- Schéma zapojenia (.pdf)
- HD-M514RD datasheet
- Testovací program (.hex) - po zapnutí rozsvieti desatinné bodky a čaká na stlačenie S1. Potom zobrazí všetky číslice 0-9 a zhasne.
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 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
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.
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.