PCF8563 RTC óra

Tartalom:

  • A PCF8563 chip műszaki adatai, legfontosabb tulajdonságai
  • Összehasonlítás a DS3231 chippel, hasonlóságok és különbségek
  • Részletes belső működés, elemi parancsok, regiszterek funkciói
  • Elemes táplás során is működő megszakítás (riasztás) kimenet előnyei, használata
  • Teljesen saját kezelő függvények példa programmal, teljes forráskód

————————————————————————————–

Kezdetben a DS3231 RTC óra chip-et illetve modult kezdtem el használni. Minden webáruházban ebbe akad bele először, aki óra IC-t keres magának. Nincs is vele semmi baj, nagyon pontos, hőmérséklet kompenzált, két időpontot is lehet riasztásra használni stb. Egy aprócska problémám adódott. Az chip INT kimenete, amit egy ATmega chip felélesztésére szerettem volna használni, csak bekapcsolt tápfesz esetén működik. Ha elemről működik az óra IC, akkor az INT kimenete nem küld megszakítást. Viszont, ha tápfesszel látom el folyamatosan a modult (mert én nem csupasz IC-t használtam), már elég tekintélyes áramfelvétel adódott, ami elemes áramköröknél nagy hátrány. Hát szétnéztem, hogy mi van még a piacon. Találtam is egy őskövületet 1995 környékéről. Ez a PCF8563 IC-re épülő modul. Ha nem használom az I2C kommunikációt, akkor szinte elenyésző az áramfelvétele, adatlap szerint 250-300nA. Az INT kimenete mindig működik, mert ez az IC annyira korszerűtlen, hogy nincs külön elemes táplálásra szolgáló tápfesz bemenete, nem is tud arról, hogy elemről működik. Szóval az én céljaimra tökéletes. Bónusz tulajdonság, hogy van beépített timer, ami beállítható időnként ad megszakítás jelet az INT kimeneten, vagyis nem kell azzal vacakolnom, hogy egy megszakítást követően kiszámolgassam a következő riasztási időpontot és beállítsam a riasztási idő regiszterekben. Hátrány, hogy ez a timer nem egy rugalmasan beállítható darab, de esetemben néhány perces, esetleg néhány órás időintervallumokat szerettem volna beállítani, így számomra ez is tökéletes.

Lássunk néhány műszaki adatot:

  • tápfeszültség:1,8V – 5,5V
  • áramfelvétel (ha nem használom az I2C kommunikációt): 250nA
  • timer intervallumok: ~2mikrosec-től 4 óra 15 percig. (hosszú időzítéseknél 1 perces felbontással)

Az óra IC beállítása nagyon hasonló a DS3231-nél használatos megoldásokhoz. Van egy csomó regisztere, amiket lehet írni és olvasni. Az I2C buszon az IC a Hex 0x51 címen (decimális 81) érhető el. A regisztereinek összefoglaló táblázata:

A 0-val jelölt biteket mindig 0-val kell írni, olvasáskor a kiolvasott érték 0 és 1 is lehet.
Az „x”-el jelölt bitek nincsenek használva. Regiszter beállításakor tetszőleges értéket lehet írni az adott bit pozícióba, amikor ezeket visszaolvassák, a visszaolvasott értékek eltérhetnek beírt értéktől.

