Spínací hodiny


Bc. Jiří Christen UREL, FEKT, VUT Brno
xchris01@stud.feec.vutbr.cz

Obsah

  1. Úvod
  2. Realizace
  3. Program pro mikrokontrolér
  4. Závěr
  5. Použitá literatura

Úvod

Cílem projektu bylo navrhnout digitální programovatelný časový spínač pro řízení provozu elektrických zařízení. Časový spínač nachází uplatnění v řadě míst, kde je třeba ohlídat čas. Například při různých výrobních procesech. Maximální časový údaj, který lze nastavit, je 99 hodin, 59 minut a 59 sekund.


Realizace

Obvodové zapojení časovače je na obr.1. Jeho jádrem je naprogramovaný mikrokontrolér ATmega16 od firmy Atmel, který obsluhuje veškeré periférie. MCU je taktován krystalem o frekvenci 16 MHz a k jeho naprogramování slouží konektor ISP . Spínaný obvod je připojen pomocí svorkovnice K1. Silový výstup je vyřešen pomocí 5 V relé s maximálním proudem až 8 A. Toto relé je spínáno tranzistorem T1, který je připojen na pin PC5 mikrokontroléru. Zda je relé sepnuté je opticky signalizováno LED diodou D6. Při rozepnutí výstupního kontaktu dojde po dobu jedné sekundy k akustické signalizaci piezoměničem s rezonančním kmitočet přibližně 4 kHz, který je připojen na pin PD7. Na portu D jsou dále připojeny tři LED diody D2 - D4 a čtyři tlačítka SW2 - SW5. LEDky nejsou použity, ale připraveny na pozdější využití.

Ovládání časovače je vyřešeno pomocí pěti tlačítek SW1 až SW5. Jejich funkce je následující:


Kompletní zapojení spínače
Obr. 1: Kompletní zapojení spínače

O zobrazení uživatelských informací se stará alfanumerický displej MC1602E-TRV o 16x2 znacích obsahující řadič HD44780 od Hitachi. LCD je připojeno na port C a komunikace probíhá ve 4-bitovém režimu, což umožňuje ušetřit čtyři piny MCU a rovněž jednodušší návrh DPS. Trimr P1 slouží pro nastavení kontrastu zobrazení. Na LCD se při provozu zobrazuje stav výstupního konstaktu (ON nebo OFF), aktuální čas zbývající do vypnutí a také formát zobrazovaných hodin. Na obr.2 je ukázán návrh DPS a rozložení periférií na desce.

Napájecí napětí 12 V je na desku plošného spoje přivedeno ze síťového adaptéru na konektor K2. Toto napětí je dále sníženo na 5 V stabilizátorem 7805. Přítomnost napětí signalizuje LED dioda D5.


Navržená DPS
Obr. 2: Konstrukční řešení

Program pro mikrokontrolér


Pro řízení LCD byla použita upravená knihovna od Tomáše Frýzy (odkaz).

// Knihovny--------------------------------------------------------------------------------------------------------------------------------------

#define F_CPU 16000000UL		        // kmitočet hodin
#include <avr/io.h>				// hlavičkový soubor mikrokontroléru
#include "lcd_h.h"				// hlavičkový soubor pro LCD
#include <stdio.h>				// standartní I/O knihovna
#include <avr/interrupt.h>		        // hlavičkový soubor pro přerušení
#include <util/delay.h>				// hlavičkový soubor pro zpoždění


// Definice řetězců -----------------------------------------------------------------------------------------------------------------------------

 unsigned char MSG1[12] = "Device is: ";
 unsigned char MSG2[4] = "OFF";
 unsigned char MSG3[4] = "ON ";
 unsigned char MSG6[6] = "H:M:S";
 unsigned char MSG_pom[4];


