Porovnání RTC a SW hodin
Logo ústavu

Rostislav Vyroubal, Rostislav Nunvář UREL, FEEC, VUT Brno
xvyrou00stud.feec.vutbr.cz
xnunva00stud.feec.vutbr.cz

Obsah:

  1. Úvod
  2. Softwarové systémové hodiny s krystalem 16MHz
  3. Zdroj reálného času PCF8563T
  4. Zdrojový kód
  5. Realizace RTC hodin
  6. Závěr
  7. Literatura

Úvod

     Úkolem této práce je vytvořit program, který bude porovnávat přesnost softwarových hodin na ATmega16 [1] s krystalem 16MHz se zdrojem reálného času PCF8563T [2]. Cílem je zjistit, do jaké doby jsou jednotlivé hodiny schopny měřit přesný čas a po jaké době je jejich použití již nevhodné.


Softwarové systémové hodiny s krystalem 16MHz

     Vytvoření softwarových hodin je bezesporu hardwarově nejméně náročná varianta. Stačí, když mikroprocesor používá jako zdroj systémových hodin externí krystal fosc = 16MHz. Tato metoda byla vyzkoušena a byla zjištěna odchylka několik sekund již po dvanácti hodinách běhu programu. Ve vývojovém studiu byl program odladěn na jmenovitou hodnotu krystalu fosc = 16MHz. Bylo provedeno srovnání přesnosti softwarových hodin s RTC, z pokusu bylo patrno, jak moc je krystal nepřesný. Výrobce udává toleranci frekvence Δf = ±50ppm, to odpovídá rozsahu kmitočtů od f = 15999200Hz  do f = 16000800Hz. Další nevýhodou je spotřeba mikroprocesoru. Jednou za sekundu se provedou instrukce příslušné hodinám a zbytek času tráví mikroprocesor v čekací smyčce. Při ztrátě napájení dojde ke ztrátě reálného času, tudíž tato varianta nesplňuje stanovené požadavky.



Zdroj reálného času PCF8563T

     Integrovaný obvod PCF8563T [2] je kalendář, hodiny a budík. Obvod je zaměřen na minimální spotřebu, typický proudový odběr má I = 0,25μA při Ucc = 3V. Je v provedení osmi-pinového SMD pouzdra SOT96, což zaručuje velmi dobrou miniaturizaci. Interface pro komunikaci je sběrnice I2C s maximální rychlostí 400kbit/s. Adresa pro čtení je 0xA3 a pro zápis 0xA2. Pro funkční komunikaci je nutné mít napájení obvodu v rozmezí Ucc = 1,8V až Ucc = 5,5V. Pro udržení reálného času musí být napájení Ucc = 1,0V až Ucc = 5,5V. Vývod 7, CLKOUT, generuje kmitočty f = 32,768kHz, f = 1024Hz, f = 32Hz a f = 1Hz. Jako oscilátor je použit krystal fosc = 32,768kHz. Výrobce udává maximální odchylku ±5 minut za rok. Oscilátor je možné jemně doladit kapacitním trimrem. Ze schéma zapojení je patrné že kondenzátor C2 plní funkci záložního napájení při odpojení Vcc. Záložní kondenzátor má kapacitu C = 1F. Záložní kondenzátory mají velmi malé samovybíjení, obvykle výrobce udává, při nabití na U = 5V, pokles napětí menší než ΔU = 1V za 24 hodin při pokojové teplotě. Integrovaný obvod PCF8563 je běžně dostupný a laciný. Jeho aplikace je vhodná všude, kde dochází k výpadkům napájení. Lze jej také použít jako generátor přesného kmitočtu.

schema RTC
Obrázek 1. Schéma RTC obvodu

Zdrojový kód

/*
********************************************************************************
** Nazev souboru   : MMIA1.c
** Mikrokontroler  : ATmega16
** Popis           : Srovnani hodin s PCF8563 a SW hodin s 16MHz krystalem
** Autor           : Rostislav Vyroubal, Rostislav Nunvar
** Predmet         : Mikroprocesorova technika
** Posledni zmena  : 19.4.2010
********************************************************************************
*/
/******************************************************************************* 
pouzite veci																	 
-	krystal 16MHz															   
-	funkce																	   
-	pouziva 16 bit citac1													   
	fosc= fclk/(2*N*(1+OCR)) ; N= 1 8 32 64 128 256 1024					   
	fosc= 16000000/(2*256*(1+31249))=1Hz									   
-	pocet cyklu citace pro jednu sekundu je 15624 = 0011 1101 0000 1000		   
********************************************************************************
*/