Néhány tulajdonság azoknak, akik használni szeretnék:

  • Az idő beállításához az I2C buszon keresztül az 0x02-től az 0x08 regiszterig kell beírni az adatokat nagyrészt BCD kódolással. Kiolvasni ugyanezeket a regisztereket kell.
  • A riasztási időpont beállítása a 0x09-tól a 0x0C regiszterekkel lehetséges. Csak az óra és perceket állíthatjuk be, azonban lehetőség van óránként azonos percben, naponta, hónap adott napján, vagy hét adott napján történő ismétlésre. Ezt úgy lehet elérni, hogy a regiszterek balról első bitjébe 1-et kell írni annál a paraméternél, aminek az egyezőségét nem szeretnénk figyelni. Pl. ha csak a perc regiszterben adunk meg ezen a biten 0 értéket, a többi riasztás regiszterben itt 1-et írunk, akkor minden órában ugyanabban a percben fog riasztani. Ha pl. beállítjuk a percet, órát és a hét egy adott napját, akkor csak a hétnek az adott napján fog a megadott időben riasztani. A példa programban csináltam egy setAlarm() nevű függvényt négy bemenő paraméterrel (sorrendben: perc, óra, hónap napja, hét napja). Néhány példa ezzel a függvénnyel:
    – riasztás minden óra minden adott (15.) percében -> setAlarm(15,99,99,99)
    – riasztás minden nap 15 óra 14 perckor -> setAlarm(14,15,99,99)
    – riasztás minden hónap 5. napján 15 óra 14 perckor -> setAlarm(14,15,5,99)
    – riasztás minden hétfőn (1 nap) 15 óra 14 perckor -> setAlarm(14,15,99,1)
  • Létezik egy időintervallum beállítási lehetőség, aminek a segítségével ciklikusan a beállított időintervallumban keletkezik riasztás. Az intervallum megadáshoz egy byte áll rendelkezésre, tehát 1-255 közötti értéket állíthatunk be. Erről az értékről mindig visszaszámol 0-ra, és ekkor ad megszakítás jelet az INT kimeneten. Persze a visszaszámlálás sebessége beállítható. Az órajel ami ezt végzi 4096Hz, 64Hz, 1Hz, és 1/60Hz lehet. Ezzel gyakorlatilag kb. 2miliSec és 4 óra 15 perc között állíthatunk be riasztási (időzítési) időintervallumot. Azonban a beállítható idő felbontása is elég korlátozott, pl. a legnagyobb időzítésnél már csak 1 perc. De ugyan kit érdekel 4 órás időzítésnél, hogy nem adhatja meg másodperc pontosan az időintervallumot!
    További hátrány, hogy a visszaszámlálás órajelét a folyamatosan működő órajel osztóból kapja a visszaszámoló regiszter, így az első számlálási időzítés meglehetősen pontatlan is lehet. Amikor elindítjuk a visszaszámlálást, szerencsétlen esetben szinte azonnal megkapjuk ez első visszaszámoló impulzust, miközben a következőre már fix és pontos időt kell várni. Pl. ha az időalap 1/60Hz, és 2-et állítunk be a timer regiszterbe, akkor lehet hogy az első időzítésünk 1 perc lesz, míg ettől kezdve a többi már stabilan és pontosan az elvárt 2 perc. Ezzel meg kell békélni! Ez kőkorszaki masina, lehet, hogy még ma is kőbaltával faragják a szilícium lapkát.
  • Az időpont és a visszaszámláló timer riasztás szabadon keverhető, akár mindkettő működhet egyszerre. Azt, hogy az INT kimenet melyik riasztásra reagáljon 0 szinttel (folyamatosan magas a kimenet, de riasztáskor lehúzza az IC 0-ra), az a STATUS2 regiszterben beállítható. Jobbról a második AIE (alarm interrupt enable) bittel az időpont riasztás, míg jobbról az első TIE (timer interrupt enable) bittel a timer riasztás megszakítást engedélyezhetjük. Ha bármelyik 1, akkor az INT kimenet 0 lesz riasztáskor. A riasztás bekövetkeztét pedig jobbról a 4. AF (alarm flag) és jobbról a 3. TF (timer flag) bitek jelzik. Ezek 1-re billennek be, ha elérte az óra a beállított időpontot, vagy a timer leszámolt 0-ra. Azt nagyon fontos tudni, hogy a riasztás bebillenti a megfelelő bitet (AF vagy TF), de semmi nem billenti vissza 0-ba. Márpedig az INT kimenet mindaddig 0, amíg ezen bitek valamelyike 1. Tehát ha egy riasztás megtörtént, akkor szoftverből gondoskodni kell ezen bitek törléséről. Ha a megfelelő bitet töröltük, az INT azonnal újra 1 lesz, és várhatjuk is a következő riasztás.
  • A timer riasztás használatában van egy picike könnyítés. Ha a STATUS2 regiszter jobbról 5. bitjét a TI/TP bitet 1-re állítjuk, akkor nem kell a TF bittel vacakolni. Minden egyes visszaszámlálás okozta riasztás után az INT kimenet vissza áll 1-re. A visszaállási idő késleltetése a visszaszámlálási frekvenciától függ, de maximális értéke is 1/64 másodperc. Végül is ez közömbös, hiszen itt a lefutó élet fogjuk figyelni.  

Ehhez az RTC óra chip-hez nem találtam számomra megfelelő könyvtárat. Van könyvtár hozzá, de túl sok függvényt valósítottak meg benne, külön függvény van a dátum beállításra, az idő beállításra, külön lehet lekérdezni az órát, percet stb. Írtam hát egy saját függvény gyűjteményt. Alább a minta program, és részletes kommentekkel az összes függvény. Sok sikert a használatához!

#include <Wire.h>
int PCF8563_I2C_cim = 81;

// globális változók az időpont tárolásához. A getDateTime() függvény 
// ezen változók érétkét tölti az aktuális dátummal és időponttal
byte ora;
byte perc;
byte sec;
byte nap;
byte het_napja;
byte honap;
int ev;
// globális változók a riasztási időpontok és flag-ek tárolásához. A getAlarm() függvény
// ezen változók értékét tölti fel az aktuális riasztási időpont adatokkal
byte alarm_ora;
byte alarm_perc;
byte alarm_nap;
byte alarm_het_napja;
bool alarm_flag; //a getAlarm() geletölti az STATUS2 regiszter AF flag állapotát (0 esetén még nem volt riasztás)
bool timer_flag; //a getAlarm() geletölti az STATUS2 regiszter TF flag állapotát (0 esetén még nem volt riasztás)
bool int_alarm; //a getAlarm() geletölti az STATUS2 regiszter AIE flag állapotát (0 esetén INT tiltva, 1 engedélyezve)
bool int_timer; //a getAlarm() geletölti az STATUS2 regiszter TIE flag állapotát (0 esetén INT tiltva, 1 engedélyezve)
byte timer; //a getAlarm() geletölti az timer számláló regiszter (0x0F) tertalmát, ha 0-ra ér, akkor riaszt (TF=1)

