AVR ile DS3231 RTC Kütüphanesi ve Kullanımı

Bu makalede biraz daha gelişmiş bir RTC entegresi olan DS3231'i kullanacağız ve bir önceki uygulamaya benzer bir uygulama yapacağız. Yalnız artık bir kütüphane yazma vakti geldi ve temel fonksiyonları kullanacağımız bir kütüphane yazacağız. Öncelikle DS3231 entegresini tanımakla işe başlayalım. Bunun için entegrenin üreticisi olan Maxim’in ilgili sayfasına bakıyoruz.

Burada genel bilgilere baktığımızda bu entegrenin DS1307'den daha özellikli olduğunu görmekteyiz. En büyük özelliği ise dahili TCXO kristalin bulunması ve bu kristal ile sapmaların oldukça düşmesidir. RTC devrelerinde sapmanın asıl sebebi dijital kısım değil bu dijital devreyi besleyen kristaldir. Bu kristal sabit bir frekansta kalmayıp sıcaklığa bağlı olarak değişmektedir. Kristalin kalitesine göre sapmada değişiklik olsa da sapmada en önemli belirleyici kristalin veya kristal osilatörün tipidir.

Bazı kristal osilatörler bu sapmayı kontrol altına almak için ısı odası yöntemine başvururlar. Yani kristalin içindeki bir ısıtıcı ortam sıcaklığını sabit tutar ve sapmayı en aza indirir. TCXO osilatöründe ise bir adet termistör ortam sıcaklığını ölçerek kristaldeki sapmayı dengelemektedir. Normal kristal ile TCXO arasındaki farkı şu grafikte görebilirsiniz.

Bu sadece RTC’ler için değil bazen mikrodenetleyiciler için de gerekli olabilir. Mikrodenetleyicileri genellikle düz kristaller ile beslesek de bazı zamanlama uygulamalarında ve özellikle frekans üretme gibi konularda kristaller çok isabetli olmayabiliyor. Ortam sıcaklığına göre mikrodenetleyicinin çalışma saatinin de değişebileceğini unutmamak gereklidir. Hatırladığım kadarıyla TCXO osilatörü ile yıllık sapma oranı 1–1.3 saatten 2–3 dakikaya kadar düştüğü söylenmekte. Ben elimdeki bir adet DS3231 modülü ile bunu gözlemleyemedim ve sapmanın DS1307'den çok da farklı olmadığını gördüm. Yalnız bunun klon modül olduğunu unutmamak gerekli. Çok doğru gözlemleme şansım olmasa da böyle bir durumda ben klon modüle asla güvenmem.

Üreticinin sayfasında SOIC kılıftaki entegrenin 4.67 dolar olduğunu görmekteyiz.

Sadece entegrenin değil, modülün fiyatına Aliexpress’den bakalım.

Görüldüğü gibi modülün pilsiz fiyatı 1 dolara kadar düşmekte. Üstelik pil entegreden daha pahalıya geliyor. Üretici bunu sayfasında 4.67 dolara satarken burada 1 dolara modülü satın alıp bundan aynı özellikleri beklemek oldukça gülünç olur.

Üstelik Aliexpress’de yer alan orjinal elektronik parçaların Avrupa veya Amerika fiyatlarından çok daha yüksek olduğunu söyleyelim. Mesela bir STM32 geliştirme kartının Amerika fiyatı 15.5 dolar ise Aliexpress’de bu 24 dolara satılmakta. Gerçekten orjinal bir entegreyi almak isterseniz Mouser gibi yerlerden yüksek kargo ücreti ve vergilerle satın almak zorundasınız ya da en uygun seçenek olarak Rsdelivers’den alabilirsiniz.

https://tr.rsdelivers.com/product/maxim-%C4%B1ntegrated/ds3231m/maxim-%C4%B1ntegrated-ds3231m-real-time-clock-serial/1913543

Bazı Çin menşeili sitelerin koca bir elektronik çöplüğü olduğunu ve buradan aldığınız parçalarla profesyonel bir iş yapmanın başınızı oldukça ağrıtacağını asla unutmayın. Daha önce belki çok uygun fiyata biraz araştırmayla kaliteli parçaları bulabilseniz de onun yıllar öncesinde kaldığını ve bütün satıcıların artık “gözünü açtığını” unutmayın. Genel olarak buradan aldığınız elektronik parçaların %30'u çalışmadığından daha alır almaz çöpe gidecektir. Eğer satıcı çok kötü klonları satıyorsa alır almaz çöpe atabilirsiniz. İyi klonlar olsa bile datasheette yazan değerleri size sağlamayabilir. Mesela bir entegre datasheette 4 ampere kadar çalışıyorsa aldığınız klon 2 ampere kadar çalışır. Sonrasında her şeyi doğru yaptım neden bu karttan dumanlar çıktı diye düşünmemek için profesyonel anlamda orijinal ürün kullanmak şart. Eğitim alanında öğrenci bütçesi ve yapılan işin niteliğinden dolayı bu tarz klonları kullanmakta çok sıkıntı olmayacaktır.

DS3231'in yazmaçlarına baktığımızda şöyle bir tablo ile karşılaşmaktayız.

Burada DS1307'ye ilave olarak alartm, artık yıl gibi özelliklerin geldiğini görmekteyiz. Ayrıca 0x11 ve 0x012 yazmaçlarında sıcaklık verisi de yer almaktadır. Ben yapacağım uygulamada çok zamanım olmadığı için sadece tarih verilerini kullandım. Uygulamayı geliştirmeyi size bırakıyorum. Şimdi basit bir saat verisi okuma uygulamasını inceleyelim.

