Odpočítavacie hodiny na riadenie diskusie
Zo stránky SensorWiki
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.
![](/sensorwiki/images/thumb/c/c0/7-segmentLEDdisplayShield.jpg/300px-7-segmentLEDdisplayShield.jpg)
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 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. K tejto doske je pripojená vývojová doska so 7-segmentovým displejom podľa uvedenej 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
![](/sensorwiki/images/thumb/8/85/Duemilanove.jpg/300px-Duemilanove.jpg)
Vývojová doska so 7-segmentovým displejom, bzučiakom a tlačidlami
![](/sensorwiki/images/thumb/d/db/MR7sd1.jpg/600px-MR7sd1.jpg)
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
![](/sensorwiki/images/thumb/1/11/Schema_zapojenia_7_segment_displeja.jpg/400px-Schema_zapojenia_7_segment_displeja.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:
/* 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
}
//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++;
}
Nezabudnite však nahrať aj kompletné zdrojové kódy vášho programu!
Zdrojový kód: serial.h a main.c
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.
![](/sensorwiki/images/thumb/3/3e/SET_button.jpg/400px-SET_button.jpg)
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 sa znovu nastaví 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.
![](/sensorwiki/images/thumb/a/ae/RESET_button.jpg/400px-RESET_button.jpg)