//byte status2;
long ido_tmp=millis();  //segéd változó a másodpercenkénti idő kiíráshoz

void setup()
{
  pinMode(13, OUTPUT);           // A beépített LED fogj jelezni a PCF8563 INT kivezetésének állapotát
  pinMode(3, INPUT);           // Ezen a bemeneten fogjuk figyelni PCF8563 INT kivezetésének állapotát
  digitalWrite(3, HIGH);       // felhúzó ellenállás bekapcsolása a 3-as bemeneten
  //megvillogtatjuk a beépített ledet, hogy lássuk az alaplapon melyik az
  delay(500);
  digitalWrite(13,HIGH);delay(100);digitalWrite(13,LOW);delay(100);digitalWrite(13,HIGH);delay(100);digitalWrite(13,LOW);delay(100);
  digitalWrite(13,HIGH);delay(100);digitalWrite(13,LOW);delay(100);digitalWrite(13,HIGH);delay(100);digitalWrite(13,LOW);
  delay(500);
  Wire.begin();
  Serial.begin(9600);
  
  //Egyszer be kell állítani a dátumot és az órát
  //Paraméterek sorrendje: év, hó, nap, hét napja, óra, perc
  //setDateTime(2020,9,8,2,7,43);

  //a riasztási üzemmódok beállítása
  // Paraméterek sorrendje: időpont riasztás, timer riasztás
  // Nem szükséges kiadni, mert setAlarm() és a setTimer() is beállítja magának
  // a szükséges biteket a STATUS2 regiszterben. Azonban flag-ek törlésére és 
  // a megszakítás (INT) kimenet tiltására ezt lehet felhasználni
  // Az időpont és a timer riasztás szabadon keverhető, egyszerre mindkettőt
  // be lehet kapcsolni
  setAlarmTimerMode(0,0);
  
  // Ha az időpont riasztást teszteljük, akkor setAlarm() fügvénnyel állíthatjuk be az időpontot
  // Paraméterek sorrendje: perc, óra, nap, hét napja
  //   Ha periódikus riasztást akarsz, akkor 99-et kell megadni annál a paraméternél, 
  //   amit nem kell vizsgálnia a chip-nek
  //   pl. minden óra minden adott (15.) percében -> 15,99,99,99
  //   pl. minden nap 15 óra 14 perckor -> 14,15,99,99
  //   pl. minden hónap 5. napján 15 óra 14 perckor -> 14,15,5,99
  //   pl. minden hétfőn (1 nap) 15 óra 14 perckor -> 14,15,99,1
  setAlarm(46,99,99,99);
  
  //Riasztás beállított időintervallumonként (ismétlődő).
  //Paraméterek sorrendje: időzítési idő (másodperc)
  //Az időzítési idő csak 1-tól 15300 másodpercig (aza 255 perc, ami 4óra 15 perc) terjedhet
  //255 másodperc után már csak perc pontosságú azaz, egész percre kerekíti a beállított értéket (lefele kerekít egészre)
  setTimer(12); //10 másodperc


  getAlarm();  //lekérdezi a riasztással kapcsolatos paramétereket és biteket
  Serial.println("Ido riasztás status:"); 
  Serial.println("===================="); 
  Serial.print("           AF flag:"); Serial.println(alarm_flag);
  Serial.print(" Megszakítás (INT):"); Serial.println(int_alarm);
  Serial.print("              Perc:"); if (alarm_perc==99) {Serial.println("-");} else {Serial.println(alarm_perc);} 
  Serial.print("               Ora:"); if (alarm_ora==99) {Serial.println("-");} else {Serial.println(alarm_ora);} 
  Serial.print("       Honap napja:"); if (alarm_nap==99) {Serial.println("-");} else {Serial.println(alarm_nap);}
  Serial.print("         Het napja:"); if (alarm_het_napja==99) {Serial.println("-");} else {Serial.println(alarm_het_napja);} 
  Serial.println("Timer riasztás status:"); 
  Serial.println("======================"); 
  Serial.print("           TF flag:"); Serial.println(alarm_flag);
  Serial.print(" Megszakítás (INT):"); Serial.println(int_timer);
  Serial.print("          Szamlalo:"); Serial.println(timer);
}