#include 	"lcd_h.h"   
#include 	"lcd_c.c"
#include 	<avr/io.h>
#include 	<util/delay.h>
#include	<avr/interrupt.h>
#define 	F_CPU 16000000UL
#define 	PCF8563	0xA2 	// adresa PCF8563
#define 	I2C_READ   1    // PCF8563 cteni
#define  	I2C_WRITE   0   // PCF8563 zapis
#define		sekundyRTC		0x02
#define		minutyRTC		0x03
#define		hodinyRTC		0x04
#define		dnyRTC			0x05
#define		den_v_tydnuRTC	        0x06
#define		mesiceRTC		0x07
#define		rokyRTC			0x08
char 	        buffer1[20];
char 	        rozdil;
unsigned char   DEC;
// promenny pro RTC
unsigned int 	BCD;
unsigned char 	sekundy;
unsigned char 	minuty;
unsigned char 	hodiny;
unsigned char 	dny;
unsigned char 	den_v_tydnu;
unsigned char 	mesice;
unsigned char 	roky;

  // promenné pro SW hodiny
char 	sekunda	=0;
char 	minuta	=0;
char 	hodina	=0;
char 	den     =0;
char 	mesic	=0;
int  	rok     =0;

unsigned char read(bunka)
{
	i2c_start_wait(PCF8563+I2C_WRITE);      // zapis do PCF8563
	i2c_write(bunka);                       // adresa bunky
	i2c_rep_start(PCF8563+I2C_READ);       // cteni z PCF8563
	BCD=i2c_readNak();						
	i2c_stop();		               // stop
};

unsigned char write(DEC,bunka)
{	
	//	Prevede DEC do BCD
	BCD = ((((DEC/10) <<4) & 0xF0) | ((DEC% 10) & 0x0F));
	i2c_start_wait(PCF8563+I2C_WRITE);   	  									   	
        i2c_write(bunka);	        // 	vyber adresy( hodiny minuty atd) 											   
	
        i2c_write(BCD);		// 	zapise BCD na vybranou adresu							   
	i2c_stop(); 		//	stop
};



ISR(TIMER1_COMPA_vect)
	{
//      Zacatek RTC hodin
	read(rokyRTC);								         // cte roky z RTC
	roky = (((BCD & 0xf0)>>4)*10 + (BCD & 0x0f));
	read(mesiceRTC);								// cte mesice z RTC
	mesice = ((((BCD & 0b10011111) & 0xf0)>>4)*10 + (BCD & 0x0f));
	read(den_v_tydnuRTC);						               // cte tydny z RTC
	den_v_tydnu = BCD & 0b00000111;		
	read(dnyRTC);								         // cte dny z RTC
	dny = ((((BCD & 0b00111111) & 0xf0)>>4)*10 + (BCD & 0x0f));	
	read(hodinyRTC);								// cte hodiny z RTC
	hodiny = ((((BCD & 0b00111111) & 0xf0)>>4)*10 + (BCD & 0x0f));
	read(minutyRTC);								// cte minuty z RTC
	minuty = ((((BCD & 0b01111111) & 0xf0)>>4)*10 + (BCD & 0x0f));
	read(sekundyRTC);								// cte sekundy z RTC
	sekundy = ((((BCD & 0b01111111) & 0xf0)>>4)*10 + (BCD & 0x0f)) ;  
//	Konec RTC hodin
//	Zacatek SW hodin
	sekunda++;
	if (sekunda>=60)
		{
		sekunda=0;
		minuta++;
		};
	if (minuta>=60)
		{
		minuta=0;
		hodina++;
		};
	if (hodina>=24)
		{
		den++;
		hodina=0;
		};
	switch (mesic)
		{
		case 1:
			if (den==31)
				{
				den=1;
				mesic++;
				};
			break;
		case 2:
			if ( (rok % 4)==0 )
				{		//zbytek po deleni 4 je 0 
				if (den==30)
					{
					den=1;
					mesic++;
					}
				}
			else{
				if (den==29)
					{
					den=1;
					mesic++;
					};	
				};
			break;
		case 3:
			if (den==31)
				{
				den=1;
				mesic++;
				};
			break;
		case 4:	
			if (den==30)
				{
				den=1;
				mesic++;
				};
			break;
		case 5:			
			if (den==31)
				{
				den=1;
				mesic++;
				};
			break;
		case 6:
			if (den==30)
				{
				den=1;
				mesic++;
				};
			break;
		case 7:
			if (den==31)
				{
				den=1;
				mesic++;
				};
			break;
		case 8:
			if (den==31)
				{
				den=1;
				mesic++;
				};
			break;
		case 9:
			if (den==30)
				{
				den=1;
				mesic++;
				};
			break;
		case 10:
			if (den==31)
				{
				den=1;
				mesic++;
				};
			break;
		case 11:
			if (den==30)
				{
				den=1;
				mesic++;
				};
			break;
		case 12:
			if (den>=31)
				den=1;
				mesic=1;
				rok++;  		
			break;
		};
//	Konec SW hodin	
	rozdil = sekunda - sekundy;	// sekunda patří SW sekundy RTC
//	Zacatek zapisu na LCD
	lcd_gotoxy(0,0);
	sprintf(buffer1,"RTC:%u:%u:%u       ",hodiny,minuty,sekundy);
	lcd_puts(buffer1);
	sprintf(buffer1,"SW: %u:%u:%u ", hodina, minuta, sekunda);
	lcd_gotoxy(0,1);
	lcd_puts(buffer1);
	sprintf(buffer1,"D:%u W:%u M:%u R:20%u   ",dny,den_v_tydnu, mesice, roky);
	lcd_gotoxy(0,2);
	lcd_puts(buffer1);
	sprintf(buffer1,"Rozdil sekund: %d   ", rozdil);
	lcd_gotoxy(0,3);
	lcd_puts(buffer1);
//      konec zapisu na lcd
	};
