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.