void loop()
{
  if (ido_tmp<millis()){      //másodpercenként kiirjuk az időt a soros portra
    getDateTime();
    Serial.print("Datum:"); Serial.print(ev); Serial.print(":"); Serial.print(honap); Serial.print(":"); Serial.print(nap);
    Serial.print("  Het napja:"); Serial.print(het_napja);
    Serial.print("  Ido:"); Serial.print(ora); Serial.print(":"); Serial.print(perc); Serial.print(":"); Serial.println(sec);
    ido_tmp=millis()+1000;
  }
  if (digitalRead(3))          //AZ INT kimenet állapotát beolvassuk 
    {digitalWrite(13,HIGH);}   //INT kimenet HIGH, tehát nincs megszakítás
  else {
    digitalWrite(13,LOW);      //INT kimenet LOW, tehát megszakítás érkezett, törölni kell a TF bitet
    Serial.println("Riasztas");
    delay(200);                //ha ezt nem tesszük be, nem látható, hogy a led egy pillanatra elalszik amikor riasztás jött. 
    clearAlarm();              //Törli az AF és a TF bitet a status2 regiszterben, így jöhet egy újabb riasztás ami majd bebillenti ezek valamelyikét
  }
}

void clearAlarm()
/********************************************************************************************************
 * STATUS2 (0x01) regiszter AF (BIT3) és TF (BIT2) törlése                                              *
 * Amikor az időpont riasztás megtörténik az AF flag bebillen 1-re. Ha a timer riasztást történik meg   *
 * a TF flag billen be 1-re. Egy újabb riasztást csak akkor lehet észre venni, ha ezen biteket 0-ra     *
 * billentjük vissza. Mivel az INT kimenet akkor generál megszakítást, ha az AF vagy a TF 1, ezért      *
 * Ezen bitek törlése az INT kimeneten is megszünteti a LOW szintet. Az INT LOW szintű megszakításához  *
 * még engedélyezni kell a megszakítás láb (INT) működését is a AIE illetve a TIE bitekkel.             *
 ********************************************************************************************************/
{
  Wire.beginTransmission(PCF8563_I2C_cim);  //megszólítjuk a slave egységet a címével
  Wire.write(0x01);                         //megadjuk a STATUS2 regiszter címét
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 1);     //1 byte-ot kérünk az I2C buszon a slave-től
  byte status2 = Wire.read();                    //beolvassuk a STATUS2 regiszter tartalmát
  status2=status2 & 0b11110011;
  Wire.beginTransmission(PCF8563_I2C_cim);  //megszólítjuk a slave egységet a címével
  Wire.write(0x01);                         //megadjuk a STATUS2 regiszter címét
  Wire.write(status2);                      //Írjuk a STATUS2 regisztert az új értékével
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
}

void setTimer(int masodperc)
/***************************************************************************************************************
 * Beállítja a timert, ami ciklikusan megszakítást generál az INT kimeneten, amikor a számláló 0-ra számol     *
 * vissza a beállított értékről (max 255). Ha a STATUS2 regiszterben a TI_TP bit értéke 0, akkor a TI bitet    *
 * a STATUS2 regiszterben minden megszakítás után törölni kell, különben INT folyamatosan alacsony marad       *
 * az első generált timer riasztás után. Ha TI_TP bit értéke 1, akkor az INT kimenet egy órajel ciklus után    *
 * automatikusan HIGH lesz, vagyis nem kell törölni a TF bitet. Ez a függvény nem használja ki ezt a           *
 * lehetőséget, mert így egységesen ugyanúgy kell lekezelni a timer riasztást, mint ha időpont riasztás        *
 * történt volna.                                                                                              *
 * Bemenő paramétere a visszaszámlálási ciklus időtartama másodpercben megadva. Ennek lehetséges intervalluma  *
 * 1-15300 sec. Amíg a bemenő paraméter értéke 1-255, addig a timer számláló visszaszámoló időalapja           *
 * 1hz-ra állítódik be, ha az érték 255-nél nagyobb, akkor 1/60Hz-re. Utóbbi esetben már csak egy perces       *
 * felbontásban lehet a riasztást beállítani 1-255 perc között. Ha ennél pontosabb beállítás szükséges         *
 * az időpont riasztást lehet felhasználni a következő riasztási időpont kiszámolásával és beállításával       *
 * Az első timer riasztás időintervalluma nem pontos. Az 1/60Hz előállításakor az órajel az óra egész          *
 * perceikor keletkezik, így szerencsétlen esetben akár 1 perccel kevesebb is lehet az eltelt idő.             *
 * Pl 4. perc beállításakor (bemenő paraméter 256), és ezt pont az adott perc 59. másodpercében állítjuk       *
 * be, a következő másodpercben már számol is egyet vissza a timer, így 3 perc múlva jön a riasztás. A további *
 * időintervallumok már pontosak. Ez a működés minden bizonnyal igaz a többi visszaszámolási frekvenciára      *
 * is, tehát pl. az 1Hz frekvencia is egész másodperc értékeknél keletkezik.                                   *
 * Időalap lehetséges értékei:                                                                                 *
 *  1 - 0,000244140625 sec (0,244 milisec, 0,24 msec - 62,2 msec) ==> BIT1=0,BIT0=0                            *
 *  2 - 0,015625 sec (15,6 milisec 15 msec - 3,98 sec)==> BIT1=0,BIT0=1                                        *
 *  3 - 1 sec (1 sec - 4,25 perc)==> BIT1=1,BIT0=0                                                             *
 *  4 - 1/60 sec (60 sec 1 perc - 4,25 óra)==> BIT1=1,BIT0=1                                                   *
 *   (ezekből mi csak a 3-as és 4-es paramétert fogjuk használni)                                              *
 ***************************************************************************************************************/
{
  byte ciklus;                              //ebben a változóban adjuk meg a "timer" regiszter értékét 
  byte timercontrol;                        //ebben a változóban képezzük a "timer control" regiszter értékét
  if (masodperc == 0) {ciklus = 1;}         //a ciklus minimuma 1
  if (masodperc<256){timercontrol = 0b10000010;ciklus=masodperc;} //BIT7=1 engedélyezi a timer működését és 1Hz-es visszaszámolást állít be
  else {timercontrol = 0b10000011;ciklus=masodperc/60;} //BIT7=1 engedélyezi a timer működését és 1/60Hz-es visszaszámolást állít be
  Wire.beginTransmission(PCF8563_I2C_cim);  //megszólítjuk a slave egységet a címével
  Wire.write(0x01);                         //megadjuk a STATUS2 regiszter címét
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 1);     //1 byte-ot kérünk az I2C buszon a slave-től
  byte status2 = Wire.read();                    //beolvassuk a STATUS2 regiszter tartalmát
  status2 = status2 & 0b11101111;           //A BIT4 TI/TP=0, TI-t mindig törölni kell riasztás után
  status2 = status2 & 0b11111011;           //A BIT2 TF=0, a következő timer riasztás bebillentheti 1-re
  status2 = status2 | 0b00000001;           //A BIT0 TIE=1, engedélyezett az interrupt kimenet (INT) amikor TF bebillen 1-re
  Wire.beginTransmission(PCF8563_I2C_cim);  // megszólítjuk a slave egységet a címével
  Wire.write(0x01);                         //megadjuk a STATUS2 regiszter címét
  Wire.write(status2);                      //írjuk a STATUS2 regisztert az új értékével
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.beginTransmission(PCF8563_I2C_cim);  // megszólítjuk a slave egységet a címével
  Wire.write(0x0E);                         //"timer control" regiszter címzése
  Wire.write(timercontrol);                 //írjuk a "timer control" regisztert az új értékével
  Wire.write(ciklus);                       //visszaszámlálási ciklusszám beállítása
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
}