// Definice globálních proměnných ---------------------------------------------------------------------------------------------------------------

 unsigned char sek, sek1 = 0;						// sekundy
 unsigned char min, min1 = 0;						// minuty
 unsigned char hod, hod1 = 0;						// hodiny
 unsigned char pom1, pom2, pom3, pom4, pom5, pom6, pom7 = 0;		// pomocné proměnné 
 unsigned char kurzor_x = 0;						// horizontální souřadnice kurzoru - použito při nastavování časovače 
 unsigned char inkr = 0;						// proměnná pro inkrementaci cifer časovače


 // Prototypy funkcí -----------------------------------------------------------------------------------------------------------------------------

 void piezo(void);							        // sepnutí piezoměniče na 1s
 void LCD_show( char kur_x, char kur_y, char cislo1, char cislo2, char str);	// zobrazení dvoučíslí na LCD
										// kur_x - horizontální pozice kurzoru, kur_y - vertikální pozice kurzoru
										// cislo1 - první z dvoučísel, cislo2 - druhé z dvoučísel
										// str - pomocná proměnná pro rozlišení formátu zobrazení


// Obsluha přerušení při přetečení čítače1 ------------------------------------------------------------------------------------------------------

 ISR (TIMER1_OVF_vect){			// obsluha slouží pro dekrementaci stavu digitálního časovače		 
	
	TCNT1 = 0x0BDC;			// počáteční hodnota čítače1 (aby přetekl za 1s)
	
	if(sek == 0){ 	 			
		if(sek1==0 && sek==0){		 			
			if( min1 == 0 &&  min == 0){ 
				if( hod == 0){ 				
					if( hod1==0 &&  hod==0){ 
						 
						hod1=10;				
					}	
					hod1--;		
					hod=10;				
				}	
				  		  
				min1=5;
				min=10;
				hod--;  			  				  
			}			
			
			if(min1 > 0 && min == 0 ){
			  
				min1--;
				min=10;  			  				  
			}	
			 	
			if(min1 == 0 && min > 0){
			  
				min1=0;
				min--;  			  				  
			}	
			  
			if(min1 > 0 && min > 0){
						    
				min--;  			  				  
			}							  	 						 			
			sek1=6;
		}	
	 	sek1--;		
	 	sek=10;				
	}		 
	  	 
	sek--;	 	   
 }


// Obsluha přerušení při přetečení čítače0 ------------------------------------------------------------------------------------------------------

 ISR (TIMER0_OVF_vect){					// blikání vybrané cifry na displeji při nastavování počátečního stavu časovače
	 
	 if (pom7 != 20 )pom7++;			// blikání s periodou přibližně 320 ms
			 	 
	 else{
		if (pom6 == 0) pom6 = 1;		// střídavé zobrazení požadované cifry a prázdného znaku - blikání cifry
		else pom6 = 0;				// pokud je pom6=0 dojde k zobrazení nastavené hodnoty, jinak prázdný znak
		pom7 = 0;	
	 }	  
 }


// Obsluha externího přerušení od tlačítka ENTER -----------------------------------------------------------------------------------------------

 ISR (INT0_vect){					// inkrementace vybrané cifry - při nastavování časovače
	
	_delay_ms(50);					//
	if(bit_is_clear(PIND,2)){			// ošetření zákmitů tlačítka
			
		if(kurzor_x == 3){			// kurzor na čtvrté pozici - desítky minut
			if(inkr == 5) inkr=0;		// desítky minut max do hodnoty 5, pak nulovat
			else inkr++;			// jinak inkrementuj
							
		}
		else if(kurzor_x == 6){			// kurzor na šesté pozici - desítky sekund
			if(inkr == 5) inkr=0;		// desítky sekund max do hodnoty 5, pak nulovat
			else inkr++;			// jinak inkrementuj
							
		}
		else{
			if(inkr == 9) inkr=0;		// na jiných pozicích kurzoru je max hodnota 9, pak nulování
			else inkr++;			// jinak inkrementuj 				
		}											
	}				
}



// Obsluha externího přerušení od tlačítka START/STOP ------------------------------------------------------------------------------------------

