Odpočítavacie hodiny na riadenie diskusie

Z SensorWiki
Prejsť na: navigácia, hľadanie
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.