void setAlarm(byte perc, byte ora, byte nap, byte het_napja)
/*******************************************************************************************************************
 * Ez a függvény beállítja a riasztási időpontot. A lehetséges parméterek a perc, óra nap és hét napja             *
 * A beállított időpont ciklikusan ismétlődő riztást fog eredményezni amikor a beállított paraméter megegyezik     *
 * az aktuális időponttal. Pl. ha csak a percet adjuk meg, akkor minden órában időpont riasztás fog generálódni.   *
 * Ha egy paramétert nem akarunk megadni, akkor annak értéke legyen 99. Ekkor az adott paramétert Pl. hét napját   *
 * Nem fogja figyelni, és csak a többi paraméter egyezőségét figyeli.                                              *
 * Pl.  riasztás minden óra minden adott (15.) percében -> setAlarm(15,99,99,99)                                   *
 *      riasztás minden nap 15 óra 14 perckor -> setAlarm(14,15,99,99)                                             *
 *      riasztás minden hónap 5. napján 15 óra 14 perckor -> setAlarm(14,15,5,99)                                  *
 *      riasztás minden hétfőn (1 nap) 15 óra 14 perckor -> setAlarm(14,15,99,1)                                   *
 * A STATUS2 regiszterben az AF bitet minden egyes riasztás után törölni kell, különben a következő ciklikus       *
 * riasztás nem jut érvényre hiszen már 1 az AF értéke. Ez a függvény a riasztási időpont beállításakor            *
 * beállítja megának a STATUS2 regiszterben az AF bitet 0-ra, valamint beállítja 1-re az AIE bitet, ami            *
 * engedélyezi az INT kimenet működését. INT kimenet LOW szinttel jelzi a riasztás bekövetkeztét. INT akkor        *
 * lesz újra HIGH, ha az AF bitet töröljük, egyébként folyamatosan LOW marad a riasztás bekövetkezte után.         *
 *******************************************************************************************************************/
{
  Wire.beginTransmission(PCF8563_I2C_cim);  // megszólítjuk a slave egységet a címével
  Wire.write(0x01);                         //megadjuk a STATUS2 regiszter címét
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 1);     //1 byte-ot kérünk az I2C buszon a slave-től
  byte status2 = Wire.read();                    //beolvassuk a status regiszter tartalmát
  if (perc < 99) {                          //Ha a perc nem 99, akkor értékét figyelni fogja a chip riasztáskor
    perc=constrain(perc, 0, 59);
    perc=decToBcd(perc);
    perc=perc & 0b01111111;                 //ha 0x09 regiszter 7. bitje 0, ekkor a regiszter értékét is figyeli riasztáskor
  } 
  else {perc = 0b10000000;}                 //ha 0x09 regiszter 7. bitje 1, ekkor a regiszter értékét nem figyeli riasztáskor
  if (ora < 99) {                           //Ha az óra nem 99, akkor értékét figyelni fogja a chip riasztáskor
    ora=constrain(ora, 0, 23);
    ora=decToBcd(ora);
    ora=ora & 0b01111111;                   //ha 0x0A regiszter 7. bitje 0, ekkor a regiszter értékét is figyeli riasztáskor
  } 
  else {ora = 0b10000000;}                  //ha 0x0A regiszter 7. bitje 1, ekkor a regiszter értékét nem figyeli riasztáskor
  if (nap < 99) {                           //Ha a nap nem 99, akkor értékét figyelni fogja a chip riasztáskor
    nap=constrain(nap, 1, 31);
    nap=decToBcd(nap);
    nap=nap & 0b01111111;                   //ha 0x0B regiszter 7. bitje 0, ekkor a regiszter értékét is figyeli riasztáskor
  } 
  else {nap = 0b10000000;}                  //ha 0x0B regiszter 7. bitje 1, ekkor a regiszter értékét nem figyeli riasztáskor
  if (het_napja < 99) {                     //Ha a hét napja nem 99, akkor értékét figyelni fogja a chip riasztáskor
    het_napja=constrain(het_napja, 0, 6);
    het_napja=decToBcd(het_napja);
    het_napja=het_napja & 0b01111111;       //ha 0x0C regiszter 7. bitje 0, ekkor a regiszter értékét is figyeli riasztáskor
  } 
  else {het_napja = 0b10000000;}            //ha 0x0C regiszter 7. bitje 1, ekkor a regiszter értékét nem figyeli riasztáskor
  status2 = status2 & 0b11110111;           //A BIT3 AF=0, a következő riasztás bebillentheti 1-re
  status2 = status2 | 0b00000010;           //A BIT1 AIE=1, engedélyezett az interrupt kimenet
  Wire.beginTransmission(PCF8563_I2C_cim);  // megszólítjuk a slave egységet a címével
  Wire.write(0x01);                         //STATUS2 regiszter címét állítjuk be a slave-en
  Wire.write(status2);                      //kiírjuk az I2C buszon a STATUS2 regiszter új tartalmát a slave-re
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.beginTransmission(PCF8563_I2C_cim);  //megszóllítjuk a I2C buszon a slave-et
  Wire.write(0x09);                         //alarm perc regiszter címét állítjuk be
  Wire.write(perc);                         //beírjuk a riasztás perc értékét a 0x09 regiszterbe
  Wire.write(ora);                          //beírjuk a a riasztás óra értékét a 0X0A regiszterbe (soron következő cím a slave-en)
  Wire.write(nap);                          //beírjuk a a riasztás nap értékét a 0X0B regiszterbe (soron következő cím a slave-en)
  Wire.write(het_napja);                    //beírjuk a a riasztás hét napja értékét a 0X0C regiszterbe (soron következő cím a slave-en)
  Wire.endTransmission();                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
}

