DS1307 óra IC kezelése

 

2019-03-10 - Kiegészítés a chip vizsgálatával

Másnap, hogy elkészültem az alábbi írásommal, rábukkantam egy oldalra, amin egy I2C Scanner program található. Ennek alapján készítettem egy "chktwi()" ellenőrző függvényt, szóval lehet hogy elkerülhető a kékhalál, amit emlegettem.

2019-03-10

Hol is kezdjem? Most ugye az óra IC-s küzdelem végén vagyok. Az elején feltettem magamban, hogy nem fogok vacakolni, letöltök egy library-t, aztán kész. Találtam is vagy háromfélét, csak egyik sem akart működni. Gyanakodtam én, hogy nem bennük van a baj, bennem persze nem lehet, hanem kizárásos alapon a kis áramkörömben, ami Kínából érkezett, "Data logging board" névre hallgat. Van rajta egy DS1307 IC, ami egy naptár és óra IC, és mivel maradt még egy kis hely a chip-en, tettek még rá 56 byte RAM-ot is. Van rajta egy foglalat, 3V-os CR1220 gombelemnek. Azután van rajta egy micro SD foglalat, amire tulajdonképpen elég sok adatot lehet rögzíteni. Az óra IC-hez van rajta egy 32kHz-es quartz, az SD kártyához, meg egy 3.3V-os fesz szabályzó. És mindez alig valamivel többért mint 2 USD...

A kis panel kifogástalan, szépen fel van íratozva. Sajnos a web címen semmi infó nincs róla. Elem nélkük érkezik. Az SD kártya SPI-n keresztül érhető el, a DS1307 I2C-n, Atmel szóhasználatban TWI-n keresztül. Az SD kártával majd később bírkózok meg.

Így néz ki a DS1307 kapcsolása:

A prospektus azt írja, hogy az IC-ben van egy áramkör (dióda?), ami a tápfeszültség megszüntekor rendkívűl gyorsan átkapcsol az elemre, és így biztosított az IC folyamatos működése. Mivel elemem kezdetben nem volt, gondoltam ha kísérletezés közben elmegy az áram, nem az a legnagyobb baj, hogy az IC újraindul. Fogtam maga, egyArduino Nano-t rádugtam az USB-re, ahhoz meg hozzákötöttem a kis panelt. Csak három napomba került, hogy rájöjjek, elem nélkül az IC nem mükszik. Tettem bele elemet oszt jól van. Sajnos nem. Egyszer majd kifogy az elem, és az Arduino-nak amilyen sügén van megírva az (I2C) TWI busz kezelése, simán megakad, ha egy lekérdezett TWI eszköztől nem kap választ. Három megoldása lenne a dolognak, csak annyire nem voltam lelkes, hogy le is programozzam. Felprogramozunk egy számlálót, hogy a beállított idő letelte után küldjön egy megszakítást, és akkor megszakítjuk a várakozást. Másik megoldás az AVR-ekbe beépített WatchDog használata, ami hasonló elven működik, mint az első megoldás. Az I2C master vezérlés leprogramozása nem túl nehéz, tehát a harmadik megoldás az lehet, hogy a saját programunkban figyelünk rá, hogy ha adott időn belül nem kapunk választ, akkor baj van. Mivel kezdetben nem működött a cucc, kénytelen voltam megnézegetni, mi van a library-kben. Most már biztos, hogy velem van a baj. Rémületes. Az összes megoldás az Arduino "Wires" library-ra épül, ami a változatosság kedvéért a "c:\Arduino\hardware\arduino\avr\libraries\Wire\" helyen található. Csodák vannak beleírva, aminek én a töredékét sem fogom soha használni. Kicsit olyan lar pour l'art, pontosabban esetünkben a programozás a programozás kedvéért. A Wire library egyébként a "c:\Arduino\hardware\arduino\avr\libraries\Wire\src\utility\" -ben található TWI függvénykészletet használja, amik szimpla C függvények. Ezeket most nem írtam újra, megelégedtem a Wire objektum használatával, és ha egyszer kifogy az elem, úgy megáll a programom, hogy még egy kékhalált sem tud dobni. A következő ábra mutatja az IC regisztereit, és hogy melyiben mit találunk.