ISR (INT1_vect)	{					// spouštění a zastavování digitálního časovače
	
	_delay_ms(50);					//
	if(bit_is_clear(PIND,3)){			// ošetření zákmitů tlačítka
		if (pom5 == 0){
			TCCR1B |= (1 << CS12);		// zapnutí čítače1 - předělička 256
			pom5 = 1;	
		}
		else {
			TCCR1B = 0x00;			// vypnutí čítače1
			pom5 = 0;
		}		
	}
}

// Hlavní funkce main --------------------------------------------------------------------------------------------------------------------------

int main(void){							// hlavní funkce main
	
	DDRD = 0x80;						// port D vstupní, pouze PD7 výstupní
	DDRC |= (1 << PINC5);					// relé na portu C pin 5
	PORTC = 0x00;						// na výstupní piny portu C -> 0 (relé vyplé)
	MCUCR |= (1 << ISC11)|(1 << ISC01);			// externí přerušení INT1, INT0 od tlačítka při sestupné hraně
	GICR  |= (1 << INT1)|(1 << INT0);			// povolení externího přerušení INT1 a INT0						
	TCCR2 |= (1 << WGM21)|(1 << CS22)|(1 << CS21);		// CTC režim čítače 2 (toggle), předdělička 256
	TCCR1A = 0x00;						// normální režim čítače1
	TCCR1B = 0x00;						// vypnutý čítač1
	TCCR0 |= (1 << CS02)|(1 << CS00);			// zapnutý čítač0 - normální režim, předdělička 1024, přetečení - asi 16ms
	TIMSK |= (1 << TOIE1)|(1 << TOIE0);			// povolení přerušení při přetečení 16bit čítače1 a 8bit čítače0 
	TCNT1 = 0x0BDC;						// počáteční hodnota čítače1 - přetečení za 1s				
	OCR2 = 0x07;						// konstanta pro CTC režim čítače2 - obdélník 4 kHz(pro piezoměnič)	
	
	lcd_init ( );						// inicializace displeje
	lcd_clrscr ( );						// smazání obsahu displeje

	lcd_gotoxy(0,0);					// kurzor na pozici 0,0 - 1. řádek, 1. sloupec
	lcd_puts(MSG1);						// zobrazení řetězce MSG1
	
	lcd_gotoxy(11,0);					// kurzor na pozici 0,0 - 1. řádek, 12. sloupec
	lcd_puts(MSG2);						// zobrazení řetězce MSG2
		
	lcd_gotoxy(9,1);					// kurzor na pozici 0,0 - 2. řádek, 10. sloupec
	lcd_puts(MSG6);						// zobrazení řetězce MSG6
		
	LCD_show( 0, 1, hod1, hod, 1);				// zobrazení hodin na pozici 0,1												
	LCD_show( 3, 1, min1, min, 1);				// zobrazení minut na pozici 3,1
	LCD_show( 6, 1, sek1, sek, 0);				// zobrazení sekund na pozici 7,1
		
	sei ();							// povolení globálního přerušení


// Nastavování počátečeního stavu digitálního časovače -----------------------------------------------------------------------------------------

	while(kurzor_x != 8){					// smyčka se provádí dokud není kurzor na pozici 8 - až se nastaví všechny
								// cifry časovače	
		if(bit_is_clear(PIND,0) || pom3 == 1 ){		// testování zda je stiknuto tlačítko ON/OFF
			_delay_ms(100);				//
			pom3 = 1;				//
			if(bit_is_set(PIND,0)){			//
				_delay_ms(20);			// ošetření zákmitů při stisku tlačítka ON/OFF 
				PORTC ^= 0b00100000;		// při každém stisku tlačítka je relé bud vypnuto nebo sepnuto		
				pom3 = 0;
			}
		}
		
		if (PORTC == 0b00100000){			// pokud je relé sepnuté, na displeji se zobrazí ON
			lcd_gotoxy(11,0);
			lcd_puts(MSG3);
		}				
		else if (PORTC == 0b00000000){			// pokud je relé vypnuté, na displeji se zobrazí OFF
			lcd_gotoxy(11,0);
			lcd_puts(MSG2);
		}					

		if(bit_is_clear(PIND,1) || pom4 == 1 ){			// testování zda je stiknuto tlačítko SELECT
			_delay_ms(100);					//
			pom4 = 1;					//
			if(bit_is_set(PIND,1)){				//
				_delay_ms(20);				// ošetření zákmitů při stisku tlačítka SELECT
				LCD_show( kurzor_x, 1, inkr, 5, 2);	// zajistí zobrazení poslední nastavené hodnoty - sekundy (sek)
				kurzor_x++;				// přesunutí kurzoru o jednu pozici doprava - další cifra
				inkr=0;					// počáteční hodnota cifry při nastavování 	
				pom4 = 0;
			}
		}
		
		if(kurzor_x == 2) kurzor_x = 3;				// pokud je pozice kurzoru 2, přesun na pozici 3 - vynechání dvojtečky
		if(kurzor_x == 5) kurzor_x = 6;				// pokud je pozice kurzoru 5, přesun na pozici 6 - vynechání dvojtečky
		
		switch (kurzor_x){					// podle pozice kurzoru se do proměnných, které budou následně
									// dekrementovány, uloží nastavené hodnoty 			
			case (0): hod1 = inkr; break;
			case (1): hod  = inkr; break;
			case (3): min1 = inkr; break;
			case (4): min  = inkr; break;
			case (6): sek1 = inkr; break;
			case (7): sek  = inkr; break;
								
		}
				
		if(kurzor_x != 8){					// provádí se dokud se nenastaví všechny cifry
			if(pom6 == 0){					// k zobrazení nastavené hodnoty dojde jen když pom6=0
				LCD_show( kurzor_x, 1, inkr, 5, 2);	// zobrazení nastavené hodnoty (inkr) na pozici kurzor_x,1
			}
			else{
				lcd_gotoxy(kurzor_x,1);			// pokud pom6=1 zobrazí se prázdný znak
				sprintf(MSG_pom, " ");
				lcd_puts(MSG_pom);
			}
		}		
    	}


// Dekrementace časovače -----------------------------------------------------------------------------------------------------------------------
		
	TCCR0 = 0x00;							// vypnutí čítače0
	while (1)							// nekonečná smyčka, kde dochází k dekrementaci časovače a aktualizaci jeho  
	{								// stavu na LCD
		if(bit_is_clear(PIND,0) || pom2 == 1 ){   		// testování zda je stisknuto tlačítko ON/OFF
			_delay_ms(100);					//
			pom2 = 1;					//
			if(bit_is_set(PIND,0)){    			// 
				_delay_ms(20);				// ošetření zákmitů při stisku tlačítka ON/OFF
				PORTC ^= 0b00100000;			// při každém stisku tlačítka je relé bud vypnuto nebo sepnuto												
				pom2 = 0;
			}
		}
		
		if (PORTC == 0b00100000){				// pokud je relé sepnuté, na displeji se zobrazí ON
			lcd_gotoxy(11,0);
			lcd_puts(MSG3);
		}				
		else if (PORTC == 0b00000000){				// pokud je relé vypnuté, na displeji se zobrazí OFF
			lcd_gotoxy(11,0);
			lcd_puts(MSG2);
		}			

		LCD_show( 0, 1, hod1, hod, 1);				// zobrazení hodin na pozici 0,1
		LCD_show( 3, 1, min1, min, 1);				// zobrazení minut na pozici 3,1
		LCD_show( 6, 1, sek1, sek, 0);				// zobrazení sekund na pozici 6,1	
					
		if(sek1 == 0 && sek == 0 && min1 == 0 && min == 0 && hod1 == 0 && hod == 0) // pokud je časovač ve stavu 00:00:00, tak se provede jeho zastavení
		{		
			TCCR1B = 0x00;					// vypnutí čítače1 - zastaví dekrementaci časovače
			if (pom1 == 0){					// vypnutí relé a pípnutí piezoměniče se provede pouze jednou
				LCD_show( 0, 1, hod1, hod, 1);		// zobrazení hodin na pozici 0,1
				LCD_show( 3, 1, min1, min, 1);		// zobrazení minut na pozici 3,1
				LCD_show( 6, 1, sek1, sek, 0);		// zobrazení sekund na pozici 6,1	
				PORTC = 0x00;				// vypnutí relé
				lcd_gotoxy(11,0);			// kurzor na pozici 11,0 - 1. řádek, 12. sloupec 
				lcd_puts(MSG2);				// zobrazení řetězce MSG2 na displeji
				piezo();				// volání funkce pro piezoměnič - akustická indikace vypnutí časovače			
				pom1 = 1;
			}
		}						
	}			
} // Konec funkce main