void setAlarmTimerMode(bool alarm, bool timer) 
/******************************************************************************************************************
 * Ez a függvény beállítja vagy törli az időpont illetve timer riasztás megfelelő bitjeit a STATUS2 regiszterben  *
 * Az AF és TF riasztás flegeket törli, a következő riasztás bebillentheti ezeket újra.                           *
 * bemenő paraméterek:                                                                                            *
 * alarm=1 AIE=1 időpont megszakítás engedélyezve az INT kimeneten                                                *
 * alarm=0 AIE=0 időpont megszakítás tiltva az INT kimeneten                                                      *
 * timer=1 TIE=1 timer megszakítás engedélyezve az INT kimeneten                                                  *
 * timer=0 TIE=0 timer megszakítás tiltva az INT kimeneten                                                        *
 ******************************************************************************************************************/
{
  Wire.beginTransmission(PCF8563_I2C_cim);   // megszólítjuk a slave egységet a címével
  Wire.write(0x01);                          //STATUS2 regiszter címét állítjuk be a slave-en
  Wire.endTransmission();                    //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 1);      //1 byte-ot kérünk az I2C buszon a slave-től
  byte status2=Wire.read();                  //beolvassuk a STATUS2 regiszter tartalmát
  status2=status2 & 0b11110011;              //A BIT3 AF, és BIT2 TF 0, a következő riasztás bebillentheti 1-re
  if (alarm) {status2=status2 | 0b00000010;} //A BIT1 AIE=1, engedélyezett az interrupt kimenet időpont riasztás esetén
  else {status2 = status2 & 0b11111101;}     //nem kell időpont riasztás, ezért AIE=0 (INT kimenet tiltva)
  if (timer) {status2=status2 | 0b00000001;} //A BIT0 TIE=1, engedélyezett az interrupt kimenet timer riasztás esetén
  else {status2 = status2 & 0b11111110;}     //nem kell timer riasztás, ezért TIE=0 (INT kimenet tiltva)
  Wire.beginTransmission(PCF8563_I2C_cim);   //vége az I2C tranzakciónak
  Wire.write(0x01);                          //STATUS2 regiszter címét állítjuk be a slave-en
  Wire.write(status2);                       //kiírjuk az I2C buszon a STATUS2 regiszter új tartalmát a slave-re
  Wire.endTransmission();                    //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
}