Valójában nem olyan rémületes mint amilyennek látszik. Persze az ember sokkal jobban elbizonytalanodik, ha letölthető library-k alapján próbál rájönnin, melyikkel mit kellene csinálni. A regiszterekből BCD formátumban lehet az értékeket kiolvasni. A BCD egy olyan formátum, amikor 4 biten csak 0..9 közötti értéket tárolunk. Valamikor ugye 10-es számlálókat használtunk, amikor még nem volt uP-nk, hogy emberi fogyasztásra alkalmasan kiírhassuk a számlálók tartalmát. Tehát az alsó négy biten vannak az egyesek (0..9), a felsőn a tizesek (0..90). Kezdjük hátulról :). A Control regiszternek esetünkben nincs jelentősége, akit érdekel olyassa el a prospektust. Azután jönn Years (évek), elég egyértelmű, kiolvashatjuk, és felül is írhatjuk a tartalmát. Az IC évente növeli az értékét. A Months (hónapok) dettó, nyilván az értéke havonta nő. A napok Date álnevet kaptak. Azért volt erre szükség, mert a hét napjai kerültek a Day nevű regiszterbe. Ez szimplán naponta ugrik egyet, a hét napjainak számontartására lehet használni. Teljesen tetszőleges melyik számot melyik napnak feletetjük meg. Kicsit visszatérve a Date regiszterhez, nem tudom a hónap napjait milyen pontosan követi, ugye a február 28-29/szökőév kezelést egyszer meg kellene nézni. A Hours (órák) regiszterben van egy őrültség, a 6. bit. Törne le a ceruzája annak a mérnöknek aki ilyet tervez. A 6. bitbe 0-t írva 24 órásra álítjuk a számlálót, 1-et írva 12 órásra, és ilyenkor az 5. bit jelzi ki, hogy délelőtt, vagy délután van. Én 24 órás beállítást használtam. A Minutes (percek) megint egyértelmű. A Seconds (másodpercek) 7. bitje megint egy dupla hülyeség, ha már ilyet akartak, miért nem tették a Control regiszterbe? Szóval ha 1-et írunk bele, leáll az oszcillátor, és nagyon takarékosan nem megy az óra. ???

Szóval azok a csacska (őrülten elb.) library-k helyett itt találtok egy egyszerűen kezelhető objektumot. A now() függvény beolvassa a DS1307 összes regiszterét az objelktum változóiba, és megteszi azt a szívességet is, hogy BCD-ből átváltja őket szimpla binárisba. Ezeket ugye tetszőlegesen olvashatjuk, átírhatjuk, és a set() függvény pedig szépen mindet visszaírja a DS1307-ba. A RAM cellákat a read()-del lehet olvasni, és a write()-tal lehet írni. Használatukra mintát a Setup() és a Loop() részben találtok.


/******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2019.03.10.
*   Chip         -  Ardiono, DS1307 real time clock
*   Compiler     -  Arduino IDE
*
*   DS1307 ora IC kezelese
*   
*******************************************************************************
*    TWI pins:
*                  Uno     Mega     Nano
*    SDA            A4       20       A4
*    SCL            A5       21       A5
*
*******************************************************************************/

#include <Arduino.h>
#include <Wire.h>


#define DS1307_ADDRESS  0x68


class kt_ds1307
{
   public:
   byte years;                                   // 0..99
   byte months;                                  // 1..12
   byte days;                                    // 1..31
   byte days_of_week;                            // 1..7
   byte hours;                                   // 0..23
   byte minutes;                                 // 0..59
   byte seconds;                                 // 0..59
   kt_ds1307( void);
   void now( void);                              // read date+ time
   void set( void);                              // set date+ time
   byte read( byte address);                     // read RAM 0..55 (8..63)
   void write( byte address, byte data);         // write RAM 0..55 (8..63)

   private:
   byte bcd2bin( byte n);
   byte bin2bcd( byte n);
};


byte kt_ds1307::bcd2bin( byte n)
{
   return ((n & 0B11110000) >> 4)* 10+ ( n & 0B00001111);
}