//Definice funkcí ------------------------------------------------------------------------------------------------------------------------------

void piezo(void){							// funkce piezoměniče

	TCCR2 |= 0b00010000;						// zapnutí piezoměniče
	char m = 4;																	
	while (m>0){							// sepnutí piezoměniče na 250ms x 4 = 1s	
		_delay_ms(250);						// zpoždění 250 ms
		m--;																	
	} 	
	TCCR2 &= 0b11101111;						// vypnutí piezoměniče
}


void LCD_show( char kur_x, char kur_y, char cislo1, char cislo2, char str){		// funkce pro zobrazení doučíslí na displeji
											// proměnná str pro rozlišení požadovaného formátu zobrazení - 3 možnosti
	char cifra [4];									// lokální proměnná 
	 
	if (str == 0){									// zobrazení doučíslí bez dvojtečky
		lcd_gotoxy(kur_x,kur_y);						// kurzor na pozici kur_x a kur_y	
		sprintf(cifra, "%d%d",cislo1, cislo2);					// do proměnné cifra uloží řetězec znaků
		lcd_puts(cifra);							// zobraz na displeji řetězec cifra
	}
	
	if (str == 1){									// zobrazení doučíslí s dvojtečkou
		lcd_gotoxy(kur_x,kur_y);						// kurzor na pozici kur_x a kur_y
		sprintf(cifra, "%d%d:",cislo1, cislo2);					// do proměnné cifra uloží řetězec znaků
		lcd_puts(cifra);							// zobraz na displeji řetězec cifra
	}	
	
	if (str == 2){									// možnost zobrazení pouze jedné cifry
		lcd_gotoxy(kur_x,kur_y);						// kurzor na pozici kur_x a kur_y	
		sprintf(cifra, "%d",cislo1);						// do proměnné cifra uloží řetězec znaků
		lcd_puts(cifra);							// zobraz na displeji řetězec cifra
	}				
}