int main(void)
{
	DDRA=0b00000000;       // PORTA vstupni
	TCCR1A= 0b00000000;    // konfigurace c/c
	TCCR1B= 0b00001101;    //konfigurace c/c
	TIMSK = (1<<OCIE1A);  //povoleni preruseni	
	OCR1AH =0b00111101;    //pocet cyklu citace pro jednu sekundu (15624)
	OCR1AL =0b00001000;
	sei();		      // povoleni globalniho preruseni
	i2c_init();            // inicializace I2C
	lcd_init();	      // inicializace LCD
//* Konfigurace hodin**********************************************************	
	write(10,rokyRTC);	// Rok je od 00 do 99
	write(4,mesiceRTC);
	write(1,den_v_tydnuRTC);//0 PO, 1 Ut, ...
	write(20,dnyRTC);
	write(21,hodinyRTC);	// Bohuzel PCF8563 umoznuje mit den s 32hodinami:-)
	write(35,minutyRTC);
	write(0,sekundyRTC);
//****************************************************************************
//
	// Na zacatku ulozime cas z RTC do SW hodin
	read(hodinyRTC);	// cte hodiny z RTC
	// prevod bcd do bin s osetrenim ignorovanych bitu
	hodina = ((((BCD & 0b00111111) & 0xf0)>>4)*10 + (BCD & 0x0f));
	read(minutyRTC);	// cte minuty z RTC
	minuta = ((((BCD & 0b01111111) & 0xf0)>>4)*10 + (BCD & 0x0f));
	read(sekundyRTC);	// cte sekundy z RTC
	sekunda = ((((BCD & 0b01111111) & 0xf0)>>4)*10 + (BCD & 0x0f)) ;  
	
	while(1)	        // cekaci smycka
	{
	if (bit_is_clear(PINA,PA7)) // Manualni reinicializace lcd pri jeho odpojeni 
		  {	  
		  	lcd_init();
			lcd_clrscr();
			lcd_puts("Loading");
		  };
		  asm ("nop");
		  asm ("nop");
	};
  }
  

Realizace RTC hodin

     Hodiny RTC byly zapojeny na nepájivém poli dle schéma. Softwarové hodiny byly na kitu pod LCD (obr. 3). Tlačítka sloužily pouze pro manuální inicializaci LCD. LCD není inicalizováno automaticky a při dlouhodobém testu je vhodné odpojit display kvůli spotřebě. Se zapnutým LCD a bateriemi GP2700mAh vydrží hodiny běžet až 40 hodin, poté je zapotřebí baterie vyměnit, ale pozor, je nutné neustále napájet celý kit. Obrázek 2 znázorňuje provedený pokus po 2 dnech a 3:40:12 viz obr. 2. Jako referenční hodnotu bereme RTC, protože je přesnější. Je patrné, že po uvedeném čase se SW a RTC hodiny rozejdou o 23 sekund. Z toho plyne, že SW hodiny se předbíhají. Při pozorování pokusu se první sekunda, representující nepřesnost, objevila již po dvou hodinách běhu aplikace.
     Školní knihovna pro lcd byla optimalizována pouze na LCD 2x16 znaků. Použité LCD je ovšem 4x20 znaků. Knihovnu bylo zapotřebí upravit, protože zobrazení s původními knihovnami bylo nekorektní.
realizace
Obrázek 2. Realizace

kit
Obrázek 3. Kit s mikroprocesorem ATmega16

Závěr

   Pro měření krátkých časových úseků SW hodiny dostačují. Při čítání pulzů po dobu několika minut až hodin jsou SW hodiny nevhodné. Měření se nám líbilo :-)

Soubory zdrojových kódů a upravenou knihovnu LCD je možné stáhnout zde.

Literatura

[1] ATmega16 [online], [cit. 27. 4. 2010]. Dostupné na WWW:
       http://www.gme.cz/_dokumentace/dokumenty/958/958-099/dsh.958-099.1.pdf

[2] PCF8563 [online], [cit. 27. 4. 2010]. Dostupné na WWW:
       http://www.pira.cz/pdf/PCF8563.pdf

[3] VÁŇA, V., Mikrokontroléry ATMEL AVR-Programování v jazyce C. Praha: BEN - technická literatura, 2003. 206 s. ISBN 80-7300-102-0