void setDateTime(int ev, byte honap, byte nap, byte het_napja, byte ora, byte perc)
/*******************************************************************************************************************
 * Beállítja az óra dátumát és időpontját. A másodperceket nem lehet állítani, az alapértelmezetten 0 beállításkor *
 *******************************************************************************************************************/
{
  Wire.beginTransmission(PCF8563_I2C_cim);               // megszólítjuk a slave egységet a címével
  Wire.write(0x02);                                      //megmondjuk a "second" regiszter címét
  Wire.write(decToBcd(0));                               //kiírjuk az I2C-ra a slave nek a 0x02 regiszterbe a másodperc értékét (0 a beállításkor)
  Wire.write(decToBcd(perc));                            ///kiírjuk az I2C-ra a slave nek a 0x03 regiszterbe a perc értékét
  Wire.write(decToBcd(ora));                             //kiírjuk az I2C-ra a slave nek a 0x04 regiszterbe az óra értékét
  Wire.write(decToBcd(nap));                             //kiírjuk az I2C-ra a slave nek a 0x05 regiszterbe a nap értékét
  Wire.write(decToBcd(het_napja));                       //kiírjuk az I2C-ra a slave nek a 0x06 regiszterbe a hét napjának értékét
  if (ev > 1999) {honap=decToBcd(honap);ev=ev-2000;}     //Ha az év 2000, akkor a 0x07 regiszter balról első bitje 0, vagyis a hónap értékét 
                                                         //változatlanul tároljuk, az év értékéből 2000-et kivonunk tárolás előtt 
  else {honap=decToBcd(honap) | 0b10000000;ev=ev-1900;}  //Ha az év 2000 előtti, akkor a hónap regiszter balról első bitje 1, és az 
                                                         //év értékéből 1900-at kell kivonni tárolás előtt
  Wire.write(honap);                                     //kiírjuk az I2C-ra a slave nek a 0x07 regiszterbe a hónap értékét
  Wire.write(decToBcd((byte)ev));                        //kiírjuk az I2C-ra a slave nek a 0x08 regiszterbe az év értékét
  Wire.endTransmission();                                //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
}

void getDateTime()
/*****************************************************************************************
 * Ez a függvény beolvassa a dátum és időpont értékeket a megfelelő globális változókba  *
 *****************************************************************************************/
{
  Wire.beginTransmission(PCF8563_I2C_cim);                  // megszólítjuk a slave egységet a címével
  Wire.write(0x02);                                         //megadjuk a second regiszter címét
  Wire.endTransmission();                                   //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 7);                     //megadjuk a slave-nak, hogy küldjön 7 byte-ot, a dátum és idő registereket
  sec=bcdToDec(Wire.read() & 0b01111111);                   //másodperc kiolvasás 0x02 "second" regiszterből, az elso bit nem kell a regiszterből, 
                                                            //mert az az óra integritásról tartalmaz információt
  perc=bcdToDec(Wire.read() & 0b01111111);                  //perc kiolvasás 0x03 "minute" regiszterből, az első bit nem kell a regiszterből
  ora=bcdToDec(Wire.read() & 0b00111111);                   //őra kiolvasás 0x04 "hour" regiszterből, az első bit nem kell a regiszterből
  nap=bcdToDec(Wire.read() & 0b00111111);                   //őra kiolvasás 0x05 "day" regiszterből, az első bit nem kell a regiszterből
  het_napja=bcdToDec(Wire.read() & 0b01111111);             //őra kiolvasás 0x06 "weekday" regiszterből, az első bit nem kell a regiszterből
  honap=Wire.read();                                        //őra kiolvasás 0x07 "mount" regiszterből, az első bit nem kell a regiszterből
  if (honap & 0b10000000) {ev=1900+bcdToDec(Wire.read());}  //a 0x07 "mount" regiszter balról első bitje 1, év kiolvasás 0x08 "year" regiszterből év=+1900 
  else {ev=2000+bcdToDec(Wire.read());}                     //a 0x07 "mount" regiszter balról első bitje 0, év kiolvasás 0x08 "year" regiszterből év=+2000 
  honap=honap & 0b00011111;honap=bcdToDec(honap);           //Az első három bit nem kell a hónaphoz
}


