Operácie

Zbernica i2c: hodiny RTC.

Zo stránky SensorWiki

Autori: Pavol Lukáč, Ivan Jankovič
Študijný odbor: Aplikovaná mechatronika 2. Ing. (2014/2015)

Zadanie

  • Zobrazte na LCD displeji reálny čas z RTC obvodu PCF8583. Nastavovanie a prepínanie údaja tlačidlami.

Literatúra

Zoznam použitej literatúry, vrátane katalógových údajov (datasheet), internetových odkazov a pod.:

Analýza

Našou úlohou je navrhnúť hodiny reálneho času pomocou obvodu PCF8583 a LCD modulu s tlačidlami. Tlačidlami pod displejom sa majú dať nastavovať hodiny (teda hodiny, minúty, sekundy). Takisto po stlačení jedného z tlačidiel sa hodiny prepnú na dátum, ktorý sa tiež dá nastaviť. Na komunikáciu medzi mikroprocesorom a čipom PCF8583 použijeme I2C zbernicu, ktorá je pre tento čip štandardom. Na programovanie adries čipu nám bude slúžiť pin A0. Ako mikroprocesor využijeme Atmegu 328P, ktorý je súčasťou vývojovej dosky Acrob.

PCF8583

Vlastnosti a výhody:

  • Základ tvorí CMOS RAM 256 B (2048b)
  • napájacie napätie i2c zbernice 2,5 V až 6 V
  • 240 x 8 bitová nízko-napäťová pamäť
    • 8 bitová pamäť pre (hodiny, kalendár)
    • 8 bitová pamäť pre (alarm)
  • operačný prúd (pri f(scl)=0 Hz) maximálne 50 uA
  • funkcia hodín so štvorročným kalendárom
  • časovač s alarmom, indikácia pretečenia
  • 12 alebo 24 hodinový formát času
  • potreba 32,768 kHz kryštálu
  • dvojvodičová zbernica i2c
  • automatická inkrementácia adresy po zápise/čítaní
  • programovateľný alarm, časovač a prerušovacie funkcie
  • adresa slave-u A1h alebo A3h pre čítanie, A0h alebo A2h pre zápis (podľa stavu pinu A0)+

Rozloženie a popis pinov PCF8583

Zbernica I2C

Skratka pre zbernicu I2C je IIC(Inter-Integrated Circuit). Protokol a zbernicu vyvinula firma Philips Semiconductor pôvodne pre svoje TV príjímače v roku 1980 za účelom komunikácie medzi IO na jednej DPS pri použití minimálneho množstva pinov.

Špecifikácia zbernice I2C je založená na jednoduchých hardvérových štandardoch (nie sú potrebné špeciálne konektory alebo kabeláž) a rovnako jednoduchého softvérového štandardu pre komunikačný protokol. Obvody, ktoré používajú I2C protokol zahŕňajú pamäte EEPROM a RAM, senzory teploty, expandéry portov, hodiny reálneho času, atď. I2C sa tiež používa ako riadiaca zbernica v obvodoch spracovania signálov, ktoré majú oddelenú dátovú zbernicu, napr. RF tunery, video dekódery a enkódery a audio procesory a kodeky.


Zbernica I2C môže pracovať pri troch prenosových rýchlostiach:

  • Slow (pod 100 Kbps)
  • Fast (400 Kbps)
  • High-speed (3.4 Mbps) – protokol označený ako I2C v.2.0

Vzdialenosť komunikujúcich zariadení je limitovaná z dôvodu udržania komunikačnej rýchlosti na 4m. Max. kapacita prenosového vedenia je približne 400pF. Zbernica používa dva vodiče – Serial Data (SDA) a Serial Clock (SCL). Je to multimasterová zbernica s polovičným duplexom. Každý IO na zbernici je identifikovaný svojou adresou, ktorá je v rámci siete jedinečná, preto zbernica I2C nevyžaduje signál CS (chip select) ani ďalšiu logiku. Linky SDA aj SCL sú pripojené na napájacie napätie pomocou tzv. pullup rezistorov. Čím je vyššia komunikačná rýchlosť, tým musia byť pull-up odpory menšie. Pre základnú rýchlosť 100 kHz postačujú odpory 4,7kOhm.

Postupnosť pri prenose dát: Master generuje štart bit a hodinový signál a posiela adresu podriadeného IO + generuje bit R/\W. Podriadený IO potvrdí prijatie bitom ACK a vysielacie zariadenie (master alebo slave) vyšle jeden bajt dát. Prijímacie zariadenie vloží bit ACK, čím potvrdí prijatie bajtu. Ak je potrebné vyslať viac bajtov opakujú sa predchádzajúce dva body. Pri zápise (master vysiela), master vloží stop bit po prenesení posledného bajtu dát. Pri prijímaní (master prijíma), master nepotvrdzuje posledný bajte bitom ACK, ale priamo vloží stop bit, aby oznámil podriadenému IO, že prenos bol dokončený.

