Operácie

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

Z SensorWiki

(Analýza)
(Analýza)
 
(23 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 pínov, z ktorých môžem šesť použiť ako PWM výstupy, šesť analógových vstupov a 16 MHz kryštál. Pre pripojenie je možné využiť napájací konektor a USB pripojenie.
+
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 uvedenej schémy zapojenia.  
+
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.
 
Ostatné informácie o vývojovej doske ARDUINO DUEMILANOVE sú podrobne vypísané na nasledujúcej adrese http://arduino.cc/en/Main/arduinoBoardDuemilanove.
Riadok 42: Riadok 42:
 
             '''''Vývojová doska ARDUINO DUEMILANOVE'''''
 
             '''''Vývojová doska ARDUINO DUEMILANOVE'''''
  
[[Súbor:Duemilanove.jpg|400px|thumb|center|Vývojová doska ARDUINO DUEMILANOVE.]]
+
[[Súbor:Duemilanove.jpg|300px|thumb|center|Vývojová doska ARDUINO DUEMILANOVE.]]
  
  
Riadok 72: Riadok 72:
 
=== Algoritmus a program ===
 
=== Algoritmus a program ===
  
Uveďte stručný popis algoritmu, v akom jazyku a verzii vývojového prostredia ste ho vytvorili.  
+
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.
Je vhodné nakresliť aspoň hrubú štruktúru programu napríklad vo forme vývojového diagramu.  
 
Rozsiahly program pre lepšiu prehľadnosť rozdeľte do viacerých súborov.
 
  
Vyberte podstatné časti zdrojového kódu, použite na to prostredie ''source'':
+
 
 +
Zdrojový kód pre našu aplikáciu vyzerá nasledovne:
  
 
<source lang="c">
 
<source lang="c">
/* A nezabudnite zdroják hojne komentovať */
+
#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
 +
}
  
int main(void) {
+
//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();
 
      
 
      
     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.
 +
 
 +
 
 +
[[Súbor:SET_button.jpg|400px|thumb|center|Tlačidlo ''SET''.]]
 +
 
  
Zdrojový kód: [[Médiá:Serial.h|serial.h]] a [[Médiá:Pip.c|main.c]]
+
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.
  
[[Médiá:MojProgram.c|program.c]]
+
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.
  
=== Overenie ===
+
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.

7-segmentLEDdisplayShield.jpg

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.