Bu programda saniye_oku() adında bir fonksiyon oluşturdum. Bu oluşturduğum ilk fonksiyon üzerinden farklı fonksiyonlar türetip bir kütüphane haline getireceğim. Şimdi bu fonksiyonun komutlarını inceleyerek devam edelim.

i2c_start(DS3231_ADDR+I2C_WRITE);
i2c_write(0x00);
i2c_stop();

Buraya okuyacağım adres verisini entegreye yazmakta ve iletişimi bitirmekteyim. Bir sonraki okuma işlemi 0x00 adresinden itibaren başlayacak. Biz saniye okumak istediğimiz ve saniye verisinin 0x00'da olmasından dolayı bu değeri yazmaktayız.

i2c_start(DS3231_ADDR+I2C_READ);
unsigned char data = i2c_readNak();
i2c_stop();

Burada okuma modunda iletişimi başlattım ve sadece bir adresi okuyacağım için i2C_readNak() fonksiyonunu kullandım. Şu an data değişkenine hem saniye hem de on saniye değerini aktarmış bulunuyoruz. Bunu düz saniye diye yazdıramamaktayız.

unsigned char sec = data & 0b00001111;
unsigned char secx10 = ((data & 0b01110000) >> 4);

Bunun için yukarıda gördüğünüz gibi sec ve secx10 adında iki değişken oluşturdum ve sec değişkenine AND işlemi uygulayarak sadece son dört biti yani saniye verisini aktardım. On saniye verisini ise secx10 değişkenine aktarmam gerekli. Bunun için data değişkeninde yer alan 6, 5 ve 4 numaralı bitleri en sağa hizalayıp bunu sec10x değişkenine aktardım.

sec += secx10 * 10;
return sec;

Şimdi elimizdeki saniye ve on saniye değerlerini tamsayı haline dönüştürmemiz gerekli. On saniye değerini on ile çarparak saniye değerini ise doğrudan ekleyerek elde ettiğimiz değerleri 45, 30 ve 15 gibi doğrudan okunabilecek saniye değerine dönüştürüyoruz. Bunu unsigned int formatında ekrana yazdırdığımıza dikkat edin.

sprintf(buf,"SANIYE:%u \n",saniye_oku());
uart_puts(buf);

Şimdi bir kütüphane yazıp tamamen tamsayı değerler üzerinden kolayca işlem yapalım.

Bu arada bazen açıklamaların İngilizce bazen ise Türkçe olduğunu fark edebilirsiniz. Genellikle yazılımın ortak dili olan İngilizce ifadeleri kodlarda kullanmaya dikkat etmekteyim. Bu sayede başkaları kodu okurken bir zorluk yaşamamakta, zaten İngilizce bildiğim için benim için anlamakta bir sorun olmamaktadır. İngilizce olması İngilizce bilmeyenler için biraz zorluk ortaya çıkarabilse de bu değişken ve fonksiyon adlarını ve açıklamaları anlamak için yüksek bir seviye İngilizce’ye gerek olmadığını biliniz.

Burada daha önceki uygulamada saniye_oku() olarak yaptığım fonksiyon gibi her tarih değeri için ayrı ayrı fonksiyon yaptım ve aynı zamanda bunlara tam sayı değer girerek yazma imkanı tanıdım. Bu sayede siz program yazarken tamamen tam sayı değerlerle muhatap olacaksınız. Kolayca istediğiniz tarih değerini okuyup üzerinde işlem yapabileceksiniz.

.c dosyası oldukça uzun olduğu için buraya koymayacağım incelemeyi size bırakacağım. Şimdi parça parça bu fonksiyonları inceleyelim.

Burada bir önceki saniye_oku() fonksiyonunun aynısını görebilirsiniz. Tek fark i2c_write(0x01) diyerek 0x00 adresi yerine dakika verisinin yer aldığı 0x01 adresinden okuma yapmamızdır. Her bir okuma için aynı işlemi yapacağımızı beklemeyin. Bit yerleri değiştiği için türlü zorluklar karşımıza çıkacaktır.

Burada 10 saat ve 20 saat bitleri ayrı olduğu için ikisini ayrı ayrı ele aldım. Şimdi incelediğimde buna çok da gerek olmadığını görmekteyim. Şimdi yazma fonksiyonlarını incelemeye başlayalım.

Burada bizim elimizde tamsayı olarak var olan saniye verisini DS3231'in istediği şekilde yazmamız gerekli. Bunun için önce BCD’ye çevirmeli sonrasında ise ilgili bitlere yazmalıyız. Elimizde iki haneli bir saniye verisi var diyelim. Öncelikle bunun en sağdaki basamağını elde etmek, geri kalanını ise sıfırlamak istiyoruz. Bunun için 10'a bölüp kalanı elde etmemiz gerekir.

uint8_t secx1 = sec % 10;

Aynı saniye verisinde onlar basamağını elde etmek içinse bölümü elde etmemiz gereklidir. 10'a böldüğümüz zaman hem kalan bir kenara atılacak hem de elde ettiğimiz sayının basamağı bir eksilecektir.

uint8_t secx10 = sec / 10;

Şimdi elde ettiğimiz verileri yazacağımız adresteki uygun bitlere yerleştirmek için bit kaydırma ve OR operatörünü kullanacağız.

uint8_t data = (secx10 << 4) | (secx1);

Fark ettiyseniz okuma fonksiyonundaki işlemin tersini yaptık. Aynı şekilde diğer fonksiyonlarda da bunu uygulamaktayız. Kütüphanenin geri kalanını (AVRADV15…) uygulamasından inceleyebilirsiniz.

--

--