Potvrdenie príjmu (Acknowledge)- vykonáva sa počas deviateho impulzu hodinového signálu a je povinný. Vysielajúce zariadenie uvolní linku SDA (umožní jej „plávať“ na vysokej úrovni). Prijímajúce zariadenie stiahne linku SDA na nízku úroveň (linka SCL musí byť na vysokej úrovni). Ak nedošlo k potvrdeniu, prenos je ukončený. Natiahnutie hodín (Clock Stretching)- keď slave (prijímač) potrebuje viac času na spracovanie bitu alebo práve vykonáva iné funkcie, môže stiahnuť a podržať SCL na nízkej úrovni. Master potom čaká kým slave uvoľní linku SCL predtým bit predtým, než vyšle ďalší bit.

Výhody zbernice I2C- vhodná pre medziobvodovú komunikáciu (on-board devices), ak ku komunikácii dochádza len občas - jednoduché prepojenie viacerých zariadení z dôvodu adresovania - cena a zložitosť nerastie s počtom pripojených zariadení.

Nevýhody zbernice I2C- zložitejšie softvérové riešenie, než napr. v prípade SPI - nízke prenosové rýchlosti

LCD Displej

Vlastnosti:

  • 2x8 LCD Displej bez podsvetlenia
  • 4 Tlačítka
  • Trimer na zmenu kontrastu

Technické parametre:

  • Napájanie: 5 V @ 15 mA
  • Pripojenie: 4-Bit parallel interface (Hitachi HD44780 compatible)
  • Rozmery: 60 x 50 x 20 mm
  • Pracovná teplota: 0 až +70 °C

Popis riešenia

Na pripojenie PCF8583 k mikroprocesoru ATmega328P sme využili kontaktné pole na vývojovej doske Acrob. Keďže špeciálne funkcie čipu PCF8583 nevyužívame na pripojenie RTC k mikroprocesoru nám stačia dva vodiče SDA a SCL. Kontaktné pole a mikroprocesor sa nachádzajú na jednej doske preto sme ako pull-up rezistory použili interné pull-up 40k procesora ATmega328P, ktoré su vhodné pre komunikáciu na krátku vzdialenosť s jedným bus masterom a slaveom. Tieto rezistory nám zabezpečia log.1 na obidvoch pripojených pinoch v kľudovom stave. Na pripojenie LCD modulu s tlačidlami sme použili 20 pinový konektor X1. Na piny OSCI a OSCO čipu PCF8583 sme pripojili 32,768 kHz kryštál bez ktorého by tento čip nebol funkčný.

Schéma zapojenia

Algoritmus a program

Algoritmus je napísaný v jazyku c++ vo vývojovom prostredí AVRstudio 4 a zabezpečuje inicializáciu obvodu PCF8583. Po inicializácii sa uskutoční komunikácia štandardom I2C. V krátkom vývojovom diagrame je znázornenie behu programu.

Zdrojový kód

main.c

#include<avr/io.h>
#include<avr/pgmspace.h>
#include<avr/interrupt.h>
#include<util/delay.h>
#include<stdio.h>
#include<string.h>
#include <util/twi.h>
#include "lcd.h"
#include "rtc.h"

char btns = 0xFF;		//zadefinovanie adresy pre Hodiny RTC
char buf[128];			//zadefinovanie pomocneho buffera


FILE mystdout = FDEV_SETUP_STREAM(lcdDataWrite, NULL, _FDEV_SETUP_WRITE);	// Printf pre LCD


void lcdGotoXY(riadok, stlpec)					// funkcia na nastavenie suradnic kurzora
	{
	if (riadok==1)
		lcdControlWrite(0x40+stlpec+0x80); 
		else
			lcdControlWrite(0x00+stlpec+0x80); 
	}

unsigned char num2bcd(unsigned char n)				// zakoduje cislo do BCD
{
	n = n % 100;
	return (n % 10) | ((n / 10) << 4);
}

unsigned char bcd2num(unsigned char n)				// odkoduje cislo z BCD
{
	return ((n >> 4) * 10) + (n & 0x0F);
}