Závěr

Digitální časový spínač se podařilo realizovat dle předem zadaných požadavků. Jeho přesnost závisí zejména na stabilitě taktovacího kmitočtu MCU. Pro jednoduché aplikace však plně dostačuje. Časovač by bylo možné rozšířit například o obvod RTC, který by dovoloval naprogramovat spínač tak, aby se výstupní konstakt sepl a rozepl v určitou hodinu, den, měsíc nebo rok.

Použitá literatura


[1] FRÝZA T., FEDRA Z., ŠEBESTA. J. Mikroprocesorová technika - Laboratorní cvičení [online].[cit. 2013-20-04]. Dostupné z WWW: http://www.urel.feec.vutbr.cz/~fryza/downloads/mpt_pocitace_full.pdf
[2] ATMEL ATmega16 datasheet.[online].[cit. 2013-20-04]. Dostupné z WWW: http://www.atmel.com/Images/doc2466.pdf
[3] HITACHI HD44780 Controller datasheet.[online].[cit. 2013-20-04]. Dostupné z WWW: http://www.crystalfontz.com/controllers/Hitachi_HD44780.pdf
[4] PANDATRON Ovládání znakových LCD s řadičem HD44780 – 1. díl.[online].[cit. 2013-20-04]. Dostupné z WWW: http://pandatron.cz/?685&ovladani_znakovych_lcd_s_radicem_hd44780_%96_1._dil
[5] Programmable digital timer switch using a PIC Microcontroller.[online].[cit. 2013-20-04]. Dostupné z WWW: http://embedded-lab.com/blog/?p=1378