void getAlarm()
/******************************************************************************************************
 * Ez a függvény kiolvassa az időpont riasztási értékeket. Az egyes riasztási értékek byte-jainak     *
 * balról első bitje mindig azt adja meg, hogy figyeli-e a chip a rsiztás létrehozásakor az adott     *
 * értéket. Ha ez a bit 0, akkor az adotot figyeli, ha 1 akkor nem figyeli. Utóbbi esetben a függvény *
 * riasztási adatnak 99-et ad vissza, ezzel jelzi, hogy az adott adat nem vesz részt a riasztás       *
 * létrehozásában                                                                                     *
 ******************************************************************************************************/
{
  Wire.beginTransmission(PCF8563_I2C_cim);                 //megszólítjuk a slave egységet a címével
  Wire.write(0x09);                                        //megadjuk a "minute alarm" regiszter címét
  Wire.endTransmission();                                  //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 4);                    //megadjuk a slave-nak, hogy küldjön 4 byte-ot, a riasztási időpont registereket
  alarm_perc=Wire.read();                                  //riasztás perc kiolvasás 0x09 "minute alarm" regiszterből
  if(0b10000000 & alarm_perc){alarm_perc=99;}              //ha 0x09 regiszter balról első bitje 1, akkor a perc nem vesz részt a riasztásban (visszadott érték 99)
  else {alarm_perc=bcdToDec(alarm_perc);}                  //0x09 regiszter balról első bitje 0, visszaadjuk a beállított perc értéket
  alarm_ora=Wire.read();                                   //riasztás óra kiolvasás 0x0A "hour alarm" regiszterből
  if(0b10000000 & alarm_ora){alarm_ora=99;}                //ha 0x0A regiszter balról első bitje 1, akkor az óra nem vesz részt a riasztásban (visszadott érték 99)
  else {alarm_ora=bcdToDec(alarm_ora);}                    //0x0A regiszter balról első bitje 0, visszaadjuk a beállított óra értéket
  alarm_nap=Wire.read();                                   //riasztás perc kiolvasás 0x0B "day alarm" regiszterből
  if(0b10000000 & alarm_nap){alarm_nap=99;}                //ha 0x0B regiszter balról első bitje 1, akkor a hónap napja nem vesz részt a riasztásban (visszadott érték 99)
  else {alarm_nap=bcdToDec(alarm_nap);}                    //0x0B regiszter balról első bitje 0, visszaadjuk a beállított hónap napja értéket
  alarm_het_napja=Wire.read();                             //riasztás perc kiolvasás 0x0C "weekday alarm" regiszterből
  if(0b10000000 & alarm_het_napja){alarm_het_napja=99;}    //ha 0x0C regiszter balról első bitje 1, akkor a hét napja nem vesz részt a riasztásban (visszadott érték 99) 
  else {alarm_het_napja=bcdToDec(alarm_het_napja);}        //0x0C regiszter balról első bitje 0, visszaadjuk a beállított hét napja értéket
  Wire.beginTransmission(PCF8563_I2C_cim);                 //megszólítjuk a slave egységet a címével
  Wire.write(0x01);                                        //STATUS2 regiszter címét állítjuk be a slave-en
  Wire.endTransmission();                                  //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 1);                    //megadjuk a slave-nak, hogy küldjön 1 byte-ot, a STATUS2 registert
  byte status2=Wire.read();                                     //beolvassuk a STATUS2 regiszter tartalmát
  if(0b00001000 & status2){alarm_flag=1;}                  //kimaszkoljuk az AF bitet
  else {alarm_flag=0;}
  if(0b00000100 & status2){timer_flag=1;}                  //kimaszkoljuk a TF bitet
  else {timer_flag=0;}
  if(0b00000010 & status2){int_alarm=1;}                   //kimaszkoljuk a AIE bitet
  else {int_alarm=0;}
  if(0b00000001 & status2){int_timer=1;}                   //kimaszkoljuk a TIE bitet
  else {int_timer=0;}
  Wire.beginTransmission(PCF8563_I2C_cim);                 //megszólítjuk a slave egységet a címével
  Wire.write(0x0F);                                        //megadjuk a "timer" regiszter címét
  Wire.endTransmission();                                  //vége az I2C tranzakciónak (íráskor nem mondjuk meg előre, hogy hány byte-ot akarunk írni)
  Wire.requestFrom(PCF8563_I2C_cim, 1);                    //megadjuk a slave-nak, hogy küldjön 1 byte-ot, a timer registert
  timer=Wire.read();                                       //timer regiszter értékének kiolvasása (pillanatnyi érték, közben lehet, hogy épp visszaszámol)
}


byte decToBcd(byte val)  //decimálisból BCD-be alakít
{return ((val/10*16)+(val%10));}

byte bcdToDec(byte val)  //BCD-ből decimálisba alakít
{return ((val/16*10)+(val%16));}



Mennyire volt hasznos amit olvastál? Értékelés után szövegesen is leírhatod megjegyzéseidet és véleményedet!

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet! Ha kérdésed van, ne felejtsd el megadni az email címedet!