int main()
{

	TWBR = 12; 	// nastavenie rychlosti TWI: 16MHz / (16 + 2 * (12) * 1) = 400kHz
	
	// nastavenie interneho 40k pull-up pre komunikaciu
	// na kratku vzdialenost s jednym bus masterom a slaveom
	DDRC = 0;
	PORTC = 0x30;

	stdout = &mystdout;				// Odteraz funguje printf();
	lcdInit4();						// inicializacia displeja
	           				
	lcdControlWrite(1<<LCD_CLR);	//vymazanie displeja
	lcdControlWrite(0x40);          // pozicia 0,0
	lcdGotoXY(0,0);
	
	// test RTC - nacita control register z RTC
	// do pamate ATmegy skontroluje stav a vypise
	// info na displej
	if (!rtc_read(buf, 0x80, 1))
	{
		printf("BAD BUS");					// Vypis na displej ze je chybna zbernica
		return 0;
	}
	
	lcdControlWrite(1<<LCD_CLR);	//vymazanie displeja
	lcdControlWrite(0x40);          // pozicia 0,0
	lcdGotoXY(0,2);
	printf("CAS");					// Vypis na displej cas

	while(1)
	{
		char b = ReadButtons();		// ulozi stav tlacidla do premennej b
		if (btns != b)				// podmienka ak btns sa nerovna b tak program pokracuje
		{
			if (b & 8)				// podmienka pre nastavovanie datumu
			{
			lcdControlWrite(1<<LCD_CLR);	//vymazanie displeja
			lcdControlWrite(0x40);          // pozicia 0,0
			lcdGotoXY(0,1);
			printf("DATUM");					// Vypis na displej cas
			if (b & 1)			// ak sa stlaca tlacidlo 1 natavuju sa den v mesiaci
			{
				rtc_read(buf, RTC_REG_DAY, 1);
				buf[1] = buf[0] & 0xC0;
				char k = (31 + (bcd2num(buf[0] & 0x3F) + 1) - 1) % 31;	//vypocet dna v mesiaci cez modulo a konvertovanie BCD kodu na cislo
				buf[0] = num2bcd(k + 1) | buf[1];
				rtc_write(RTC_REG_DAY, buf, 1);
			} else if (b & 2)	//ak sa stlaca tlacidlo 2 natavuju sa mesiace
				{
				rtc_read(buf, RTC_REG_MTH, 1);
				char k = (12 + (bcd2num(buf[0] & 0x1F) + 1) - 1) % 12;	//vypocet mesiaca v roku cez modulo a konvertovanie BCD kodu na cislo
				buf[0] = num2bcd(k + 1);
				rtc_write(RTC_REG_MTH, buf, 1);
			} else if (b & 4) 	//ak sa stlaca tlacidlo 3 natavuju sa roky
				{
				rtc_read(buf, RTC_REG_DAY, 1);
				buf[0] = (buf[0] & 0x3F) | ((buf[0] + 0x40) & 0xC0);	//nastavenie potrebnych registrom na zapis roku do pomocneho buffera
				rtc_write(RTC_REG_DAY, buf, 2);
				}
			
			} 
			else 					// podmienka pre nastavovanie hodin
			{
				lcdControlWrite(1<<LCD_CLR);	//vymazanie displeja
				lcdControlWrite(0x40);          // pozicia 0,0
				lcdGotoXY(0,2);
				printf("CAS");					// Vypis na displej cas
				if (b & 1)			// nastavenie hodin
				{
					rtc_read(buf, RTC_REG_HRS, 1);
					buf[0] = num2bcd((bcd2num(buf[0]) + 1) % 24);
					rtc_write(RTC_REG_HRS, buf, 1);
				} else if (b & 2)	//nastavenie minut
				{
					rtc_read(buf, RTC_REG_MIN, 1);
					buf[0] = num2bcd((bcd2num(buf[0]) + 1) % 60);
					rtc_write(RTC_REG_MIN, buf, 1);
				} else if (b & 4) 	//resetovanie sekund
				{
					rtc_read(buf, RTC_REG_HSC, 2);
					buf[0] = 0;
					buf[1] = 0;
					rtc_write(RTC_REG_HSC, buf, 2);
				}
			}

			btns = b;
		}

		rtc_read(buf, RTC_REG_CTR, 8);		

		if (btns & 8)
		{

		//zapisanie potrebnych registrov dna,mesiaca,roku PCF8583 do pomocneho buffera a 5s pauza na zobrazenie datumu
		//nastavenie formatu pre datum 00/00/00
		
		sprintf_P(buf, PSTR("%2.2X.%2.2X.%2.2X"), buf[RTC_REG_DAY] & 0x3F, buf[RTC_REG_MTH] & 0x1F, 0x14 + (buf[RTC_REG_DAY] >> 6));
		lcdGotoXY(1,0);
		printf(buf);
		_delay_ms(5000);
		} 
		else 

		//zapisanie potrebnych registrov hodin,minut,sekund PCF8583 do pomocneho buffera na zobrazenie casu
		//nastavenie formatu pre hodiny 00:00:00

		{
			sprintf_P(buf, PSTR("%2.2X:%2.2X:%2.2X"), buf[RTC_REG_HRS], buf[RTC_REG_MIN], buf[RTC_REG_SEC]);
		}
		
		lcdGotoXY(1,0);
		printf(buf);	//vypisanie obsahu buffera (CAS alebo DATUM) na displej
		}
	return 0;
}