byte kt_ds1307::bin2bcd( byte n)
{
   return  ((n/ 10) << 4)+ ( n % 10);
}


kt_ds1307::kt_ds1307( void)
{
   Wire.begin();
}


void kt_ds1307::now( void)
{
   Wire.beginTransmission( DS1307_ADDRESS);
   Wire.write( (uint8_t)0);
   Wire.endTransmission();
   Wire.requestFrom( DS1307_ADDRESS, 7);
   seconds= bcd2bin( Wire.read());
   minutes= bcd2bin( Wire.read());
   hours=   bcd2bin( Wire.read());                                   
   days_of_week=     Wire.read();
   days=    bcd2bin( Wire.read());                                   
   months=  bcd2bin( Wire.read());                                   
   years=   bcd2bin( Wire.read());                                   
   Wire.endTransmission();
}


void kt_ds1307::set( void)
{
   if( seconds > 59) seconds= 0;                 // limits
   if( minutes > 59) minutes= 0;
   if( hours > 23) hours= 0;
   if( days_of_week > 7 || days_of_week < 1) days_of_week= 1;
   if( days > 31 || days < 1) days= 1;
   if( months > 12 || months < 1) months= 1;
   if( years > 99) years= 0;
   Wire.beginTransmission( DS1307_ADDRESS);
   Wire.write( (byte)0);                         // start at location 0
   Wire.write( bin2bcd( seconds));
   Wire.write( bin2bcd( minutes));
   Wire.write( bin2bcd( hours));                 // implicit set 24 hours mode
   Wire.write( days_of_week);
   Wire.write( bin2bcd( days));
   Wire.write( bin2bcd( months));
   Wire.write( bin2bcd( years));
   Wire.write( 0);                               // controll register
   Wire.endTransmission();
}


byte kt_ds1307::read( byte address)
{
   byte a;
   if( address > 55) address= 0;
   Wire.beginTransmission( DS1307_ADDRESS);
   Wire.write( address+ 8);
   Wire.endTransmission();
   Wire.requestFrom( DS1307_ADDRESS, 1);
   a= Wire.read(); 
   Wire.endTransmission();
   return a;
}


void kt_ds1307::write( byte address, byte data)
{
   if( address > 55) address= 0;
   Wire.beginTransmission( DS1307_ADDRESS);
   Wire.write( address+ 8);
   Wire.write( data);
   Wire.endTransmission();
}


void kiir( byte n)
{
   if( n <10) Serial.print( "0");
   Serial.print( n);  
}


byte chktwi( byte address)
{
   byte a= 0;  
   Wire.beginTransmission( address);
   if ( Wire.endTransmission() == 0) a= address;
   return a;
}


kt_ds1307 rtc= kt_ds1307();


void setup()
{
   Serial.begin( 9600);
   Serial.println( "RTC demo");
   if( chktwi( DS1307_ADDRESS))
   {
      Serial.print( "DS1307 chip (at ");
      Serial.print( DS1307_ADDRESS);
      Serial.println( ") ready!");
   }
   else
   {
      Serial.print( "DS1307 chip (at ");
      Serial.print( DS1307_ADDRESS);
      Serial.println( ") not ready!");
      while( 1);
   }
   Serial.println();

   rtc.write( 0, 0);
   Serial.print( "memory 0 cell= ");
   Serial.println( rtc.read(0));
   rtc.write( 0, 255);
   Serial.print( "memory 0 cell= ");
   Serial.println( rtc.read(0));
   Serial.println();

   rtc.years= 19;
   rtc.months= 12;
   rtc.days= 31;
   rtc.hours= 23;
   rtc.minutes= 59;
   rtc.seconds= 50;
   rtc.set();
}


void loop()
{
   rtc.now();
   Serial.print( "20");
   kiir( rtc.years);
   Serial.print( "/");
   kiir( rtc.months);
   Serial.print( "/");
   kiir( rtc.days);
   Serial.print( " ");
   kiir( rtc.hours);
   Serial.print( ":");
   kiir( rtc.minutes);
   Serial.print( ":");
   kiir( rtc.seconds);
   Serial.println( "");
   delay( 1000);
}

Ennyi az egész, itt a vége, fuss el véle.