rtc.c

#include<avr/io.h>

#include "wf-def.h"
#include "rtc.h"

unsigned char rtc_mutex = 0;

unsigned char rtc_accessed()
{
	return rtc_mutex;
}

unsigned char rtc_read(unsigned char *dest, unsigned char src, unsigned char cnt)
{
	rtc_mutex++;
	unsigned char timeout = 0;
	do
	{
		if (timeout++ == 128)
		{
			rtc_mutex--;
			return false;
		}

		//start condotion
		TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
		while(!(TWCR & _BV(TWINT)));

		//send SLA+W
		TWDR = RTC_IC_ADDR;
		TWCR = _BV(TWINT) | _BV(TWEN);
		while(!(TWCR & _BV(TWINT)));

	} while ((TWSR & 0xF8) != 0x18);

	//address
	TWDR = (src);
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));
	if((TWSR & 0xF8) != 0x28)
	{
		rtc_mutex--;
		return false;
	}

	//start condition
	TWCR = _BV(TWINT)  |_BV(TWSTA) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));
	if((TWSR & 0xF8) != 0x10)
	{
		rtc_mutex--;
		return false;
	}

	//send SLA+R
	TWDR = RTC_IC_ADDR | 1;	
	TWCR = _BV(TWINT)|_BV(TWEN);
	while(!(TWCR & _BV(TWINT)));
	if((TWSR & 0xF8) != 0x40)
	{
		rtc_mutex--;
		return false;
	}

	//recieve data
	for (unsigned char x = 0; x < cnt; x++)
	{
		if (x == (cnt-1)) TWCR = _BV(TWINT) | _BV(TWEN); else TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
		while(!(TWCR & _BV(TWINT)));
		if (x == (cnt-1))
		{
			if((TWSR & 0xF8) != 0x58)
			{
				rtc_mutex--;
				return false;
			}
		} else {
			if((TWSR & 0xF8) != 0x50)
			{
				rtc_mutex--;
				return false;
			}
		}
		
		dest[x] = TWDR;
	}

	//stop condition
	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
	while(TWCR & _BV(TWSTO));

	rtc_mutex--;
	return true;
}

unsigned char rtc_write(unsigned char dest, unsigned char *src, unsigned char cnt)
{
	rtc_mutex++;
	unsigned char timeout = 0;
	do
	{
		if (timeout++ == 128)
		{
			rtc_mutex--;
			return false;
		}

		//start condotion
		TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
		while(!(TWCR & _BV(TWINT)));

		//send SLA+W
		TWDR = RTC_IC_ADDR;
		TWCR = _BV(TWINT) | _BV(TWEN);
		while(!(TWCR & _BV(TWINT)));

	} while ((TWSR & 0xF8) != 0x18);

	//address
	TWDR = (dest);
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));
	if((TWSR & 0xF8) != 0x28)
	{
		rtc_mutex--;
		return false;
	}

	//send data
	for (unsigned char x = 0; x < cnt; x++)
	{
		TWDR = (src[x]);
		TWCR = _BV(TWINT) | _BV(TWEN);
		while(!(TWCR & _BV(TWINT)));
		if((TWSR & 0xF8) != 0x28)
		{
			rtc_mutex--;
			return false;
		}
	}

	//stop condition
	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
	while(TWCR & _BV(TWSTO));

	rtc_mutex--;
	return true;
}

Source Files: Main.c,rtc.c a Lcd.c

Header Files: Lcd.h a rtc.h

Overenie

Hodiny začnú pracovať hneď po oživení dosky. Na displeji sa ako prvé zobrazia dig.hodiky, v prvom riadku nás program informuje že sa jedná o čas a v druhom sa vypisujú samotné hodiny, ktoré sú defaultne nastavené na čas 00:00:00. Tlačidlom 1 sa nastavujú hodiny, tlačidlom 2 minúty a tlačidlo 3 sa resetujú sekundy. Po stlačení 4.tlačidla sa na displeji zobrazí dátum, ktorí je tiež defaultne nastavený na 01/01/14. Dátum sa taktiež dá nastavovať analogicky ako čas len je potrebné mať stlačené tlačidlo 4. Kedže obvod nemá vlastné napájanie po vypnutí programu sa hodiny zresetuju.

Fotogaléria hodín realného času:


Demonštračné video na ktorom vidno ako hodiny pracujú a ako ich môžeme nastavovať:

http://www.youtube.com/watch?v=AOiStNTCzi8&feature=youtu.be