I2C haberleşme protokolü SDA ve SCL sinyal hatları, master-slave mimarisi ve timing diyagramının teknik gösterimi

I2C Haberleşme Protokolü: Gömülü Sistemler için Kapsamlı Teknik Rehber

Yayınlanma Tarihi: 15 Haziran 2025Yazar: Abdullah Gülderen
Etiketler:i2cembedded systemsmikroişlemciarduinoraspberry pistm32haberleşme protokolüserial communicationelectronicsiotsensöreeprommaster-slavedebuggingoscilloscopelogic analyzerc programmingpythonhal librarysmbusphilipsnxpopen drainpull-up resistorclock stretchingarbitrationack nackstart stop conditionsda scltiming diagramelectrical characteristicsnoise immunitybus recoverymulti-masteraddress space7-bit addressing10-bit addressingfast modehigh speed modespi comparisonuart comparisonpmbusi3cautomotiveindustrial automationconsumer electronicsmedical devicessecurityfault tolerancebest practicestroubleshootingperformance optimizationcode examplespractical applicationstechnical documentationembedded programminghardware designpcb designsignal integrityemireal-time systems

// import P5Sketch from ’@/components/P5Sketch.astro’; // Astro bileşen importu kaldırıldı. Eğer React bileşeni olarak kullanılacaksa uygun şekilde eklenmelidir. import [object Object] from ‘gsap’;

I2C Haberleşme Protokolü: Kapsamlı Teknik Rehber


** I²C Protokolü: Gömülü Sistemlerin Vazgeçilmez İletişim Dili**

Gömülü sistemler, günümüz teknolojisinin temel taşlarından biridir. Akıllı telefonlardan otomobillere, ev aletlerinden endüstriyel otomasyon sistemlerine kadar her yerde karşımıza çıkan bu sistemler, birden fazla bileşenin koordineli çalışmasıyla karmaşık görevleri yerine getirir. Peki, tek bir baskı devre kartı (PCB) üzerinde yer alan bir mikrodenetleyici, çok sayıda sensör, bir bellek yongası ve bir motor sürücü nasıl olur da uyum içinde çalışır? Tüm bu bileşenlerin eş zamanlı ve hatasız çalışabilmesinin temelinde, aralarındaki iletişimi standardize eden haberleşme protokolleri yer alır.


Neden Bir Haberleşme Protokolüne İhtiyaç Duyarız?

Modern bir elektronik tasarımda, farklı görevlere sahip onlarca bileşenin birbiriyle veri alışverişi yapması gerekir. Haberleşme protokolleri bu sürecin standart, verimli ve güvenilir olmasını sağlar.

  • Fiziksel Kaynak Optimizasyonu: Gömülü sistemlerde her pin ve her milimetrekarelik PCB alanı değerlidir. Protokoller, çok sayıda cihazı minimum hat kullanarak (örneğin I²C’de sadece iki hat) bağlamaya olanak tanır. Bu, pin sayısını, kablo karmaşıklığını ve üretim maliyetini ciddi ölçüde azaltır.
  • Standartlaşma ve Birlikte Çalışabilirlik: Standart protokoller, farklı üreticilerin geliştirdiği bileşenlerin “aynı dili konuşmasını” sağlar. Bu sayede bir mühendis, farklı markalara ait bileşenleri aynı veriyolu üzerinde sorunsuzca kullanabilir.
  • Veri Bütünlüğü ve Senkronizasyon: Veri aktarımının güvenilirliği kritiktir. Protokoller, saat sinyalleri ile senkronizasyonu ve ACK/NACK gibi geri bildirim mekanizmalarıyla veri bütünlüğünü garanti altına alır.
  • Verimli Güç Yönetimi: Özellikle pille çalışan cihazlarda, modern protokoller bileşenlerin ihtiyaç duyulmadığında düşük güç modlarına geçmesine olanak tanıyarak enerji tasarrufu sağlar.

Örnek Senaryo: Günümüzün modern bir akıllı saatini düşünelim. Bu küçük cihazın içinde, adımlarınızı sayan bir ivme ölçer, kalp atış hızınızı ölçen bir optik sensör, ekran komutlarınızı algılayan bir dokunmatik denetleyici ve batarya durumunu bildiren bir güç yönetim çipi bulunur. Bu bileşenlerin her biri, sürekli olarak ana işlemciyle iletişim kurmak zorundadır. Tüm bu farklı görevlerin, tek bir mikrodenetleyici tarafından, çoğunlukla aynı iki kablo (I²C veri yolu) üzerinden yönetilmesi, haberleşme protokollerinin getirdiği standartlaşma ve kaynak optimizasyonu olmadan neredeyse imkânsız olurdu.

Bu temel ihtiyaçlar doğrultusunda geliştirilen en popüler ve zarif çözümlerden biri de I²C’dir.


I²C’nin Doğuşu: 1980’lerin Karmaşasına Basit Bir Çözüm

I²C (Inter-Integrated Circuit) protokolünün temelleri, 1982 yılında Philips Semiconductor (şimdiki adıyla NXP Semiconductor) tarafından, o dönemin elektronik tasarım zorluklarına bir yanıt olarak atıldı 12. 1980’lerde televizyon gibi tüketici elektroniği ürünleri giderek daha fazla özellik kazanıyor, bu da PCB üzerindeki entegre (çip) sayısını artırıyordu.

O dönemin temel sorunu, her bir çipin diğeriyle iletişim kurmak için çok sayıda paralel veri hattı gerektirmesiydi. Bu durum, hem PCB tasarımlarını aşırı karmaşık hale getiriyor hem de maliyeti artırıyordu. Philips mühendisleri bu soruna devrimci bir çözüm getirdi: Tüm iletişimi yalnızca iki kablo üzerinden yürüten bir protokol.

  • SCL (Serial Clock Line): Veri aktarımını senkronize eden saat sinyalini taşır.
  • SDA (Serial Data Line): Asıl veriyi ve cihaz adreslerini taşır.

Bu iki hatlı “veri yolu (bus)” mimarisi, birden fazla cihazın aynı hatlara paralel olarak bağlanmasına izin veriyordu. Bir denetleyici cihaz, benzersiz bir adres göndererek hangi hedef cihazla konuşacağını seçebiliyordu. Bu yalın yaklaşım, I²C’nin doğuş amacını, yani basitlik, maliyet azaltma ve esnekliği mükemmel bir şekilde karşılıyordu 1.


I²C’yi Farklı Kılan Teknik Felsefe

I²C’nin 40 yılı aşkın süredir popülerliğini korumasının ardında yatan birkaç temel tasarım kararı vardır:

  • Adresleme Mekanizması: SPI gibi protokollerin aksine her cihaz için ayrı bir “Chip Select” pini gerektirmez. Bunun yerine, 7-bit veya 10-bit adresleme kullanarak teorik olarak binden fazla cihazın aynı iki hatta bağlanmasına olanak tanır.
  • Open-Drain Yapısı: Bu tasarım, farklı voltaj seviyelerinde çalışan cihazların (örneğin 3.3V bir sensör ile 5V bir mikrodenetleyici) aynı veri yolunda sorunsuzca iletişim kurmasını sağlar. Ayrıca, birden fazla cihazın aynı anda hattı sürmeye çalışmasını engelleyerek “Multi-Master” desteğine zemin hazırlar.
  • Clock Stretching: Protokol, yavaş hedef cihazlara söz hakkı tanır. Eğer bir hedef cihaz veriyi işlemek için hazır değilse, saat hattını (SCL) geçici olarak “LOW” seviyede tutarak denetleyiciyi bekletebilir. Bu, sistem senkronizasyonu için büyük bir esneklik sağlar.

Yıllar İçinde Evrim: Hız ve Yeteneklerin Artışı

I²C protokolü, doğduğu günden bu yana yerinde saymadı ve sürekli olarak gelişti.

  • Başlangıç (1982): Orijinal spesifikasyon, yalnızca 100 kHz iletişim hızını ve 7-bit adreslemeyi destekliyordu 2.
  • İlk Genişleme (1992): Yayınlanan ilk halka açık spesifikasyonla birlikte 400 kHz’lik “Hızlı Mod” (Fast-mode) ve kapasiteyi artıran 10-bit adresleme alanı eklendi 2.
  • Modern Hızlar: Teknolojinin ilerlemesiyle birlikte çok daha yüksek hızlara sahip modlar da standartlaştırıldı 2:
    • Fast-mode plus: 1 MHz
    • High-speed mode (Hs-mode): 3.4 MHz
    • Ultra-fast mode (UFm): 5 MHz

Bu evrim, I²C’nin basit uygulamalardan yüksek hız gerektiren modern sistemlere kadar geniş bir yelpazede kullanılabilmesini sağlamıştır.


Sonuç: Neden Hâlâ I²C?

1982’de televizyon devrelerindeki kablo karmaşasını çözmek için doğan I²C, basitliği, iki hatlı mimarisi ve çoklu cihaz desteği sayesinde günümüz elektroniğinin vazgeçilmez bir parçası haline gelmiştir. 2006’dan bu yana lisans gerektirmemesi, endüstride evrensel olarak benimsenmesini sağlamıştır 1.

Öğrenme ve uygulama kolaylığı, sensör entegrasyonlarındaki ezici üstünlüğü ve 40 yılı aşan kanıtlanmış geçmişiyle I²C, IoT cihazlarından endüstriyel otomasyona kadar her alanda karşımıza çıkmaya devam edecektir. Kısacası, gömülü sistemlerin bu güvenilir yapı taşı, daha uzun yıllar boyunca önemini koruyacak gibi görünüyor.


** I²C Protokolünün Temel Özellikleri**

Peki, I²C bu kadar az kaynakla bu kadar çok işi nasıl başarıyor? Protokolün gücü, birbiriyle mükemmel uyum içinde çalışan birkaç temel tasarım prensibinde gizlidir. Şimdi bu prensiplere tek tek, görsellerin yardımıyla daha yakından bakalım.

1. İki Telli Seri Haberleşme (SDA ve SCL)

I²C’nin en temel özelliği, tüm iletişimi yalnızca iki hat üzerinden gerçekleştirmesidir. Bu, pin sayısını ve kablo karmaşıklığını azaltarak donanım tasarımını inanılmaz derecede basitleştirir.

  • SDA (Serial Data Line - Seri Veri Hattı): Verinin kendisi bu hat üzerinden taşınır.
  • SCL (Serial Clock Line - Seri Saat Hattı): Veri aktarımını senkronize eden saat sinyalini taşır.
i2c temel bağlantısı

Şekil 2.1: Temel I²C Bağlantısı

Görselde de görebileceğiniz gibi, bir denetleyici ile bir çevre birimi arasındaki tüm iletişim bu iki hat üzerinden akar. Veriler, SCL hattının belirlediği ritimle SDA hattı üzerinden tek tek (seri olarak) gönderilir.


2. Master-Slave Mimarisi

I²C veriyolu üzerindeki iletişim, hiyerarşik bir yapı olan Master-Slave (Efendi-Köle) mimarisine dayanır.

  • Master: İletişimi başlatan, saat sinyalini (SCL) üreten ve hangi Slave ile konuşulacağını belirleyen cihazdır.
  • Slave: Master tarafından kendisine seslenildiğinde yanıt veren cihazdır. Kendi başına iletişim başlatamaz.
i2c sinyal yönleri

Şekil 2.2: I²C Sinyal Yönleri

Bu mimari sayesinde veriyolunda bir düzen oluşur. Master cihaz, orkestra şefi gibi davranarak tüm veri trafiğini yönetir. Görselde Master cihazın SCL hattını tek yönlü olarak nasıl kontrol ettiğine, SDA hattının ise veri alışverişi için nasıl çift yönlü kullanıldığına dikkat edin.


3. Çok Cihazlı Veriyolu ve Adresleme

I²C’nin asıl gücü, aynı iki hatta onlarca cihazı bağlayabilme yeteneğidir. Bu, “veriyolu (bus)” mimarisi olarak adlandırılır. Peki Master, aynı hatta bağlı onca cihazdan doğru olanla nasıl konuşur? Cevap: Adresleme.

çoklu i2c cihaz bağlantı şeması

Şekil 2.3: Çoklu I²C cihaz bağlantı şeması

Şemamızda gördüğünüz gibi, tüm cihazlar aynı iletişim hatlarını paylaşır. Master bir iletişim başlatmak istediğinde, önce hedef Slave cihazın benzersiz adresini veriyoluna gönderir. Sadece adresi eşleşen Slave cihaz bu çağrıya yanıt verir ve iletişime hazırlanır. Diğerleri ise bir sonraki çağrıya kadar pasif kalır. Bu sayede ek pinlere ihtiyaç duymadan seçici iletişim mümkün olur.


4. Teknik Sır: Açık Kolektör (Open-Drain) Mimarisi

I²C’nin esnekliğinin ardındaki en büyük sır, açık kolektör (open-drain) mimarisidir. Bu mimarinin tek ve çok önemli bir kuralı vardır:

Cihazlar, iletişim hattını yalnızca ‘0’ (Düşük) seviyesine çekebilir, asla ‘1’ (Yüksek) seviyesine itemez.

Peki hat nasıl ‘1’ oluyor? Cevap, hatta bağlı olan pull-up dirençlerinde gizli.

Aşağıdaki animasyon, bu prensibi adım adım canlandırıyor. Animasyonu izlerken renk değişimlerine ve sol alttaki MOSFET durum etiketine özellikle dikkat edin:

Frame 1
Frame 2
Frame 3
Frame 4
1 / 4

Şekil 2.4: Mosfet’in iletim ve kesimde olma durumuna göre hattın geriliminin değişimi.

1. Durum: Hat Yüksek Seviyede (Mantıksal ‘1’)

Animasyonda M₁ MOSFET Durumu: kesimde yazdığını göreceksiniz.

  • Ne Oluyor? I²C Lojik birimi, MOSFET’in kapısına (gate) düşük bir sinyal gönderir (kapıya giden mavi çizgi).
  • Sonuç: Bu düşük sinyal, M1M_1 MOSFET’ini kesime sokar, yani bir açık anahtar gibi davranmasını sağlar. Artık hatta bağlı tek aktif eleman olan pull-up direnci (Rc¸ekme-direnciR_{\text{çekme-direnci}}), SDA/SCK hattını VCC’ye doğru çekerek kırmızı renkle gösterilen yüksek (‘1’) seviyesine ulaştırır.

2. Durum: Hat Düşük Seviyede (Mantıksal ‘0’)

Şimdi M₁ MOSFET Durumu: iletimde yazdığı ana bakalım.

  • Ne Oluyor? Bu kez I²C Lojik birimi, kapıya yüksek bir sinyal gönderir (kapıya giden kırmızı çizgi).
  • Sonuç: Bu yüksek sinyal, M1M_1‘i iletime geçirir ve onu toprağa bağlı kapalı bir anahtar haline getirir. Artık hat, pull-up direncine rağmen doğrudan toprağa bağlandığı için voltajı sıfırlanır. Bu durum, hattın mavi renge dönmesiyle gösterilir.

Bu basit ama dahiyane tasarım, birden fazla cihazın aynı anda hattı ‘0’ yapmaya çalışsa bile birbiriyle çakışmasını veya kısa devreye neden olmasını engeller. Peki bu tam olarak nasıl olur?

Bu dahiyane tasarımın bir adım ötesine geçelim ve iki cihazın aynı anda hattı sıfıra çekmeye karar verdiği o anı hayal edelim.

Senaryo 1: İki Cihaz da Aynı Anda ‘0’ Gönderiyor

  • Cihaz A, kendi MOSFET’ini iletime geçirerek hattı toprağa çeker.
  • Cihaz B, aynı anda kendi MOSFET’ini iletime geçirerek o da hattı toprağa çeker.

Sonuç: Elektriksel olarak, SDA hattına paralel bağlanmış iki kapalı anahtar oluşur. Her ikisi de hattı toprağa bağladığı için, sonuç değişmez: Hat kesin bir şekilde ‘0’ seviyesindedir. Bir kısa devre veya voltaj çakışması olmaz, çünkü her iki cihaz da aynı şeyi yapmaktadır ve hiçbiri hatta zıt bir voltaj (örneğin +5V) basmaya çalışmaz.

Senaryo 2: Asıl Sihrin Gerçekleştiği An (Wired-AND Mantığı)

İşte I²C’nin gücünün ortaya çıktığı yer burasıdır. Bir cihaz ‘1’ gönderirken diğeri ‘0’ gönderirse ne olur?

  • Cihaz A ‘1’ göndermek istiyor. Bunu yapmak için MOSFET’ini kesime sokar (anahtarı açar) ve hattın pull-up direnci tarafından ‘1’e çekilmesine izin verir.
  • Cihaz B ise tam o anda ‘0’ göndermek istiyor ve MOSFET’ini iletime geçirir (anahtarı kapatır).

Sonuç: Cihaz B’nin kapattığı anahtar, Cihaz A’nın açık anahtarını “geçersiz kılar”. Hat, Cihaz B üzerinden toprağa çekildiği için sonuç yine kesin bir şekilde ‘0’ olur. ‘0’ her zaman baskındır!

Buna elektronikte “Wired-AND” (Kablolu VE) mantığı denir. Yani, hat sadece ve sadece hatta bağlı TÜM cihazlar ‘1’ göndermek istediğinde ‘1’ seviyesinde olabilir. Eğer tek bir cihaz bile ‘0’ çekerse, tüm hat ‘0’ olur.

Bu Neden Bu Kadar Önemli?

Bu “sıfırın baskınlığı” kuralı, I²C’ye iki kritik özellik kazandırır:

  1. Çok Efendili Yönetim (Multi-Master Arbitration): Eğer iki “Master” cihaz aynı anda konuşmaya başlarsa, ‘1’ göndermeye çalışırken hattın ‘0’ kaldığını gören cihaz, hattı başka birinin kullandığını anlar, “kaybettiğini” kabul eder ve hemen susarak diğerinin konuşmasını bitirmesini bekler. Bu, veri bozulmasını engelleyen zarif bir trafik kuralıdır.

  2. Saat Esnetme (Clock Stretching): Eğer yavaş bir “Slave” cihaz, Master’ın gönderdiği veriyi işlemek için daha fazla zamana ihtiyaç duyarsa, Saat (SCL) hattını geçici olarak ‘0’da tutar. Master, saati ‘1’ yapmaya çalışsa bile hattın ‘0’ kaldığını görür ve “Anlaşıldı, bekliyorum” diyerek Slave cihaz hattı serbest bırakana kadar duraklar.

Kısacası, bu tasarım sadece bir güvenlik önlemi değil, aynı zamanda I²C protokolünün en gelişmiş özelliklerinin temelini oluşturan aktif bir mekanizmadır. İşte I²C’yi bu kadar esnek ve popüler yapan sır tam olarak budur.

3. Fiziksel Katman ve Donanım

I²C protokolünün yazılım katmanına dalmadan önce, bu sihrin gerçekleştiği fiziksel dünyayı anlamamız gerekiyor. Teller, voltajlar ve dirençler… İşte I²C’nin kalbinin attığı yer burasıdır.

3.1 Sinyal Hatları

I²C’nin en zarif yanlarından biri sadeliğidir. Karmaşık paralel veriyollarının aksine, I²C tüm iletişimi sadece iki hat üzerinden yürütür.

temel_i2c_donanım_yapısı

Şekil 3.1: Temel I²C Donanım Yapısı

// … MDX içeriğiniz devam eder …

Şekil 3.1’de gördüğünüz gibi, bir “Master” cihaz (genellikle bir mikrodenetleyici) ve birden fazla “Slave” cihaz (sensörler, bellek çipleri vb.) aynı iki tele paralel olarak bağlanır. Bu hatlar şunlardır:

  • SDA (Serial Data Line) - Veri Hattı: Tüm veri aktarımı, master ve slave adresleri, okuma/yazma komutları ve asıl veri bitleri bu hat üzerinden akar. Çift yönlü bir hattır.
  • SCL (Serial Clock Line) - Saat Sinyali Hattı: Veri aktarımının hızını ve zamanlamasını belirleyen saat sinyali bu hat üzerinden gönderilir. Saat sinyali her zaman Master tarafından üretilir.

Pull-up Dirençlerinin Hayati Önemi

Şekil 3.1’de gördüğünüz Rp ile etiketlenmiş pull-up dirençleri, I²C’nin çalışması için opsiyonel değil, zorunludur. Bir önceki bölümde bahsettiğimiz “açık-kolektör” mimarisi sayesinde, cihazlar hattı sadece ‘0’ (Düşük) seviyesine çekebilirler. Hattı ‘1’ (Yüksek) seviyesine döndürme görevi tamamen bu dirençlere aittir.

Pull-Up Direncinin Hayati Rolü

Frame 1
Frame 2
1 / 2

Şekil 3.2: Pull-up Direncinin I²C İçin Önemi.

Şekil 3.2’deki animasyon bu durumu özetliyor:

  1. Bir cihaz hattı serbest bıraktığında (MOSFET kesimde), pull-up direnci hattı +Vcc+V_{cc}‘ye bağlayarak ‘1’ seviyesine çeker.
  2. Bir cihaz ‘0’ göndermek istediğinde (MOSFET iletimde), hattı doğrudan toprağa çekerek pull-up direncini “etkisiz kılar” ve hattı ‘0’ yapar.

Doğru pull-up direnci seçimi, veriyolunun kararlılığı için kritiktir. Çok yüksek değerli bir direnç, hattın yeterince hızlı ‘1’e çıkmasını engeller. Çok düşük değerli bir direnç ise cihaz ‘0’a çekmeye çalıştığında çok fazla akım çekilmesine neden olur. Genel bir kural olarak, çoğu 5V’luk sistem için 4.7kΩ, 3.3V’luk sistemler için ise 2.2kΩ ila 4.7kΩ arası dirençler iyi bir başlangıç noktasıdır.


I²C Bus Kapasitansı ve Maksimum Kablo Uzunluğu: Neden Metrelerce Kablo Kullanamıyoruz?

I²C (Inter-Integrated Circuit), mikrodenetleyiciler, sensörler ve diğer çevre birimleri arasında, özellikle aynı baskı devre kartı (PCB) üzerinde, veri alışverişi yapmak için tasarlanmış zarif ve verimli bir protokoldür. Sadece iki kablo (SDA ve SCL) ile çok sayıda cihazı yönetme yeteneği, onu sayısız elektronik projenin vazgeçilmezi yapar.

Ancak bu basitliğin arkasında, protokolün performansını doğrudan etkileyen kritik bir fiziksel sınır vardır: bus kapasitansı. Peki, bu ne anlama geliyor ve neden I²C hattını birkaç metreden fazla uzatamıyoruz? Gelin, bu konunun teknik detaylarına birlikte inelim.


Analizin Odak Noktası: Kritik Devre Bloğu

Her şey, I²C’nin çalışma prensibini ve fiziksel yapısını anlamakla başlar. Aşağıdaki şema, bir I²C hattının basitleştirilmiş bir modelini göstermektedir.

Bus Kapasitansı

Şekil 3.3: I²C Hattı ve Bus Kapasitansı

Bu devrede dikkat etmemiz gereken birkaç anahtar bileşen var:

  • Pull-Up Dirençleri (RpR_p): SDA ve SCL hatlarını, kimse aktif olarak kullanmadığında varsayılan voltaj seviyesi olan VccV_{cc}‘ye (Lojik 11) çekerler.
  • Open-Drain Yapısı (M1M_1 MOSFET): I²C cihazları, hattı 00 seviyesine çekmek için M1M_1 gibi bir N-Kanal MOSFET kullanır. MOSFET iletime geçtiğinde, hat doğrudan toprağa bağlanır. Hattı 11 yapmak için ise MOSFET’i yalıtıma alır ve görevi pull-up dirençlerine bırakır. Bu yapıya “open-drain” denir.
  • Parazitik Bus Kapasitansı (CbusC_{bus}): İşte en kritik eleman budur. Kullandığımız kabloların kendisi, devre kartı yolları ve hatta cihazların pinleri, SDA/SCL hatları ile toprak (GND) arasında küçük birer kondansatör gibi davranır. Kablo uzadıkça bu “parazitik” kapasitans değeri de artar. CbusC_{bus}, hattaki tüm bu istenmeyen kapasitansların toplamını temsil eder.

Lojik Seviyelerin Oluşumu: Hızlı Düşüş, Yavaş Yükseliş

Devremizi anladığımıza göre, sinyalin nasıl oluştuğuna bakalım:

  1. Lojik 00 (Hattı Aşağı Çekme): Bir cihaz hatta 00 göndermek istediğinde, M1M_1 MOSFET’ini sürer. Akım, idesarji_{desarj} yolunu izleyerek hattaki voltajı çok hızlı bir şekilde toprağa çeker. Bu işlem, MOSFET’in düşük iç direnci sayesinde neredeyse anlıktır.

  2. Lojik 11 (Hattı Serbest Bırakma): Cihaz 11 göndermek istediğinde ise sadece M1M_1 MOSFET’ini kapatır. Artık toprağa giden bir yol yoktur. Bu noktada, pull-up direnci RpR_p üzerinden akan isarji_{sarj} akımı, CbusC_{bus} kapasitörünü VccV_{cc} voltajına doğru şarj etmeye başlar.

Sorun tam da burada ortaya çıkar. RpR_p direnci ve CbusC_{bus} kapasitansı, bir RC devresi oluşturur. Bir RC devresinin şarj olma hızı, zaman sabitesine (τ=RCτ = R * C) bağlıdır. Cbus ne kadar büyükse (yani kablo ne kadar uzunsa), hattın voltajının Lojik 11 seviyesine yükselmesi o kadar uzun sürer.

Yükselen Kenarın Ardındaki Mantık

Bu yavaşlamanın sinyal üzerindeki etkisini aşağıdaki grafikte net bir şekilde görüyoruz. Grafik, farklı kablo uzunlukları için SDA hattı voltajının (vSDA(t)v_{SDA}(t)) zamana göre değişimini karşılaştırıyor.

RC Gecikmesi

Şekil 3.4: I²C Sinyal Yükselme Süresi Grafiği

  • Mavi Eğri (Kısa Kablo / Düşük Kapasitans): CbusC_{bus} değeri düşüktür. Bu nedenle RC zaman sabiti küçüktür ve sinyal hızla yükselir. Voltaj, alıcı cihazın bir sinyali Lojik 11 olarak tanıması için gereken minimum eşik olan VIH(min)VIH(min) seviyesini τ1τ_1 gibi çok kısa bir sürede aşar. İletişim sorunsuzdur.

  • Kırmızı Eğri (Uzun Kablo / Yüksek Kapasitans): Kablo uzadıkça CbusC_{bus} artar, RC zaman sabiti büyür. Sinyalin yükselen kenarı belirgin şekilde “yuvarlaklaşır” ve VIH(min)VIH(min) eşiğine ulaşması τ2τ_2 gibi çok daha uzun bir zaman alır.

Veri Neden Bozulur?

I²C protokolü, saat sinyali (SCL) tarafından belirlenen katı bir zamanlamaya tabidir. Alıcı cihaz, veriyi (SDA) okumak için SCL sinyalinin belirli bir anını bekler. Eğer uzun kablonun neden olduğu yavaş yükselme yüzünden SDA hattı voltajı, okunması gereken o kritik anda VIH(min)VIH(min) seviyesine ulaşamamışsa, alıcı gönderilen 11‘i hatalı bir şekilde 00 olarak yorumlar. Bu durum, veri bütünlüğünü bozar ve iletişimin başarısız olmasına neden olur.

Bu fiziksel sınırlamadan dolayı I²C standardı, toplam bus kapasitansını 400 pF ile sınırlar (Standart ve Hızlı Mod için). Bu da pratikte kablo uzunluğunu genellikle birkaç metre ile kısıtlar.

Kısacası, I²C’nin uzun mesafeler için uygun olmamasının sebebi, kablonun kendisinin sinyali yavaşlatan bir kapasitör gibi davranmasıdır. Başarılı bir I²C iletişimi için bu “gizli” kapasitansı daima kontrol altında tutmak zorundayız.


3.2 Voltaj Seviyeleri: Gönderici Garantisi ve Alıcı Beklentisi

I²C protokolünün en güçlü yanlarından biri, farklı voltaj seviyeleriyle (5V, 3.3V, 1.8V vb.) çalışabilen esnek yapısıdır. Ancak bu esnekliğin sorunsuz işlemesi için, hatta bağlı tüm cihazların “Lojik 1” (HIGH) ve “Lojik 0” (LOW) kavramları üzerinde hemfikir olması gerekir. İşte bu noktada standartlaştırılmış voltaj eşikleri devreye girer.

Aşağıdaki şema, bir I²C iletişiminin temelini oluşturan gönderici ve alıcı arasındaki bu “anlaşmayı” mükemmel bir şekilde özetlemektedir.

I²C Lojik Voltaj Seviyeleri

Şekil 3.5: I²C Lojik Voltaj Seviyeleri

Bu görseli iki temel bölümde inceleyelim: “Alıcı Tarafından Yorumlama” (kurallar) ve “Gönderici Mekanizması” (kurallara uyma eylemi).

Alıcı Tarafından Yorumlama: İletişimin Kuralları

Şemanın sağ tarafı, bir alıcı cihazın hattaki voltajı nasıl yorumlayacağının kurallarını belirler:

  • GARANTİLİ HIGH (V>VIH(min)V > VIH(min)): Alıcı cihaz, voltajı VIH(min)VIH(min) (Minimum Input High Voltage - Minimum Yüksek Seviye Giriş Voltajı) değerinin üzerinde okuduğunda, bunu kesinlikle 11 olarak kabul eder. Bu eşik genellikle 0.7Vcc0.7 * Vcc olarak belirlenmiştir.
  • GARANTİLİ LOW (V<VIL(max)V < VIL(max)): Alıcı cihaz, voltajı VIL(max)VIL(max) (Maximum Input Low Voltage - Maksimum Düşük Seviye Giriş Voltajı) değerinin altında okuduğunda, bunu kesinlikle 00 olarak kabul eder. Bu eşik ise genellikle 0.3Vcc0.3 * Vcc‘dir.
  • TANIMSIZ BÖLGE: VIL(max)VIL(max) ile VIH(min)VIH(min) arasındaki bu kritik bölge, bir “gri alan”dır. Bu aralığa düşen bir sinyalin 00 mı yoksa 11 mi olarak yorumlanacağı belirsizdir ve bu durum kararsızlığa ve veri hatalarına yol açar. Bu nedenle, gönderici cihazın sinyallerini bu bölgeden her zaman uzak tutması gerekir.

Gönderici Mekanizması: Kurallara Uymak

Şemanın sol tarafı, bir gönderici cihazın bu kurallara nasıl uyduğunu ve güvenli sinyalleri nasıl ürettiğini gösterir:

  1. Aktif LOW (Lojik 00 Gönderme):

    • Cihaz, 00 göndermek için dahili MOSFET’ini (M1M1) aktif hale getirir.
    • MOSFET, SDA hattını doğrudan toprağa (GND) çeker. Bu aktif bir çekme işlemidir.
    • Hattaki voltaj (VOLVOL - Output Low Voltage), MOSFET’in küçük iç direnci nedeniyle tam olarak 0V olmaz ama standardın gerektirdiği gibi VOL<VIL(max)VOL < VIL(max) olacak kadar düşük bir seviyeye iner. Bu, sinyalin “GARANTİLİ LOW” bölgesine güvenli bir şekilde yerleşmesini sağlar.
  2. Pasif HIGH (Lojik 11 Gönderme):

    • Cihaz, 11 göndermek için çok daha zarif bir yöntem kullanır: MOSFET’ini kapatır ve hattı “serbest bırakır”.
    • Bu durumda cihaz voltajı aktif olarak yukarı sürmez. Görev, hatta bağlı olan harici pull-up direncine (RpR_p) düşer. Bu direnç, hattı pasif olarak besleme voltajı VccV_{cc}‘ye çeker.
    • Hattaki voltaj (VOHVOH - Output High Voltage), hatta bir yük olmadığı sürece VccV_{cc}‘ye çok yakın bir değere yükselir. Bu değer, alıcının beklediği VIH(min)VIH(min) eşiğinden konforlu bir şekilde daha yüksektir (VOHVcc>VIH(min)VOH ≈ Vcc > VIH(min)). Bu da sinyalin “GARANTİLİ HIGH” bölgesine güvenli bir şekilde yerleştiği anlamına gelir.

Bu yapı sayesinde, göndericinin ürettiği sinyaller ile alıcının beklediği seviyeler arasında bir “gürültü marjı” (noise margin) oluşur. Bu marj, hattaki olası elektriksel gürültüye karşı iletişimin direncini artırarak I²C’yi güvenilir bir protokol yapar.


Farklı Voltaj Seviyelerinde Çalışan Cihazların Bağlanması

Modern elektronikte en sık karşılaşılan senaryolardan biri, farklı çalışma voltajlarına sahip bileşenleri bir araya getirme ihtiyacıdır. Örneğin, klasik bir 5V5V Arduino’yu, 3.3V3.3V ile çalışan yeni nesil bir sensörle aynı I²C hattı üzerinde nasıl konuşturabiliriz? Bu cihazları doğrudan birbirine bağlamak, 5V5V‘luk sinyalin 3.3V3.3V ile çalışan hassas sensörün giriş pinlerine zarar vermesiyle sonuçlanabilir. Bu ciddi riski ortadan kaldırmanın yolu, çift yönlü (bidirectional) bir lojik seviye dönüştürücü kullanmaktır.

Aşağıdaki şemada, bu iş için özel olarak tasarlanmış, tek bir MOSFET üzerine kurulu son derece zarif bir devre gösterilmektedir.

Çift Yönlü Seviye Dönüştürücü

Şekil 3.6: Çift Yönlü Seviye Dönüştürücü

Bu devre, I²C’nin open-drain yapısının avantajlarını kullanarak iki farklı voltaj “dünyası” arasında güvenli ve çift yönlü bir köprü kurar. Devrenin nasıl çalıştığını adım adım inceleyelim:

Temel Prensip: Devrenin sırrı, MOSFET’in (M1M1 ve M2M2) Gate (Kapı) terminalinin her zaman düşük voltaj tarafının beslemesine (VCC1=3.3VVCC1 = 3.3V) bağlanmasıdır.

Durum 1: Hat Boşta (Her İki Tarafta Lojik 11)

Hiçbir cihaz hattı 00‘a çekmediğinde, her iki taraf da kendi pull-up dirençleri (RpR_p) tarafından kendi besleme voltajlarına çekilir.

  • SDA1SDA_1 hattı 3.3V3.3V‘a çekilir.
  • SDA2SDA_2 hattı 5V5V‘a çekilir.
  • Bu durumda MOSFET’in Gate voltajı (3.3V3.3V) ile Source voltajı (SDA1SDA_1, 3.3V3.3V) birbirine eşittir. VGS=0VV_{GS} = 0V olduğu için MOSFET kapalı (OFF) kalır.
  • Sonuç: İki hat birbirinden yalıtılmıştır ve her biri kendi Lojik 11 seviyesinde güvenle bekler.
Durum 2: Düşük Voltajlı Cihaz Hattı 00‘a Çektiğinde (3.3V → 5V Yönü)

3.3V3.3V‘luk bir cihaz SDA1SDA_1 hattını 00‘a (GND) çektiğinde:

  • MOSFET’in Source voltajı  0V~0V olur.
  • Gate voltajı hala 3.3V3.3V‘ta olduğu için, Gate-Source arası voltaj farkı VGS3.3VV_{GS} ≈ 3.3V olur.
  • Bu voltaj, MOSFET’i açmak (ON) için yeterlidir.
  • Açılan MOSFET, SDA2SDA_2 (Drain) ile SDA1SDA_1 (Source) arasında düşük dirençli bir yol oluşturur ve SDA2SDA_2 hattını da 00‘a çeker.
  • Sonuç: 3.3V3.3V tarafındaki Lojik 00 sinyali, güvenli bir şekilde 5V5V tarafına iletilmiş olur.
Durum 3: Yüksek Voltajlı Cihaz Hattı 00‘a Çektiğinde (5V3.3V5V → 3.3V Yönü)

Bu, devrenin en akıllıca çalıştığı kısımdır. 5V5V‘luk bir cihaz SDA2SDA_2 hattını 00‘a çektiğinde:

  • SDA2SDA_2 hattının (MOSFET’in Drain’i) voltajı  0V~0V‘a düşer.
  • Bu sırada SDA1SDA_1 hattı (MOSFET’in Source’u) hala 3.3V3.3V‘tadır.
  • MOSFET’lerin yapısında bulunan ve normalde pasif olan parazitik gövde diyotu (body diode), Source’tan Drain’e doğru yönlüdür. Source voltajı (3.3V3.3V), Drain voltajından ( 0V~0V) daha yüksek olduğu için bu diyot iletime geçer.
  • Diyot, SDA1SDA_1 hattının voltajını, SDA2SDA_2 hattının seviyesine (artı küçük bir diyot düşüşü, örn:  0.6V~0.6V) kadar düşürür. Bu voltaj, 3.3V3.3V‘luk cihaz için geçerli bir Lojik 00 seviyesidir.
  • SDA1SDA_1 hattının voltajı düştüğü anda, bir önceki senaryoda olduğu gibi VGSV_{GS} yükselir ve MOSFET tam olarak iletime geçerek bağlantıyı güçlendirir.
  • Sonuç: 5V5V tarafındaki Lojik 00 sinyali, önce gövde diyotu, sonra da MOSFET kanalı aracılığıyla 3.3V3.3V tarafına güvenli bir şekilde yansıtılır.

Bu basit ama dâhiyane devre sayesinde, farklı voltaj seviyelerinde çalışan cihazlar aynı I²C hattı üzerinde hiçbir risk olmadan, çift yönlü ve sorunsuz bir şekilde haberleşebilir.



4.1 Master-Slave Mimarisi

I²C protokolü, bus üzerindeki cihazlar arasında net bir hiyerarşi kuran Master-Slave mimarisini temel alır. Bu yapı, iletişim süreçlerinin düzenli ve çakışmasız yürütülmesini sağlar. İletişimi başlatan ve saat sinyalini üreten cihaz Master, bu sinyallere yanıt veren cihazlar ise Slave olarak tanımlanır.

Aşağıdaki blok diyagram, tipik bir I²C bus topolojisini göstermektedir.

I²C Master-Slave İlişkisi

Şekil 4.1: I²C Master-Slave İlişkisi

Diyagramda, merkezi bir Mikrodenetleyici Master rolünü üstlenirken, dört farklı çevre birimi Slave olarak yapılandırılmıştır:

  • SSD1306 OLED: Grafiksel veri çıkış birimi. Adres: 0x3C0x3C.
  • DS3231 RTC: Zamanlama verisi sağlayan birim. Adres: 0x680x68.
  • BME280 Sensör: Çevresel veri (sıcaklık, nem, basınç) giriş birimi. Adres: 0x760x76.
  • AT24C256 EEPROM: Uçucu olmayan veri depolama birimi. Adres: 0x500x50.

Tüm cihazlar, ortak SDA (Serial Data) ve SCL (Serial Clock) hatlarına paralel olarak bağlanmıştır. Sistemin deterministik çalışması, Master ve Slave cihazların kesin olarak tanımlanmış rollerine ve sorumluluklarına dayanır.

Master Cihazın Fonksiyonları ve Sorumlulukları

Master cihaz, I²C bus’ının tüm veri transfer operasyonlarını kontrol eder.

  1. Bus Kontrolünün Başlatılması ve Sonlandırılması: Master, bir veri transferi başlatmak için START (S) koşulunu ve transferi sonlandırmak için STOP (P) koşulunu üretir. Bu iki koşul, bus’ın meşgul veya boşta olduğunu belirleyen yegane sinyallerdir.
  2. Saat Sinyalinin (SCL) Üretimi: Veri aktarımı süresince SCL hattındaki saat darbeleri sadece Master tarafından üretilir. Bu, tüm veri bitlerinin senkronizasyonunu ve aktarım hızını (bit rate) belirler.
  3. Slave Adreslemesi: Master, START koşulunu takiben, hedef Slave cihazın 7-bit veya 10-bit adresini SDA hattı üzerinden yayınlar. Diyagramdaki örnekte, Master BME280 sensörü ile iletişim kurmak için 0x76 adresini gönderir. Adresi eşleşmeyen diğer tüm Slave cihazlar, bir STOP koşulu algılanana kadar SDA hattındaki sonraki veri akışını yok sayar.
  4. Veri Transfer Yönünün Belirlenmesi: Adres baytının son biti olan R/W (Read/Write) biti, veri akışının yönünü tayin eder.
    • R/W = 0 (Write): Master’dan Slave’e veri transferi.
    • R/W = 1 (Read): Slave’den Master’a veri transferi.
  5. Veri Transferinin Yönetimi: Master, her bayt transferinin SCL darbeleriyle senkronize bir şekilde gerçekleşmesini sağlar ve transferin sonunda STOP koşulunu üreterek bus’ı serbest bırakır.

Slave Cihazların Fonksiyonları

Slave cihazlar, Master’dan gelen komutlara reaktif olarak yanıt veren pasif birimlerdir.

  • Adres Tanıma: Her Slave cihaz, kendi donanımında veya yazılımında tanımlanmış benzersiz bir adrese sahiptir. Bus üzerinde bir START koşulu algıladıktan sonra gelen adres baytını izler ve kendi adresiyle karşılaştırır.
  • Onay Sinyali (Acknowledge - ACK): Adreslemesi yapılan Slave, adres baytını doğru bir şekilde aldığını ve işleme hazır olduğunu belirtmek için, dokuzuncu SCL darbesi sırasında SDA hattını aktif olarak LOW seviyesine çeker. Bu ACK biti, Master’a iletişimin başarılı bir şekilde kurulduğunu teyit eder. Adresi eşleşmeyen veya meşgul olan bir Slave, hattı HIGH seviyesinde bırakarak bir NACK (Not Acknowledge) sinyali oluşturur.
  • Veri Alımı/Gönderimi: Adreslenen Slave, Master tarafından belirlenen R/W bitine uygun olarak ve SCL saat sinyaliyle tam senkronizasyon içinde, SDA hattı üzerinden veri alır veya hatta veri yazar.

Bu yapısal hiyerarşi, I²C’nin çoklu cihaz ortamlarında (multi-drop bus) çakışma olmaksızın, deterministik ve güvenilir bir şekilde çalışmasını sağlar. Her transfer, Master tarafından başlatılan, adreslenen ve sonlandırılan izole bir işlemdir.


Multi-Master Yapılar ve Arbitration (Öncelik Verme)

I²C protokolü, birden fazla “Master” (Yönetici) cihazın aynı veri hattı üzerinde bulunmasına olanak tanıyan esnek bir yapı sunar. Bu durum, Şekil 4.1’de görüldüğü gibi iki veya daha fazla mikrodenetleyicinin aynı sensör verisine erişmek istediği karmaşık sistemlerde büyük bir avantaj sağlar.

I²C Multi-Master Öncelik Durumu

Şekil 4.1: I²C Multi-Master Öncelik Durumu

Peki, iki Master cihaz aynı anda iletişimi başlatmaya çalışırsa ne olur? Veri bozulur mu? Hayır. Bu sorunun cevabı, I²C’nin “open-drain” (açık kollektör) yapısında ve dâhiyane Arbitration (Öncelik Verme) mekanizmasında yatmaktadır.

Şekil 4.1’deki devrede SDA ve SCL hatları, RpR_p pull-up dirençleri ile +Vcc+V_{cc}ye (+5V+5V) çekilmiştir. Bu şu anlama gelir:

  • 11 göndermek için: Cihaz, hattı serbest bırakır ve pull-up direnci hattı lojik 11 seviyesine çeker.
  • 00 göndermek için: Cihaz, hattı aktif olarak toprağa (GNDGND) çekerek lojik 00 seviyesine indirir.

Bu fiziksel yapı, önceliklendirme mekanizmasının temelini oluşturur. Şekil 4.2, bu önceliklendirme anını adım adım göstermektedir:

I²C Multi-Master Öncelik Durumu

Şekil 4.3: I²C Multi-Master Öncelik Durumu

  1. Başlangıç: İki Master (Master A ve Master B) hattın “Boşta” olduğunu görür ve aynı anda bir “BAŞLAMA Koşulu” göndererek iletişimi başlatır. Bu aşamada bir çakışma yoktur.

  2. Veri Aktarımı: Her iki Master da verilerini (genellikle önce Slave adresini) bit bit göndermeye başlar. Gönderdikleri bitler aynı olduğu sürece (“Adres bitleri ve diğer eşleşen bitler”), hatta bir çakışma olmaz ve iletişim devam eder.

  3. Öncelik Belirleme Anı (Arbitration): Asıl kritik an, iki Master’ın farklı bitler göndermeye çalıştığı andır. Şekildeki “ÖNCELİK BELİRLEME ANI” nda tam olarak bu durum yaşanır:

    • Master B bir 00 göndermek ister ve SDA hattını toprağa çeker.
    • Master A ise bir 11 göndermek ister ve SDA hattını serbest bırakır, pull-up direncinin hattı 11 seviyesine çekmesini bekler.
  4. Sonuç ve Karar:

    • Gerçekleşen Durum: “Open-drain” yapısı nedeniyle, bir cihaz hattı 00‘a çektiğinde, diğerinin 11 yapma girişimi geçersiz kalır. Dolayısıyla, SDA hattındaki gerçek sinyal (“Gerçek Hat”) kaçınılmaz olarak 00 olur.
    • Kontrol ve Karar: I²C kuralı gereği, her Master gönderdiği bitin ardından hattı okuyarak sinyalin doğruluğunu kontrol eder.
      • Master B, hatta 00 okur. Bu, kendi gönderdiği bitle eşleştiği için iletişime devam etme hakkını kazandığını anlar ve “yoluna devam eder”.
      • Master A, hatta 00 okur. Ancak kendisi 11 göndermeyi amaçlamıştı. Bu uyumsuzluk, hattı başka bir Master’ın kontrol ettiğini gösterir. Master A, arbitration’ı kaybettiğini (“Kaybetti!”) anlar, veri göndermeyi hemen durdurur ve hattın tekrar boşa düşmesini bekler.

Bu zarif mekanizma sayesinde, hiçbir veri bozulmaz. Önceliği kazanan Master B, iletişimini kesintisiz bir şekilde tamamlarken, kaybeden Master A sessizce kenara çekilir. Bu süreç, genellikle adresi daha düşük olan cihazın (adres bitlerinde daha erken 00‘a sahip olduğu için) arbitration’ı kazanma olasılığını artırır.


4.2 Adres Yapısı

Bir I²C hattı üzerinde birden fazla cihaz (sensör, bellek, sürücü vb.) bulunabilir. Master cihazın, bu cihazlardan yalnızca biriyle hedefe yönelik bir iletişim kurabilmesi için her Slave cihazın benzersiz bir kimliğe, yani bir adrese ihtiyacı vardır. Tıpkı bir apartmandaki dairelerin kapı numaraları gibi, bu adresler de verinin doğru alıcıya ulaşmasını garanti eder. I²C protokolünde temel olarak iki tür adresleme şeması kullanılır: 7-bit ve 10-bit.

7-bit Adres Sistemi

Bu, I²C dünyasında en yaygın olarak kullanılan adresleme standardıdır. Bu yapıda Master, bir BAŞLAMA (START) koşulu oluşturduktan hemen sonra 8 bitlik bir kontrol baytı gönderir. Bu baytın ilk 7 biti Slave adresini, son biti ise yapılacak işlemin yönünü belirtir.

7-bit adres yapısı

Şekil 4.4: 7-bit adres yapısı

Şekil 4.3’teki iletişim akışını adım adım inceleyelim:

  1. BAŞLAMA (START) Koşulu: Master, veri hattını (SDA) saat hattı (SCL) yüksekken alçak seviyeye çekerek iletişimi başlatır.
  2. Adres Çerçevesi (Address Frame):
    • Master, hedef Slave’in 7-bitlik adresini (Şekilde A6’dan A0’a kadar olan bitler) en anlamlı bitten (MSB) başlayarak hatta sürer.
    • Bu 7 bit ile teorik olarak 2⁷ = 128 farklı cihaz adreslenebilir.
  3. R/W (Read/Write) Biti: 8. bit, veri akışının yönünü tanımlar:
    • R/W = 0 (Write): Master, Slave’e veri yazacak.
    • R/W = 1 (Read): Master, Slave’den veri okuyacak.
  4. ACK/NACK (Acknowledge/Not Acknowledge) Biti:
    • Master, 8 biti (7 adres + 1 R/W) gönderdikten sonra SDA hattını serbest bırakır.
    • Eğer hatta bu adrese sahip bir Slave cihaz varsa ve iletişime hazırsa, SDA hattını 9. saat darbesi sırasında alçak seviyeye çekerek bir Onay (Acknowledge - ACK) sinyali gönderir. Bu, “Adresini aldım ve hazırım” demektir.
    • Eğer hatta o adreste bir cihaz yoksa veya meşgulse, SDA hattı yüksek kalır ve bu durum Onaylanmama (Not Acknowledge - NACK) olarak yorumlanır. Master, iletişimi sonlandırır.

10-bit Adres Sistemi

Elektronik dünyasındaki cihaz çeşitliliği arttıkça, 128 adreslik 7-bit alanı yetersiz kalmaya başladı. Bu ihtiyacı karşılamak üzere, 2¹⁰ = 1024 cihaza kadar adresleme imkanı sunan 10-bit adresleme standardı geliştirildi. Bu sistem, geriye dönük uyumluluğu koruyacak şekilde akıllıca tasarlanmıştır; 7-bit cihazlar, 10-bit bir adres çağrısını özel bir önek sayesinde tanıyıp görmezden gelirler.

10-bit adres yapısı

Şekil 4.5: 10-bit adres yapısı

10-bit adresleme, Şekil 4.5’te detaylandırıldığı gibi iki baytlık bir adresleme süreci gerektirir.

Adım 1: Yazma Modunda Adres Gönderimi (Zorunlu İlk Adım)

Bir Slave’e 10-bit adres ile erişmek için, Master her zaman önce bir yazma işlemi başlatır.

  • 1. Bayt (Tanımlayıcı Bayt):

    • BAŞLAMA koşulunun ardından Master, ilk baytı gönderir.
    • Bu baytın ilk 5 biti (Bit 7’den Bit 3’e) her zaman sabit olan 11110 dizisidir. Bu, I²C spesifikasyonunda “10-bit adresleme göstergesi” olarak ayrılmıştır. Hatta bulunan tüm cihazlar bu öneki gördüğünde, gelen adresin 10-bit olduğunu anlar.
    • Sonraki 2 bit (A9 ve A8), 10-bitlik adresin en anlamlı iki bitidir (MSB).
    • Son bit (Bit 0) olan R/W biti, bu ilk baytta her zaman 0 (Yazma) olmak zorundadır. Bu, Master’ın Slave’e adresin geri kalanını “yazdığını” belirtir.
    • Doğru adrese sahip Slave, bu baytı alınca bir ACK yanıtı verir.
  • 2. Bayt (Adresin Alt 8 Biti):

    • Master, adresin geri kalan 8 bitini (A7’den A0’a) ikinci bayt olarak gönderir.
    • Slave, bu baytı da doğru bir şekilde aldığında ikinci bir ACK yanıtı verir.

Bu iki bayt ve iki ACK sinyalinden sonra, Slave tamamen adreslenmiş olur. Bundan sonra Master, ya veri yazmaya devam eder ya da okuma işlemi için özel bir adım atar.

Adım 2: Okuma İşlemi (Tekrarlanan BAŞLAMA Koşulu)

Master, 10-bit adresli bir Slave’den veri okumak istediğinde, yukarıdaki iki baytlık adresleme sürecini tamamladıktan sonra iletişimi bir STOP koşulu ile bitirmez. Bunun yerine bir Tekrarlanan BAŞLAMA (Repeated START - Şekilde Y-BAŞLAMA) koşulu üretir.

  • Okuma için Adres Çerçevesi:
    • Tekrarlanan BAŞLAMA’dan sonra Master, ilk adres baytını tekrar gönderir: 11110 (önek) + A9A8 (adresin üst bitleri).
    • Ancak bu sefer R/W bitini 1 (Okuma) olarak ayarlar.
    • Zaten adreslenmiş olan Slave, bu sinyali aldığında R/W bitinin ‘1’ olduğunu görür ve artık kendisinden veri okunacağını anlar. Bir ACK yanıtı daha verir.
    • Bu son ACK’den sonra Slave, veri göndermeye başlar ve Master okuma işlemini gerçekleştirir.

Ayrılmış Adresler ve Adres Çakışmaları

I²C adres alanında bazı adresler özel amaçlar için rezerve edilmiştir:

  • 0000 000 (Genel Çağrı - General Call): Master bu adresi kullanarak hatta bağlı tüm cihazlara aynı anda bir mesaj gönderebilir.
  • 1111 0XX (10-bit Adresleme): Yukarıda açıklandığı gibi, bu önek 10-bit adresleme için kullanılır.

Pratikte karşılaşılan en büyük zorluklardan biri adres çakışmalarıdır. Farklı üreticilere ait iki modülün (örneğin bir jiroskop ve bir gerçek zamanlı saat modülü) fabrikada aynı I²C adresiyle (0x68 gibi) ayarlanmış olması mümkündür. Bu iki modülü aynı I²C hattına bağlarsanız, Master 0x68 adresini çağırdığında her ikisi de aynı anda yanıt vermeye çalışır ve bu durum iletişimi bozar.

Çözümler:

  1. Adres Seçim Pinleri: Birçok Slave cihaz, AD0 veya A0 gibi fiziksel pinler aracılığıyla adreslerinin son bir veya iki bitini değiştirme imkanı sunar. Bu pini Vcc (güç) veya GND (toprak) hattına bağlayarak cihazın adresini değiştirebilirsiniz.
  2. Yazılımsal I²C (Bit-Banging): Mikrodenetleyicinin donanımsal I²C modülü yerine, farklı genel amaçlı giriş/çıkış (GPIO) pinlerini kullanarak ikinci bir sanal I²C hattı oluşturulabilir. Bu, çakışan cihazı farklı bir hatta çalıştırmanızı sağlar.
  3. I²C Çoklayıcı/Anahtar (Multiplexer/Switch): Bu, en temiz ve ölçeklenebilir çözümdür. I²C çoklayıcı entegresi, Master’dan gelen tek bir I²C hattını alır ve onu birden çok ayrı alt hatta ayırır. Master, çoklayıcıya bir komut göndererek hangi alt hattın aktif olacağını seçer. Bu sayede, aynı adrese sahip cihazlar farklı “kanallarda” birbirlerinden izole bir şekilde sorunsuzca çalışabilir.

5. I²C Veri İletim Protokolü

Adresleme, Master’ın hangi Slave ile konuşacağını belirlemesini sağlar. Adresleme başarılı olduktan sonra ise asıl veri alışverişi başlar. I²C protokolü, bu veri akışını yönetmek ve senkronizasyonu sağlamak için bir dizi temel sinyal ve kural üzerine kurulmuştur. Bu bölümde, verinin bit düzeyinde nasıl aktarıldığını ve iletişimin temel yapı taşlarını inceleyeceğiz.

5.1 Temel Sinyaller

I²C hattı üzerindeki “konuşma”, rastgele 1’ler ve 0’lar göndermekten ibaret değildir. Her iletişim, belirli başlangıç, bitiş ve onay sinyalleriyle çerçevelenir. Bu sinyaller, hattaki tüm cihazların ne zaman dinlemesi, ne zaman konuşması ve ne zaman sessiz kalması gerektiğini anlamasını sağlar.

BAŞLAMA (START) ve DURMA (STOP) Koşulları

Bu iki sinyal, I²C iletişim paketinin sınırlarını çizer. Normal veri aktarımı sırasında asla meydana gelemeyecek özel durumlardır, bu da onların benzersiz birer sinyal olmasını sağlar.

  • BAŞLAMA Koşulu (START Condition): Bir Master’ın iletişimi başlatmak için kullandığı sinyaldir. Saat hattı (SCL) yüksek seviyedeyken, veri hattının (SDA) yüksekten alçağa çekilmesiyle oluşur. Bu sinyal, hatta bağlı tüm Slave cihazlar için bir “uyandırma” çağrısıdır; hepsi gelen adresi dinlemeye başlar.

  • DURMA Koşulu (STOP Condition): Master’ın iletişimi sonlandırdığını bildiren sinyaldir. Saat hattı (SCL) yüksek seviyedeyken, veri hattının (SDA) alçaktan yükseğe çekilmesiyle oluşur. Bu sinyalden sonra I²C hattı boşa çıkar (idle state) ve başka bir Master tarafından kullanılabilir hale gelir.

I²C Başlama ve Durma Koşulları

Şekil 5.1: I²C Başlama ve Durma Koşulları

ACK/NACK (Onay/Red) Mekanizması

I²C, güvenilir bir iletişim için geri bildirim mekanizması kullanır. Her 8 bitlik veri (adres veya data) gönderiminden sonra, alıcı cihazın bu veriyi doğru alıp almadığını bildirmesi için 9. bir saat darbesi periyodu ayrılmıştır.

  • ACK (Acknowledge - Onay): Alıcı cihaz (genellikle Slave), 8 biti başarıyla aldığını ve bir sonraki bayta hazır olduğunu belirtmek için, 9. saat darbesi sırasında SDA hattını alçak seviyesine çeker. Bu, “Mesaj alındı, devam et” anlamına gelir.
  • NACK (Not Acknowledge - Onaylanmama/Red): Eğer alıcı cihaz 9. saat darbesi sırasında SDA hattını serbest bırakırsa (yüksek seviyede kalırsa), bu bir NACK sinyalidir. NACK’in birkaç anlamı olabilir:
    1. Slave, adresi tanımamıştır veya o adreste bir cihaz yoktur.
    2. Slave, meşguldür ve şu anda veri alamaz (örneğin, bir önceki veriyi işliyor).
    3. Master, bir okuma işlemi sırasında Slave’e “daha fazla veri gönderme” demek için kasıtlı olarak bir NACK gönderir. Bu, okuma işlemini sonlandırmanın standart yoludur.
ACK (Onay) ve NACK (Onaylanmama) Sinyalleri

Şekil 5.2: ACK (Onay) ve NACK (Onaylanmama) Sinyalleri


Tekrarlanan BAŞLAMA (Repeated START) Koşulu

Bazen Master’ın, hattı serbest bırakmadan bir işlemi bitirip hemen yenisine başlaması gerekir. En yaygın senaryo, bir Slave’in belirli bir iç kaydını (register) okumaktır:

  1. Master, Slave’e yazma modunda adresini gönderir (R/W=0).
  2. Master, okunacak kaydın adresini Slave’e yazar.
  3. Master, Slave’den veri okumak için okuma moduna geçmelidir.

Eğer Master bu noktada bir STOP ve ardından yeni bir START gönderirse, başka bir Master araya girip hattı ele geçirebilir. Bunu önlemek için Tekrarlanan BAŞLAMA (Repeated START) kullanılır. Bu, bir STOP koşulu olmadan gönderilen bir START koşuludur ve Master’ın hat üzerindeki kontrolünü kaybetmeden iletişim yönünü değiştirmesine olanak tanır.

Animasyonu başlatmak için oynat tuşuna basın.

SCLSDABAŞLA01101000Adres + R/W=0 (Yazma)ACK00011101Veri Baytı (Register Adresi)ACKTekrarlananBAŞLAMA01101001Adres + R/W=1 (Okuma)ACK10110101Veri Baytı (Slave'den)NACKDUR

Şekil 5.3: Tekrarlanan BAŞLAMA (Repeated START) Kullanımı


5.2 Veri Frame Yapısı

Temel sinyaller iletişimin çerçevesini oluştururken, verinin kendisi de belirli kurallara göre iletilir.

Bit Düzeyinde Veri İletimi

I²C’nin en temel kuralı şudur: SCL hattı yüksek seviyedeyken SDA hattındaki veri kararlı (sabit) olmalıdır. SDA hattındaki değişiklikler (0’dan 1’e veya 1’den 0’a) yalnızca SCL hattı alçak seviyedeyken yapılabilir. Master, SCL’yi yükseğe çektiğinde, Slave’ler o an SDA hattında hangi seviye varsa onu “okur”. Veri, en anlamlı bitten (Most Significant Bit - MSB) başlayarak gönderilir.

1011SCL AlçakVeri değişimi başlar2SDA DeğişirYeni bit hatta yerleşir3SCL YüksekVeri artık kararlı4Veri ÖrneklenirAlıcı biti okurSCLSDA

Veri (SDA), yalnızca Saat (SCL) düşük seviyedeyken değişebilir.
Alıcı cihaz, veriyi SCL yüksek seviyedeyken, sinyal kararlı hale geldiğinde okur.

Şekil 5.4: I²C Bit Düzeyinde Veri Aktarımı


Clock Stretching (Saat Esnetme) Mekanizması

Bazen bir Slave cihaz, Master’ın gönderdiği hızda veriyi işleyemeyebilir. Örneğin, bir sensör yeni bir ölçüm yapmalı veya bir EEPROM gelen veriyi fiziksel olarak yazmalıdır. Bu durumda Slave, Master’a “biraz bekle” demek için Clock Stretching mekanizmasını kullanır. Slave, SCL hattını zorla düşük seviyede tutar. Master, SCL’yi yükseğe çekmeye çalışsa bile hattın düşük kaldığını görür ve Slave hattı serbest bırakana kadar bekler. Bu, yavaş cihazların hızlı bir Master ile sorunsuzca çalışabilmesini sağlayan kritik bir özelliktir.

1011Slave SCL'i Düşük Tutuyor(Clock Stretching)Normal SCL DarbesiMaster kontrolündeSCLSDA

Clock Stretching: Slave cihaz, veriyi hazırlamak veya bir işlemi tamamlamak için ek süreye ihtiyaç duyduğunda SCL hattını kasıtlı olarak düşük seviyede tutar.
Master, SCL hattının serbest bırakılmasını (yükselmesini) bekleyerek iletişimi güvenli bir şekilde duraklatır.

Şekil 5.5: Clock Stretching Mekanizması


Veri Setup ve Hold Zamanları (Timing)

Güvenilir iletişim için, SDA ve SCL sinyalleri arasındaki zamanlama ilişkileri standartlarla tanımlanmıştır. Bu kritik zamanlamalar, özellikle yüksek hızlarda veya gürültülü ortamlarda hayati önem taşır.

  • Setup Time (t_SU;DAT): Bir verinin geçerli sayılması için, SCL’nin yükselen kenarından önce SDA hattında ne kadar süre kararlı kalması gerektiğini belirten minimum süredir.
  • Hold Time (t_HD;DAT): Bir verinin geçerli sayılması için, SCL’nin düşen kenarından sonra SDA hattında ne kadar süre daha kararlı kalması gerektiğini belirten minimum süredir.

Bu zamanlamalara uyulmaması, alıcının veriyi yanlış okumasına neden olabilir.

HAT BOŞTAVERİ DEĞİŞEBİLİRVERİ GEÇERLİVERİ DEĞİŞEBİLİRVERİ GEÇERLİVERİ DEĞİŞEBİLİRHAT BOŞTASCLSDAMaster Sürer (Data='0')Slave Sürer (ACK='0')tHIGHSCL High PeriodtLOWSCL Low PeriodtHD;STASTART Hold TimetSU;DATDATA Setup TimetSU;STOSTOP Setup TimetBUFBus Free TimeSTARTSTOP

tSU (Setup Time): Referans olaydan (genellikle SCL yükselişi) önce, veri sinyalinin (SDA) stabil kalması gereken minimum süredir. Verinin güvenle okunabilmesini sağlar.
tHD (Hold Time): Referans olaydan (genellikle SCL düşüşü) sonra, veri sinyalinin stabil kalması gereken minimum süredir. Sinyal geçişlerinin çakışmasını engeller.

Şekil 5.6: Gelişmiş I²C Kritik Zamanlama ve Sinyal Sahipliği Diyagramı


6. I2C İletişim Senaryoları

I2C protokolü, sadece bit ve byte’ları bir noktadan diğerine göndermekten ibaret değildir. Farklı veri transferi ihtiyaçlarına yönelik olarak tasarlanmış çeşitli iletişim senaryolarını destekleyen esnek bir yapıya sahiptir. Bu senaryoları anlamak, gömülü sistemlerde verimli ve hatasız bir firmware geliştirmenin temelini oluşturur. Bu bölümde, temel yazma/okuma işlemlerinden başlayarak daha karmaşık veri transferi metodolojilerine kadar I2C’nin en yaygın kullanım senaryolarını inceleyeceğiz.

6.1 Temel Okuma/Yazma İşlemleri

Bu işlemler, I2C iletişiminin en temel yapı taşlarıdır ve bir Master ile tek bir Slave arasındaki en basit veri alışverişini temsil eder.

Master’dan Slave’e Veri Yazma (Write)

Bu, en yaygın I2C operasyonudur. Master, bir Slave cihazın belirli bir register’ına veya belleğine veri yazar. İşlem adımları şu şekildedir:

  1. START Koşulu: Master, SDA hattını SCL hattı yüksekken düşüğe çekerek iletişimi başlatır.
  2. Adres Çerçevesi (Address Frame): Master, veri yazmak istediği Slave’in 7-bit adresini ve ardından ‘0’ olan Yazma (Write) bitini (R/W=0R/W=0) gönderir. Bu 8 bitlik paket, hattaki tüm Slave’ler tarafından dinlenir.
  3. ACK (Acknowledge): Adresi üzerine alan Slave, 9. saat darbesinde SDA hattını düşüğe çekerek adresi tanıdığını ve iletişime hazır olduğunu Master’a bildirir.
  4. Veri Çerçevesi (Data Frame): Master, göndermek istediği 8 bitlik veri byte’ını gönderir. Bu genellikle Slave’in dahili register adresidir.
  5. ACK: Slave, veri byte’ını başarıyla aldığını belirtmek için tekrar bir ACK sinyali gönderir.
  6. Veri Çerçevesi (Data Frame): Master, az önce belirttiği register’a yazılacak olan asıl veriyi gönderir.
  7. ACK: Slave, bu veriyi de aldığını bir ACK ile teyit eder.
  8. STOP Koşulu: Master, SCL hattı yüksekken SDA hattını yükseğe çekerek iletişimi sonlandırır ve bus’ı serbest bırakır.
Start011010100Slave Address + W(0)001011010Register Address110001010Data ByteStopSCLSDA

Bu diyagram, bir I²C master cihazının, bir slave cihazdaki belirli bir register'a nasıl veri yazdığını gösterir.

Şekil 6.1: Master'dan Slave'e Yazma Protokolü

Slave’den Master’a Veri Okuma (Read)

Bu senaryoda Master, bir Slave cihazdan veri talep eder. Yazma işleminden temel farkı, veri transferi yönünün değişmesi ve ACK/NACK sinyallerinin yönetimindedir.

  1. START Koşulu: Master iletişimi başlatır.
  2. Adres Çerçevesi: Master, bu kez Slave’in 7-bit adresini ve ardından ‘1’ olan Okuma (Read) bitini (R/W=1R/W=1) gönderir.
  3. ACK: Adreslenen Slave, adresi kabul ettiğini ACK ile bildirir. Bu noktadan sonra SDA hattının kontrolü Slave’e geçer.
  4. Veri Çerçevesi: Slave, Master’ın okuması için 8 bitlik veri byte’ını SDA hattına koyar.
  5. ACK/NACK: Veriyi alan Master, okumaya devam etmek istiyorsa ACK gönderir. Bu, Slave’e bir sonraki veri byte’ını göndermesi için bir işarettir (Sequential Read). Eğer okuma işlemi bu byte ile bitecekse, Master NACK (Not Acknowledge) gönderir. NACK, Slave’e veri göndermeyi durdurmasını ve SDA hattını serbest bırakmasını söyler.
  6. STOP Koşulu: Master, NACK gönderdikten sonra STOP koşulunu oluşturarak iletişimi sonlandırır.

Combined Format (Birleşik Format) / Repeated START

Bazen bir I2C işlemi, bus’ı serbest bırakmadan birden fazla operasyonu atomik (bölünemez) bir şekilde gerçekleştirmeyi gerektirir. En tipik örnek, bir sensörün belirli bir register’ından okuma yapmaktır: önce o register’ın adresini yazmanız, ardından bus’ı bırakmadan o register’dan okuma yapmanız gerekir.

Bu, Repeated START (Sr) koşulu ile sağlanır. STOP koşulu göndermek yerine, Master tekrar bir START koşulu oluşturur. Bu, bus’ın kontrolünü kaybetmeden işlem modunu (örneğin Yazma’dan Okuma’ya) değiştirmesini sağlar.

Örnek Senaryo: Bir sensörün 0x2A register’ından veri okuma

  1. S (Start)
  2. Slave Address + W(0) -> ACK
  3. Register Address (0x2A) -> ACK (Slave’e hangi register’ı okumak istediğimizi söyledik)
  4. Sr (Repeated Start) (STOP yerine tekrar START)
  5. Slave Address + R(1) -> ACK (Şimdi aynı Slave’e okuma yapacağımızı söylüyoruz)
  6. Slave, 0x2A register’ındaki veriyi gönderir.
  7. NACK (Master tek byte okuyup bitireceğini bildirir)
  8. P (Stop)
Start (S)Slave Addr + W -> ACK011010100Register Addr (0x2A) -> ACK001010100Repeat Start (Sr)Slave Addr + R -> ACK011010110Data from Slave -> NACK110001011Stop (P)SCLSDA

Bu diyagram, bir sensörün 0x2A register'ından veri okuma işlemini gösterir. Master önce yazma modunda register adresini belirtir, ardından "Repeated Start" ile okuma moduna geçer ve veriyi okur.

Şekil 6.2: Sensör Okuma Senaryosu (Combined Format)

6.2 İleri Seviye İşlemler

Temel operasyonların üzerine inşa edilen bu teknikler, daha verimli ve büyük ölçekli veri transferleri için kullanılır.

Sequential Read/Write (Ardışık Okuma/Yazma)

Çoğu I2C Slave cihazı, dahili bir adres işaretçisine (address pointer) sahiptir. Bir okuma veya yazma işlemi yapıldığında, bu işaretçi otomatik olarak bir sonraki adrese geçer. Bu özellik, büyük veri bloklarını tek bir işlemde transfer etmek için kullanılır ve protokol overhead’ini ciddi ölçüde azaltır.

  • Sequential Write: Master, başlangıç register adresini ve ilk veri byte’ını gönderdikten sonra, STOP koşulu göndermeden sadece veri byte’ları göndermeye devam eder. Her veri byte’ından sonra Slave ACK gönderir ve kendi iç adres işaretçisini bir artırır.
  • Sequential Read: Master, bir okuma işlemi başlattıktan sonra, okuduğu her byte için STOP yerine ACK gönderir. Master’dan gelen her ACK, Slave için “bir sonraki byte’ı gönder” komutu anlamına gelir. Okunacak son byte’tan sonra Master NACK ve ardından STOP göndererek okumayı sonlandırır.

Random Access (Rastgele Erişim)

“Rastgele Erişim” terimi, aslında yukarıda açıkladığımız Combined Format operasyonunun pratik adıdır. Bir Slave’in belleğindeki ardışık olmayan, belirli bir adresten veri okumak için kullanılır. İşlem, “Yazma” (register adresini ayarlamak için) ve ardından “Okuma” (veriyi almak için) adımlarını birleştiren bir Repeated START işlemiyle gerçekleştirilir. Bu, I2C tabanlı EEPROM veya sensörlerden veri okumanın standart yöntemidir.

Block Read/Write Protokolleri

Bu protokoller genellikle I2C’nin üzerinde çalışan SMBus (System Management Bus) gibi daha üst seviye protokollerde tanımlanmıştır, ancak saf I2C cihazlarında da benzer implementasyonlar görülebilir. Buradaki temel fikir, transfer edilecek veri bloğunun boyutunun iletişimin bir parçası olarak gönderilmesidir.

  • Block Write: Master, Slave adresi ve yazma komutunu gönderdikten sonra, ilk veri byte’ı olarak gönderilecek toplam veri byte sayısını (NN) gönderir. Slave bu bilgiyi aldıktan sonra, takip eden NN adet veri byte’ını almaya hazır olur.
    • S -> Addr+W -> ACK -> Command -> ACK -> Byte Count (N) -> ACK -> Data_1 -> ACK -> ... -> Data_N -> ACK -> P
  • Block Read: Genellikle bir Combined Format işlemi gerektirir. Master önce okumak istediği bloğun komutunu yazar, ardından Repeated START ile okuma moduna geçer. Slave, ilk byte olarak bloğun boyutunu (NN) gönderir ve Master bunu takiben NN byte veri okur.
    • S -> Addr+W -> ACK -> Command -> ACK -> Sr -> Addr+R -> ACK -> Byte Count (N) -> ACK -> Data_1 -> ACK -> ... -> Data_N -> NACK -> P

Bu ileri seviye senaryoları anlamak, özellikle yüksek veri hacmine sahip sensörler, bellek yongaları veya karmaşık çevre birimleriyle çalışırken firmware’in performansını ve sağlamlığını doğrudan etkiler. Her zaman hedef Slave cihazın veri sayfasını (datasheet) inceleyerek desteklediği spesifik protokolleri doğrulamak kritik öneme sahiptir.


7. I2C Hız Modları ve Performans

I2C protokolü, ilk ortaya çıktığı günden bu yana gömülü sistemlerin artan veri transferi ihtiyaçlarına cevap verebilmek için sürekli evrim geçirmiştir. Bu evrim, farklı hız modlarının standartlaştırılmasıyla sonuçlanmıştır. Ancak daha yüksek hız, her zaman daha iyi performans anlamına gelmez; çünkü hız arttıkça, fiziksel katman (physical layer) üzerinde dikkat edilmesi gereken mühendislik detayları da artar. Bu bölümde, standart I2C hız modlarını ve bus performansını optimize etmenin kritik yollarını ele alacağız.

7.1 Standart Hız Modları

Her bir hız modu, maksimum saat frekansını ve buna bağlı olarak uyulması gereken elektriksel zamanlama kısıtlamalarını tanımlar. Bir sistem tasarlarken, bus üzerindeki en yavaş cihazın desteklediği hız modunun, tüm iletişimin hızını belirlediğini unutmamak önemlidir.

  • Standard Mode (S-mode): 100 kHz Bu, I2C’nin orijinal ve en temel modudur. Maksimum 100 kbit/s veri transfer hızına izin verir. Geriye dönük uyumluluğun en yüksek olduğu moddur ve basit sensörler, EEPROM’lar gibi düşük hız gerektiren çevre birimleri için hala yaygın olarak kullanılmaktadır. Elektriksel gereksinimleri en esnek olan mod olduğu için en sağlam (robust) iletişim bu modda sağlanır.

  • Fast Mode (F-mode): 400 kHz Günümüzdeki birçok mikrokontrolcü ve sensör için “standart” haline gelmiş moddur. Maksimum 400 kbit/s hıza ulaşabilir. Standard Mode’a göre daha keskin (daha kısa) yükselme ve düşme zamanları (rise/fall times) gerektirir. Bu nedenle, pull-up dirençlerinin seçimi ve bus kapasitansı daha kritik hale gelir.

  • Fast Mode Plus (Fm+): 1 MHz Daha yüksek bant genişliği gerektiren uygulamalar için geliştirilmiştir. 1 Mbit/s’ye kadar veri transferi sağlar. Bu hıza ulaşabilmek için, I/O pinlerinin daha yüksek akım çekebilen (daha güçlü) sürücülere sahip olması ve daha düşük değerli pull-up dirençleri kullanılarak yükselme zamanlarının kısaltılması gerekir. Her cihaz bu modu desteklemez, bu nedenle datasheet kontrolü zorunludur.

  • High-Speed Mode (HS-mode): 3.4 MHz Bu mod, protokol seviyesinde önemli bir farklılık getirir. 3.4 Mbit/s gibi yüksek hızlara çıkmak için, sadece pasif pull-up dirençlerine güvenmek yerine, Master cihaz iletişim sırasında SCL hattını aktif olarak hem düşüğe hem de yükseğe sürer (active pull-up). İletişim, önce 100 veya 400 kHz gibi daha yavaş bir modda başlar. Master, özel bir “Master Code” (00001XXXb) göndererek hattaki HS-mode uyumlu Slave’lere yüksek hıza geçiş yapacağını bildirir. Bu işlemden sonra saat frekansı 3.4 MHz’e çıkarılır. Bu mod, geriye dönük uyumluluğu korurken yüksek hız sunar.

  • Ultra Fast Mode (UF-mode): 5 MHz Bu mod, I2C’nin geleneksel yapısından en çok sapan moddur. Temel özellikleri şunlardır:

    • Tek Yönlü (Unidirectional): Sadece Master’dan Slave’e veri yazma (write-only) işlemleri için tasarlanmıştır. Okuma operasyonunu desteklemez.
    • Push-Pull Sürücüler: Geleneksel açık-drain (open-drain) yapısı yerine, hatları aktif olarak süren push-pull sürücüler kullanır. Bu nedenle pull-up dirençlerine ihtiyaç duymaz.
    • Multi-Master Yok: Push-pull yapısı nedeniyle, bus üzerinde birden fazla Master bulunamaz. Genellikle LED sürücüleri gibi hızlı ve sürekli veri akışı gerektiren uygulamalarda kullanılır.

7.2 Performans Optimizasyonu

I2C bus’ının kararlı ve spesifikasyonlara uygun çalışması, özellikle yüksek hız modlarında, dikkatli bir donanım tasarımına bağlıdır.

Bus Loading ve Kapasitif Yük Etkileri

I2C bus’ındaki her bir cihazın pini, PCB hatları ve konnektörler, SDA ve SCL hatlarına küçük bir miktar kapasitans ekler. Bu kapasitansların toplamına bus kapasitansı (CbusC_{bus}) denir. Kapasitif yük, pull-up direnci (RpR_p) ile birlikte bir RCRC zaman sabiti oluşturur. Bu sabit, sinyalin mantık ‘0’ seviyesinden mantık ‘1’ seviyesine yükselme süresini (rise time) doğrudan etkiler.

  • Problem: Yüksek kapasitans, yükselme süresini uzatır. Eğer bu süre, I2C standardının belirlediği maksimum değeri aşarsa (örneğin Fast Mode için 300 ns), veri bozulmaları ve iletişim hataları meydana gelir.
  • Kural: I2C standardı, toplam bus kapasitansının genellikle 400 pF’yi aşmamasını tavsiye eder. HS-mode için bu sınır daha da düşüktür.

Rise Time ve Fall Time Optimizasyonu

Sinyalin kalitesini, yükselme ve düşme sürelerinin kontrolü belirler.

  • Fall Time (Düşme Zamanı): Genellikle bir sorun teşkil etmez. Çünkü bir cihaz hattı ‘0’a çektiğinde, transistör aktif olarak hattaki kapasitansı toprağa deşarj eder. Bu hızlı bir işlemdir.
  • Rise Time (Yükselme Zamanı): En kritik parametredir. Hat, cihaz tarafından serbest bırakıldığında, pasif bir şekilde pull-up direnci üzerinden VDD’ye doğru şarj olur. Bu sürecin hızı τ=RpCbus\tau = R_p \cdot C_{bus} formülü ile belirlenir.

Optimizasyon: Yükselme süresini kısaltmak için pull-up direncini (RpR_p) düşürmek gerekir. Daha düşük bir direnç, hattı şarj etmek için daha fazla akım sağlar.

  • Ancak bir alt limit vardır: RpR_p çok düşürülürse, hat ‘0’a çekildiğinde akan akım (I=VDD/RpI = V_{DD} / R_p) artar. Bu durum, cihazın çıkış transistörünün hattı standardın gerektirdiği mantık ‘0’ seviyesine (VOLV_{OL} - Voltage Output Low) kadar çekememesine veya aşırı güç tüketimine neden olabilir.

Sonuç: Pull-up direnci seçimi, yeterince hızlı bir rise time ile geçerli bir VOLV_{OL} seviyesi arasında bir denge kurma sanatıdır.

Noise Immunity ve Sinyal Bütünlüğü

Yüksek hızlı dijital hatlar, gürültüye ve sinyal bozulmalarına karşı hassastır. I2C bus’ının sinyal bütünlüğünü korumak için şu tasarım pratikleri önerilir:

  • PCB Yerleşimi:
    • SDA ve SCL hatlarını mümkün olduğunca kısa tutun.
    • Bu iki hattı birbirine paralel ve yakın bir şekilde yönlendirin.
    • Etrafını bir topraklama alanı (ground plane) ile çevreleyerek harici gürültüye karşı kalkanlama yapın.
    • Anahtarlamalı güç kaynakları (SMPS) veya yüksek frekanslı saat sinyalleri gibi gürültü kaynaklarından uzak tutun.
  • Filtreleme:
    • Çok gürültülü ortamlarda, her bir hatta seri olarak eklenen düşük değerli dirençler (22100Ω22-100\,\Omega) sinyaldeki çınlamayı (ringing) sönümleyebilir.
    • Pull-up dirençlerini cihazlara yakın yerleştirmek, gürültü toplama riskini azaltır.

Bu optimizasyon teknikleri, özellikle 400 kHz ve üzeri hızlarda çalışan sistemlerde, veri bozulmalarını önleyerek güvenilir bir iletişim kanalı kurmanın temelini oluşturur.


8. Hata Yönetimi ve Debugging

I2C, genel olarak güvenilir bir protokol olsa da, donanımsal ve yazılımsal faktörler nedeniyle hatalar meydana gelebilir. Başarılı bir gömülü sistem mühendisi, bu hataları öngörebilmeli, teşhis edebilmeli ve yönetebilmelidir. Bu bölümde, I2C iletişiminde karşılaşılan en yaygın hataları ve bu hataları ayıklamak için kullanılan temel teknikleri inceleyeceğiz.

8.1 Yaygın Hatalar

Bus Stuck (Bus’ın Takılı Kalması)

Bu, en sinir bozucu I2C hatalarından biridir. SDA veya SCL hattının, genellikle mantık ‘0’ (LOW) seviyesinde, süresiz olarak takılı kalması durumudur.

  • Neden Olur? Genellikle bir Slave cihazın, veri transferinin ortasında (örneğin bir ACK gönderdikten sonra) beklenmedik bir şekilde resetlenmesi, gücünün kesilmesi veya dahili bir hata nedeniyle kilitlenmesi sonucu oluşur. Slave, hattı serbest bırakması gerektiğini asla anlayamaz.
  • Sonuç: Bus kilitlenir. Master yeni bir START koşulu (SCL yüksekken SDA’yı düşürmek) oluşturamaz, çünkü hatlardan biri (genellikle SDA) zaten düşük seviyededir. Bu durum, tüm I2C bus’ını kullanılamaz hale getirir.

Clock Stretching Problemleri

Clock stretching, I2C protokolünün bir hatası değil, bir özelliğidir. Bir Slave cihaz, gelen bir veriyi işlemek veya bir sonraki veri byte’ını hazırlamak için ek zamana ihtiyaç duyduğunda, SCL hattını geçici olarak LOW seviyesine çekerek Master’ı bekletebilir.

  • Problem Nerede Başlar? Sorun, Master’ın bu duruma hazırlıklı olmamasından kaynaklanır. Bazı I2C donanım çevre birimleri (peripheral), SCL’nin belirli bir süreden daha uzun süre düşük kalması durumunda bir timeout hatası üretir. Eğer yavaş bir Slave, Master’ın timeout süresinden daha uzun bir süre clock stretching yaparsa, Master iletişimin koptuğunu varsayarak işlemi iptal eder.

ACK/NACK Hataları

ACK ve NACK sinyalleri, I2C’nin temel geri bildirim mekanizmasıdır ve doğru yorumlanmaları kritik öneme sahiptir.

  • Adresleme Sonrası NACK: Master bir adres ve R/W biti gönderdikten sonra ACK yerine NACK alıyorsa, bunun birkaç anlamı olabilir:
    1. Bus üzerinde o adrese sahip bir cihaz yoktur.
    2. Cihaz bus’a bağlıdır ancak gücü yoktur veya henüz açılmamıştır.
    3. Cihaz meşguldür ve şu anda iletişim kuramıyordur (bazı sensörler ölçüm sırasında I2C’ye cevap vermez).
  • Veri Transferi Sırasında NACK: Master’ın gönderdiği bir veri byte’ından sonra Slave NACK gönderiyorsa, bu genellikle Slave’in veriyi kabul etmediğini gösterir. Nedenleri arasında geçersiz bir komut gönderilmesi veya Slave’in dahili yazma tamponunun (buffer) dolu olması sayılabilir.

Önemli Not: Master’ın bir okuma işleminin sonunda NACK göndermesi bir hata değildir. Bu, Master’ın Slave’e “daha fazla veri gönderme, okuma bitti” demesinin standart yoludur.

Adres Çakışmaları

Bu, aynı I2C bus’ı üzerinde iki veya daha fazla Slave cihazın aynı 7-bit adrese sahip olması durumudur.

  • Sonuç: Master bu adresi gönderdiğinde, her iki Slave de aynı anda ACK göndermeye çalışır. Daha da kötüsü, bir okuma işlemi sırasında her iki cihaz da aynı anda SDA hattını sürmeye çalışır. Bu durum bus contention (bus çekişmesi) olarak adlandırılır ve veri bozulmasına, hatta potansiyel olarak cihazların donanımına zarar vermeye yol açabilir.
  • Çözüm: Cihazların adres seçme pinlerini (A0, A1, A2) kullanarak farklı adresler atamak veya bir I2C multiplexer/switch kullanmaktır.

8.2 Debug Teknikleri

Logic Analyzer Kullanımı

I2C hatalarını ayıklarken logic analyzer, en güçlü müttefikinizdir. Sadece 0 ve 1’leri göstermekle kalmaz, aynı zamanda I2C protokolünü çözerek (decode) size insan tarafından okunabilir bir formatta sunar.

  • Ne Aranmalı?
    • START ve STOP koşullarının zamanlaması doğru mu?
    • Adres doğru gönderiliyor mu? R/W biti doğru mu?
    • Hangi cihaz ACK, hangisi NACK gönderiyor? Hatanın tam olarak hangi byte’tan sonra oluştuğunu görebilirsiniz.
    • Saat (SCL) frekansı beklenen değerde mi?
5µs13µs21µs29µs37µs45µs53µs61µs69µs77µs85µs93µs101µs109µs117µsStartAddr: 0x68 (W)ACKData: 0x0FACKData: 0x80NACKStopSCLSDA

Bu görüntü, modern bir logic analyzer'ın I²C iletişimini nasıl çözdüğünü gösterir. Kırmızı NACK balonu, alıcı cihazın veriyi onaylamadığını net bir şekilde belirtir.

Şekil 6.3: Logic Analyzer Görüntüsü

Osiloskop ile Sinyal Analizi

Logic analyzer protokol seviyesinde ne olduğunu söylerken, osiloskop fiziksel katmanda (sinyal kalitesi) ne olduğunu gösterir.

  • Ne Aranmalı?
    • Sinyal Seviyeleri: Mantık ‘0’ seviyesi (VOLV_{OL}) yeterince düşük mü? Mantık ‘1’ seviyesi (VOHV_{OH}) VDD’ye ulaşıyor mu?
    • Yükselme Zamanı (Rise Time): Sinyal, yavaş bir şekilde mi yükseliyor? Bu, çok yüksek bir bus kapasitansı (CbusC_{bus}) veya çok yüksek değerli bir pull-up direncine (RpR_p) işaret eder.
    • Gürültü ve Çınlama (Ringing): Sinyal kenarlarında aşırı çınlama veya hat üzerinde gürültü var mı? Bu, sinyal bütünlüğü (signal integrity) sorunlarına işaret eder.

OscilloscopeView

V_IH (High Threshold)V_IL (Low Threshold)Slow Rise TimeSinyal, VIH'ye çok yavaş ulaşıyor.(Zayıf Pull-up / Yüksek Kapasitans)

Bu osiloskop görüntüsü, I²C hattındaki yaygın bir donanım sorununu gösterir. Sinyalin yükselen kenarı, kavisli bir RC şarj eğrisini andırır ve mantık '1' seviyesine (VIH) ulaşması çok uzun sürer. Bu durum, genellikle pull-up direncinin çok zayıf olmasından veya hattaki kapasitansın yüksek olmasından kaynaklanır ve veri hatalarına yol açabilir.

Şekil 6.4: Osiloskopta Yavaş Yükselme Zamanı Problemi

Yazılımsal Debug Yöntemleri

Donanım araçları olmadan önce, yazılım seviyesinde kontrol edilmesi gerekenler vardır:

  • Dönüş Kodlarını Kontrol Etme: Kullandığınız I2C sürücü fonksiyonları (örneğin HAL_I2C_Master_Transmit) genellikle HAL_OK, HAL_ERROR, HAL_BUSY gibi durum kodları döndürür. Bu kodları her zaman kontrol edin. Bir HAL_ERROR dönüşü, genellikle bir NACK alındığı anlamına gelir.
  • I2C Bus Tarayıcı (Scanner): 1’den 127’ye kadar tüm olası adresleri tarayan ve ACK yanıtı veren adresleri listeleyen basit bir fonksiyon yazın. Bu, cihazınızın gerçekten bus üzerinde olup olmadığını ve doğru adrese sahip olup olmadığını doğrulamanın en hızlı yoludur.
  • Adım Adım Debugging: Bir JTAG/SWD debugger kullanarak, I2C iletişim fonksiyonuna girmeden önce ve girdikten sonra değişkenleri ve sistem durumunu kontrol edin. Kodun nerede takıldığını veya hata verdiğini tespit edebilirsiniz.

Bus Recovery (Bus Kurtarma)

“Bus Stuck” durumundan kurtulmak için bir mekanizma gereklidir. Çoğu modern mikrokontrolcü sürücüsü bunu otomatik olarak yapmaya çalışsa da, manuel olarak uygulamak gerekebilir.

  1. Tespiti: Yazılım, bir I2C işlemi başlatmadan önce SDA ve SCL hatlarının durumunu kontrol eder. Eğer hatlar belirli bir süreden uzun süre LOW seviyesinde ise bus’ın takılı kaldığı varsayılır.
  2. Kurtarma Prosedürü:
    • Master, I2C donanımını geçici olarak devre dışı bırakır ve SCL/SDA pinlerini standart GPIO çıkışı olarak yapılandırır.
    • Master, SDA hattını serbest bırakır (HIGH) ve SCL hattına manuel olarak 9 adet saat darbesi gönderir.
    • Bu darbeler, hattı kilitleyen Slave’in mevcut byte transferini tamamladığını sanmasını ve 9. darbeden sonra SDA hattını serbest bırakmasını sağlar.
    • SDA hattı HIGH seviyesine döndükten sonra, Master pinleri tekrar GPIO olarak kullanarak bir STOP koşulu (SCL yüksekken SDA’yı yükseğe çekme) oluşturur.
    • Son olarak pinler tekrar I2C moduna alınır ve bus normale döner.

9. Pratik Uygulama Örnekleri

Teori, bir protokolün “ne” olduğunu anlatır; pratik ise “nasıl” kullanıldığını gösterir. I2C, gömülü sistemlerin adeta bir iskeleti gibi, sayısız farklı bileşeni birbirine bağlar. Bu bölümde, en yaygın I2C cihazlarının çalışma mantığını inceleyecek ve popüler geliştirme platformlarında bu cihazları nasıl hayata geçirebileceğimizi adım adım göreceğiz.

9.1 Popüler I2C Cihazları

EEPROM Bellek Entegreleri (Örn: AT24Cxx Serisi)

EEPROM (Electrically Erasable Programmable Read-Only Memory) entegreleri, güç kesildiğinde bile veriyi saklayabilen kalıcı belleklerdir. I²C protokolü üzerinden kolayca erişilebilir olmaları, onları sensör verilerini kaydetme, kalibrasyon değerlerini saklama veya cihaz ayarlarını depolama gibi uygulamalar için ideal kılar.

Aşağıdaki Arduino kodu, bir BME280 sensöründen alınan sıcaklık, nem ve basınç verilerini periyodik olarak bir AT24C256 EEPROM’a kaydetmeyi ve ardından bu verileri okumayı göstermektedir. Bu örnek, I²C tabanlı iki farklı sensörün (BME280 ve AT24C256) aynı bus üzerinde nasıl birlikte kullanılabileceğini de sergilemektedir.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <EEPROM.h> // Arduino'nun dahili EEPROM kütüphanesi, harici I2C EEPROM için Wire kütüphanesi kullanılacak

// BME280 sensörü için I2C adresi (genellikle 0x76 veya 0x77)
#define BME_ADDRESS 0x76 

// AT24C256 EEPROM için I2C adresi (genellikle 0x50)
#define EEPROM_ADDRESS 0x50 

// EEPROM'da veri yazmaya başlanacak adres
// AT24C256 32KB (32768 byte) kapasiteye sahiptir.
// Her okuma 12 byte (3 float * 4 byte/float) yer kaplayacak.
// Bu örnekte basitlik adına her kaydı ardışık adreslere yazacağız.
// Gerçek uygulamalarda dairesel tampon (circular buffer) veya 
// dosya sistemi benzeri yapılar kullanılabilir.
int currentEEPROMAddress = 0; 

Adafruit_BME280 bme; // I2C

struct SensorData {
  float temperature;
  float humidity;
  float pressure;
};

void setup() {
  Serial.begin(9600);
  while (!Serial); // Seri portun açılmasını bekle (sadece Leonardo ve türevleri için)

  Serial.println(F("BME280 ve EEPROM Veri Kaydedici Başlatılıyor..."));

  // BME280 sensörünü başlat
  if (!bme.begin(BME_ADDRESS)) {
    Serial.println("BME280 sensörü bulunamadı! Bağlantıları kontrol edin.");
    while (1);
  }
  Serial.println("BME280 sensörü başarıyla başlatıldı.");

  // EEPROM'u kontrol et (sadece varlığını doğrulamak için basit bir kontrol)
  Wire.beginTransmission(EEPROM_ADDRESS);
  if (Wire.endTransmission() == 0) {
    Serial.println("AT24C256 EEPROM başarıyla bulundu.");
  } else {
    Serial.println("AT24C256 EEPROM bulunamadı! Bağlantıları kontrol edin.");
    while (1);
  }

  // EEPROM'daki ilk 10 kaydı oku (varsa)
  Serial.println("\nEEPROM'daki Mevcut Veriler:");
  readAllEEPROMData();
}

void loop() {
  // Her 5 saniyede bir veri oku ve kaydet
  static unsigned long lastLogTime = 0;
  if (millis() - lastLogTime > 5000) {
    lastLogTime = millis();
    logSensorData();
  }

  // Seri porttan 'r' veya 'R' gelirse tüm verileri oku
  if (Serial.available()) {
    char command = Serial.read();
    if (command == 'r' || command == 'R') {
      Serial.println("\nSeri Porttan Okuma İsteği Alındı. EEPROM Verileri:");
      readAllEEPROMData();
    } else if (command == 'c' || command == 'C') {
      Serial.println("\nSeri Porttan Temizleme İsteği Alındı. EEPROM Temizleniyor...");
      clearEEPROM();
      Serial.println("EEPROM temizlendi. currentEEPROMAddress sıfırlandı.");
    }
  }
}

void logSensorData() {
  SensorData data;
  data.temperature = bme.readTemperature();
  data.humidity = bme.readHumidity();
  data.pressure = bme.readPressure() / 100.0F; // hPa olarak

  Serial.print("Sıcaklık: ");
  Serial.print(data.temperature);
  Serial.print(" *C, Nem: ");
  Serial.print(data.humidity);
  Serial.print(" %, Basınç: ");
  Serial.print(data.pressure);
  Serial.println(" hPa");

  // Veriyi EEPROM'a yaz
  // Her float 4 byte yer kaplar. Toplam 3 float = 12 byte.
  // EEPROM'a yazmadan önce adresin sınırları içinde olduğundan emin olun.
  if (currentEEPROMAddress + sizeof(SensorData) <= 32768) { // AT24C256 için 32KB
    writeEEPROM(EEPROM_ADDRESS, currentEEPROMAddress, (byte*)&data, sizeof(SensorData));
    Serial.print("Veri EEPROM'a yazıldı, adres: ");
    Serial.println(currentEEPROMAddress);
    currentEEPROMAddress += sizeof(SensorData); // Sonraki kayıt için adresi güncelle
  } else {
    Serial.println("EEPROM dolu! Yeni veri kaydedilemiyor.");
  }
}

void writeEEPROM(int deviceAddress, int eeAddress, byte* data, int len) {
  Wire.beginTransmission(deviceAddress);
  Wire.write((int)(eeAddress >> 8));   // Yüksek adres baytı
  Wire.write((int)(eeAddress & 0xFF)); // Düşük adres baytı

  for (int i = 0; i < len; i++) {
    Wire.write(data[i]);
  }
  Wire.endTransmission();
  delay(5); // EEPROM yazma döngüsü için kısa bir gecikme
}

void readEEPROM(int deviceAddress, int eeAddress, byte* data, int len) {
  Wire.beginTransmission(deviceAddress);
  Wire.write((int)(eeAddress >> 8));   // Yüksek adres baytı
  Wire.write((int)(eeAddress & 0xFF)); // Düşük adres baytı
  Wire.endTransmission();

  Wire.requestFrom(deviceAddress, len); // Belirtilen uzunlukta veri iste
  for (int i = 0; i < len; i++) {
    if (Wire.available()) {
      data[i] = Wire.read();
    }
  }
}

void readAllEEPROMData() {
  SensorData readData;
  int address = 0;
  int recordCount = 0;

  while (address + sizeof(SensorData) <= currentEEPROMAddress) {
    readEEPROM(EEPROM_ADDRESS, address, (byte*)&readData, sizeof(SensorData));
    Serial.print("Kayıt ");
    Serial.print(recordCount++);
    Serial.print(": Sıcaklık: ");
    Serial.print(readData.temperature);
    Serial.print(" *C, Nem: ");
    Serial.print(readData.humidity);
    Serial.print(" %, Basınç: ");
    Serial.print(readData.pressure);
    Serial.println(" hPa");
    address += sizeof(SensorData);
  }
  if (recordCount == 0) {
    Serial.println("EEPROM'da henüz kaydedilmiş veri yok.");
  }
}

void clearEEPROM() {
  // EEPROM'u tamamen temizlemek yerine, sadece yazma adresini sıfırlayalım.
  // Gerçekten temizlemek isterseniz, her adrese 0xFF yazmanız gerekir.
  // Bu işlem uzun sürebilir.
  currentEEPROMAddress = 0; 
  Serial.println("EEPROM yazma adresi sıfırlandı.");
}

Devre Bağlantıları:

  • Arduino Uno/Nano:
    • SDA (A4) -> BME280 SDA, AT24C256 SDA
    • SCL (A5) -> BME280 SCL, AT24C256 SCL
    • VCC (3.3V veya 5V, sensör ve EEPROM’un çalışma voltajına göre) -> BME280 VCC, AT24C256 VCC
    • GND -> BME280 GND, AT24C256 GND
  • Pull-up Dirençleri: SDA ve SCL hatlarına 4.7kΩ pull-up dirençleri bağlamayı unutmayın (çoğu modülde dahili olarak bulunur, emin değilseniz ekleyin).

Kütüphane Kurulumu:

Bu kodu derleyebilmek için Arduino IDE Kütüphane Yöneticisi’nden aşağıdaki kütüphaneleri kurmanız gerekmektedir:

  1. Adafruit Unified Sensor
  2. Adafruit BME280 Library

Çalışma Mantığı:

  1. Başlatma (setup()):
    • Seri haberleşme başlatılır.
    • BME280 sensörü başlatılır ve bağlantısı kontrol edilir.
    • AT24C256 EEPROM’un I²C adresi üzerinden varlığı doğrulanır.
    • EEPROM’da daha önce kaydedilmiş veriler varsa, başlangıçta okunur ve seri portta gösterilir.
  2. Veri Kaydı (loop() ve logSensorData()):
    • Her 5 saniyede bir BME280’den sıcaklık, nem ve basınç verileri okunur.
    • Okunan veriler SensorData yapısına dönüştürülür.
    • writeEEPROM fonksiyonu kullanılarak bu yapıdaki veriler EEPROM’a yazılır. currentEEPROMAddress değişkeni, bir sonraki kaydın yazılacağı adresi tutar ve her yazma işleminden sonra güncellenir.
    • EEPROM’un kapasitesi dolduğunda uyarı verilir.
  3. Veri Okuma (readAllEEPROMData()):
    • Seri porttan ‘r’ veya ‘R’ karakteri gönderildiğinde, readAllEEPROMData() fonksiyonu çağrılır.
    • Bu fonksiyon, currentEEPROMAddress’e kadar olan tüm kayıtları EEPROM’dan okur ve seri portta görüntüler.
  4. EEPROM Temizleme (clearEEPROM()):
    • Seri porttan ‘c’ veya ‘C’ karakteri gönderildiğinde, clearEEPROM() fonksiyonu çağrılır.
    • Bu fonksiyon, currentEEPROMAddress’i sıfırlayarak EEPROM’un başına dönülmesini sağlar. Not: Bu fonksiyon EEPROM’daki veriyi fiziksel olarak silmez, sadece yeni yazma işlemlerinin en baştan başlamasını sağlar. EEPROM’u tamamen silmek için her adrese 0xFF yazmak gerekir ki bu işlem büyük EEPROM’lar için uzun sürebilir.

Bu örnek, I²C’nin çok yönlülüğünü ve EEPROM’ların kalıcı veri depolama yeteneklerini birleştirerek, gömülü sistem projelerinde sıkça karşılaşılan bir ihtiyaca çözüm sunmaktadır.

EEPROM’lar (Electrically Erasable Programmable Read-Only Memory), gücü kesildiğinde bile veriyi saklayabilen, non-volatile (kalıcı) bellek yongalarıdır. I2C tabanlı EEPROM’lar, sadece iki pin kullanarak sisteme kalıcı depolama eklemenin en kolay yoludur.

  • Kullanım Alanları: Cihaz konfigürasyon ayarları, kalibrasyon verileri, hata logları, kullanıcı tercihleri.
  • I2C Konsepti: Bu cihazlar, Random Access ve Sequential Read/Write işlemlerinin en klasik örnekleridir. Veri yazmak veya okumak için önce 2 byte’lık bir bellek adresi (örneğin 16-bit adresleme için) gönderilir.
  • Tipik Yazma İşlemi (Page Write):
    1. START
    2. Cihaz Adresi + W -> ACK
    3. Bellek Adresi (Yüksek Byte) -> ACK
    4. Bellek Adresi (Düşük Byte) -> ACK
    5. Veri 1 -> ACK
    6. Veri 2 -> ACK
    7. … (Genellikle 64 byte’a kadar)
    8. Veri N -> ACK
    9. STOP
  • Tipik Okuma İşlemi (Random Read): Bu işlem, Combined Format (Repeated START) kullanımının zorunlu olduğu en güzel örnektir. Önce okunacak adres ayarlanır, ardından bus serbest bırakılmadan okuma moduna geçilir.
Faz 1: Adres AyarlamaFaz 2: Veri OkumaSCLSDASTARTADDR+W Cihaz Adresi + WACK← EEPROMMEM HIGHBellek Adresi (Yüksek)ACK← EEPROMMEM LOW Bellek Adresi (Düşük)ACK← EEPROMSrADDR+R Cihaz Adresi + RACK← EEPROM10100101Veri Byte (0xA5)NACK← MasterSTOP

Şekil 9.1: EEPROM Rastgele Okuma Sekansı

Bu diyagram, bir I2C EEPROM'dan rastgele okuma işleminin ayrıntılı adımlarını gösterir. Master, önce yazma işlemiyle adresleri ayarlar, ardından "Repeated Start" ile okuma moduna geçer ve EEPROM'un gönderdiği veriyi (örnek: 0xA5) alır.


RTC (Real-Time Clock) Modülleri (Örn: DS3231, PCF8523)

RTC’ler (Gerçek Zaman Saatleri), sistemin gücü kapalıyken dahi küçük bir yedek pil ile zamanı ve tarihi saymaya devam eden özel entegrelerdir.

  • Kullanım Alanları: Veri kaydı sırasında zaman damgası ekleme, alarm sistemleri, dijital saatler, takvim uygulamaları gibi senaryolarda yaygın olarak kullanılır.

  • I²C Konsepti: RTC entegrelerinin içinde saniye, dakika, saat, gün, ay ve yıl gibi bilgiler için ayrı register alanları bulunur. Bu verilere erişmek için I²C üzerinden doğrudan bu register adreslerine ulaşılır. Özellikle Sequential Read yöntemi oldukça kullanışlıdır; çünkü tek bir okuma komutu ile tüm zaman verilerini blok olarak alabilirsiniz.

  • Tipik Zaman Okuma İşlemi:

    1. Master, RTC’nin saniye register’ının adresini (genellikle 0x00) yazar.
    2. Repeated START ile iletişim kesilmeden okuma moduna geçer.
    3. Ardından sırasıyla 7 byte’lık bir okuma yapılır: saniye, dakika, saat, gün, tarih, ay ve yıl. Her byte için ACK, son byte için NACK gönderilir.
RegisterİçerikBit Dağılımı (7 → 0)Format
0x00Saniye
CH
10s
1s
BCD
0x01Dakika
0
10m
1m
BCD
0x02Saat
0
12/24
10h/AP
1h
BCD
0x03Gün (haftanın)
0
Gün
Sayısal
0x04Tarih (ayın)
0
10d
1d
BCD
0x05Ay & Yüzyıl
C
0
10M
1M
BCD
0x06Yıl
10y
1y
BCD

Açıklamalar

  • Bit Dağılımı: Bu sütun, her bir 8-bitlik register'ın iç yapısını gösterir. Renkler bitlerin işlevini belirtir: Kontrol, BCD (Yüksek), BCD (Düşük), Sayısal, ve Kullanılmayan.
  • BCD (Binary-Coded Decimal): Her 4 bitlik grup (nibble), bir ondalık basamağı temsil eder. Örneğin, `35` sayısı BCD'de `0011 0101` olarak kodlanır. Bu, mikrokontrolcülerin insan tarafından okunabilir sayılarla doğrudan çalışmasını kolaylaştırır.
  • Kontrol Bitleri: `CH` (Clock Halt) osilatörü durdurur, `12/24` saat modunu değiştirir ve `C` (Century) yüzyıl değişikliğini işaret eder. Bu bitleri değiştirmek için bit maskeleme işlemleri gerekir.

Şekil 9.2: RTC Register ve Bit Dağılımı


Sensör Entegreleri (Örn: BME280 - Sıcaklık/Nem/Basınç, MPU-6050 - İvme/Gyro)

Modern sensörlerin büyük çoğunluğu I2C (veya SPI) arayüzü kullanır. Bu, karmaşık ölçüm yeteneklerini sadece 2-4 pin ile bir mikrokontrolcüye bağlamayı sağlar.

  • Kullanım Alanları: Hava durumu istasyonları, drone’lar ve robotlar (yönelim tespiti), akıllı cihazlar.
  • I2C Konsepti: Sensörler, genellikle iki tür register seti içerir: Kontrol Register’ları ve Veri Register’ları.
    1. Konfigürasyon: Cihaz ilk açıldığında, Master kontrol register’larına yazarak sensörün çalışma modunu (örneğin ölçüm hassasiyeti, güç modu, filtre ayarları) belirler.
    2. Okuma: Master, veri register’larından ardışık bir okuma yaparak ölçüm sonuçlarını (örneğin ivme için X, Y, Z eksen verileri) alır.

SensorBlockDiagram

BME280SDASCLDijitalArayüzKontrolLojigiSıcaklık SensörüBasınç SensörüNem SensörüADC(Analog-Dijital Dönüştürücü)Register Haritası(Veri & Konfigürasyon)ID (0xD0)RESET (0xE0)ctrl_hum (0xF2)status (0xF3)ctrl_meas (0xF4)config (0xF5)data (0xF7-0xFE)Veri/KomutKontrolAnalog VeriDijital VeriKonfig./DurumDoğrudan Erişim

Bu diyagram, bir BME280 sensörünün iç yapısını ve I2C/SPI arayüzünün sensörün farklı fonksiyonel bloklarına nasıl eriştiğini görselleştirir. Sensör çekirdeklerinden gelen analog veriler ADC tarafından dijitale dönüştürülür ve Register Haritası üzerinden erişilebilir hale gelir. Kontrol Lojigi, tüm işlemleri yönetir ve Dijital Arayüz üzerinden dış dünya ile iletişim kurar.

Şekil 9.3 : Sensör Blok Diyagramı

Entegre Proje: BME280 Sensör Verilerini DS3231 Zaman Damgası ile EEPROM’a Kaydetme

Bu bölümde, I²C protokolünün çok yönlülüğünü gösteren entegre bir proje sunulmaktadır. Bu proje, bir BME280 ortam sensöründen (sıcaklık, nem, basınç) alınan verileri, bir DS3231 Gerçek Zaman Saati (RTC) modülünden alınan zaman damgasıyla birlikte harici bir AT24C256 EEPROM belleğe kaydetmeyi amaçlamaktadır. Bu sayede, güç kesintilerinde bile sensör verilerinin ne zaman alındığı bilgisiyle birlikte kalıcı olarak saklanması sağlanır.

Bu örnek, I²C bus üzerinde birden fazla cihazın (BME280, DS3231, AT24C256) nasıl sorunsuz bir şekilde birlikte çalışabileceğini göstermektedir.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "RTClib.h" // Adafruit RTClib kütüphanesi

// BME280 sensörü için I2C adresi (genellikle 0x76 veya 0x77)
#define BME_ADDRESS 0x76 

// AT24C256 EEPROM için I2C adresi (genellikle 0x50)
#define EEPROM_ADDRESS 0x50 

// EEPROM'da veri yazmaya başlanacak adres
// AT24C256 32KB (32768 byte) kapasiteye sahiptir.
// Her okuma yaklaşık 19 byte (3 float * 4 byte/float + 6 byte zaman) yer kaplayacak.
// Bu örnekte basitlik adına her kaydı ardışık adreslere yazacağız.
// Gerçek uygulamalarda dairesel tampon (circular buffer) veya 
// dosya sistemi benzeri yapılar kullanılabilir.
int currentEEPROMAddress = 0; 

Adafruit_BME280 bme; // I2C
RTC_DS3231 rtc; // DS3231 RTC objesi

char daysOfTheWeek[7][12] = {"Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"};

struct SensorData {
  float temperature;
  float humidity;
  float pressure;
  uint16_t year;
  uint8_t month;
  uint8_t day;
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
};

void setup() {
  Serial.begin(9600);
  while (!Serial); // Seri portun açılmasını bekle (sadece Leonardo ve türevleri için)

  Serial.println(F("BME280, DS3231 ve EEPROM Entegre Veri Kaydedici Başlatılıyor..."));

  // BME280 sensörünü başlat
  if (!bme.begin(BME_ADDRESS)) {
    Serial.println("BME280 sensörü bulunamadı! Bağlantıları kontrol edin.");
    while (1);
  }
  Serial.println("BME280 sensörü başarıyla başlatıldı.");

  // DS3231 RTC'yi başlat
  if (! rtc.begin()) {
    Serial.println("RTC bulunamadı! Bağlantıları kontrol edin veya pini değiştirin.");
    Serial.flush();
    abort();
  }
  Serial.println("DS3231 RTC başarıyla başlatıldı.");

  if (rtc.lostPower()) {
    Serial.println("RTC gücü kaybetti, zamanı derleme zamanına ayarlayalım!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // EEPROM'u kontrol et (sadece varlığını doğrulamak için basit bir kontrol)
  Wire.beginTransmission(EEPROM_ADDRESS);
  if (Wire.endTransmission() == 0) {
    Serial.println("AT24C256 EEPROM başarıyla bulundu.");
  } else {
    Serial.println("AT24C256 EEPROM bulunamadı! Bağlantıları kontrol edin.");
    while (1);
  }

  // EEPROM'daki ilk 10 kaydı oku (varsa)
  Serial.println("\nEEPROM'daki Mevcut Veriler:");
  readAllEEPROMData();
}

void loop() {
  // Her 5 saniyede bir veri oku ve kaydet
  static unsigned long lastLogTime = 0;
  if (millis() - lastLogTime > 5000) {
    lastLogTime = millis();
    logSensorData();
  }

  // Seri porttan 'r' veya 'R' gelirse tüm verileri oku
  if (Serial.available()) {
    char command = Serial.read();
    if (command == 'r' || command == 'R') {
      Serial.println("\nSeri Porttan Okuma İsteği Alındı. EEPROM Verileri:");
      readAllEEPROMData();
    } else if (command == 'c' || command == 'C') {
      Serial.println("\nSeri Porttan Temizleme İsteği Alındı. EEPROM Temizleniyor...");
      clearEEPROM();
      Serial.println("EEPROM temizlendi. currentEEPROMAddress sıfırlandı.");
    }
  }
}

void logSensorData() {
  SensorData data;
  data.temperature = bme.readTemperature();
  data.humidity = bme.readHumidity();
  data.pressure = bme.readPressure() / 100.0F; // hPa olarak

  DateTime now = rtc.now();
  data.year = now.year();
  data.month = now.month();
  data.day = now.day();
  data.hour = now.hour();
  data.minute = now.minute();
  data.second = now.second();

  Serial.print("Zaman: ");
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" (");
  Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
  Serial.print(") ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print(", Sıcaklık: ");
  Serial.print(data.temperature);
  Serial.print(" *C, Nem: ");
  Serial.print(data.humidity);
  Serial.print(" %, Basınç: ");
  Serial.print(data.pressure);
  Serial.println(" hPa");

  // Veriyi EEPROM'a yaz
  // Her float 4 byte, her uint8_t 1 byte, uint16_t 2 byte. Toplam 3*4 + 5*1 + 1*2 = 12 + 5 + 2 = 19 byte.
  // sizeof(SensorData) ile otomatik boyut hesaplanır.
  if (currentEEPROMAddress + sizeof(SensorData) <= 32768) { // AT24C256 için 32KB
    writeEEPROM(EEPROM_ADDRESS, currentEEPROMAddress, (byte*)&data, sizeof(SensorData));
    Serial.print("Veri EEPROM'a yazıldı, adres: ");
    Serial.println(currentEEPROMAddress);
    currentEEPROMAddress += sizeof(SensorData); // Sonraki kayıt için adresi güncelle
  } else {
    Serial.println("EEPROM dolu! Yeni veri kaydedilemiyor.");
  }
}

void writeEEPROM(int deviceAddress, int eeAddress, byte* data, int len) {
  Wire.beginTransmission(deviceAddress);
  Wire.write((int)(eeAddress >> 8));   // Yüksek adres baytı
  Wire.write((int)(eeAddress & 0xFF)); // Düşük adres baytı

  for (int i = 0; i < len; i++) {
    Wire.write(data[i]);
  }
  Wire.endTransmission();
  delay(5); // EEPROM yazma döngüsü için kısa bir gecikme
}

void readEEPROM(int deviceAddress, int eeAddress, byte* data, int len) {
  Wire.beginTransmission(deviceAddress);
  Wire.write((int)(eeAddress >> 8));   // Yüksek adres baytı
  Wire.write((int)(eeAddress & 0xFF)); // Düşük adres baytı
  Wire.endTransmission();

  Wire.requestFrom(deviceAddress, len); // Belirtilen uzunlukta veri iste
  for (int i = 0; i < len; i++) {
    if (Wire.available()) {
      data[i] = Wire.read();
    }
  }
}

void readAllEEPROMData() {
  SensorData readData;
  int address = 0;
  int recordCount = 0;

  while (address + sizeof(SensorData) <= currentEEPROMAddress) {
    readEEPROM(EEPROM_ADDRESS, address, (byte*)&readData, sizeof(SensorData));
    Serial.print("Kayıt ");
    Serial.print(recordCount++);
    Serial.print(": Zaman: ");
    Serial.print(readData.year, DEC);
    Serial.print("/");
    Serial.print(readData.month, DEC);
    Serial.print("/");
    Serial.print(readData.day, DEC);
    Serial.print(" ");
    Serial.print(readData.hour, DEC);
    Serial.print(":");
    Serial.print(readData.minute, DEC);
    Serial.print(":");
    Serial.print(readData.second, DEC);
    Serial.print(", Sıcaklık: ");
    Serial.print(readData.temperature);
    Serial.print(" *C, Nem: ");
    Serial.print(readData.humidity);
    Serial.print(" %, Basınç: ");
    Serial.print(readData.pressure);
    Serial.println(" hPa");
    address += sizeof(SensorData);
  }
  if (recordCount == 0) {
    Serial.println("EEPROM'da henüz kaydedilmiş veri yok.");
  }
}

void clearEEPROM() {
  // EEPROM'u tamamen temizlemek yerine, sadece yazma adresini sıfırlayalım.
  // Gerçekten temizlemek isterseniz, her adrese 0xFF yazmanız gerekir.
  // Bu işlem uzun sürebilir.
  currentEEPROMAddress = 0; 
  Serial.println("EEPROM yazma adresi sıfırlandı.");
}

Devre Bağlantıları:

  • Arduino Uno/Nano:
    • SDA (A4) -> BME280 SDA, DS3231 SDA, AT24C256 SDA
    • SCL (A5) -> BME280 SCL, DS3231 SCL, AT24C256 SCL
    • VCC (3.3V veya 5V, sensör ve EEPROM’un çalışma voltajına göre) -> BME280 VCC, DS3231 VCC, AT24C256 VCC
    • GND -> BME280 GND, DS3231 GND, AT24C256 GND
  • Pull-up Dirençleri: BME280, DS3231 ve AT24C256 modüllerinde genellikle dahili pull-up dirençleri bulunur. Eğer modülünüzde yoksa, SDA ve SCL hatlarına 4.7kΩ pull-up dirençleri eklemeniz gerekebilir.

Kütüphane Kurulumu:

Bu kodu derleyebilmek için Arduino IDE Kütüphane Yöneticisi’nden aşağıdaki kütüphaneleri kurmanız gerekmektedir:

  1. Adafruit Unified Sensor
  2. Adafruit BME280 Library
  3. Adafruit RTClib

Çalışma Mantığı:

  1. Başlatma (setup()):
    • Seri haberleşme başlatılır.
    • BME280 sensörü ve DS3231 RTC başlatılır ve bağlantıları kontrol edilir.
    • RTC gücü kaybetmişse, derleme zamanına ayarlanır.
    • AT24C256 EEPROM’un varlığı doğrulanır.
    • EEPROM’da daha önce kaydedilmiş veriler varsa, başlangıçta okunur ve seri portta gösterilir.
  2. Veri Kaydı (loop() ve logSensorData()):
    • Her 5 saniyede bir BME280’den sıcaklık, nem ve basınç verileri ile DS3231’den güncel zaman bilgisi okunur.
    • Okunan veriler SensorData yapısına dönüştürülür.
    • writeEEPROM fonksiyonu kullanılarak bu yapıdaki veriler EEPROM’a yazılır. currentEEPROMAddress değişkeni, bir sonraki kaydın yazılacağı adresi tutar ve her yazma işleminden sonra güncellenir.
    • EEPROM’un kapasitesi dolduğunda uyarı verilir.
  3. Veri Okuma (readAllEEPROMData()):
    • Seri porttan ‘r’ veya ‘R’ karakteri gönderildiğinde, readAllEEPROMData() fonksiyonu çağrılır.
    • Bu fonksiyon, currentEEPROMAddress’e kadar olan tüm kayıtları EEPROM’dan okur ve seri portta görüntüler.
  4. EEPROM Temizleme (clearEEPROM()):
    • Seri porttan ‘c’ veya ‘C’ karakteri gönderildiğinde, clearEEPROM() fonksiyonu çağrılır.
    • Bu fonksiyon, currentEEPROMAddress’i sıfırlayarak EEPROM’un başına dönülmesini sağlar. Not: Bu fonksiyon EEPROM’daki veriyi fiziksel olarak silmez, sadece yeni yazma işlemlerinin en baştan başlamasını sağlar. EEPROM’u tamamen silmek için her adrese 0xFF yazmak gerekir ki bu işlem uzun sürebilir.

Bu örnek, I²C’nin çok yönlülüğünü ve farklı sensörlerin, RTC’nin ve EEPROM’un kalıcı veri depolama yeteneklerini birleştirerek, gömülü sistem projelerinde sıkça karşılaşılan bir ihtiyaca çözüm sunmaktadır.

DS3231 Gerçek Zaman Saati (RTC) Entegresi

DS3231, yüksek hassasiyetli bir I²C gerçek zaman saati (Real-Time Clock - RTC) entegresidir. Dahili sıcaklık kompanzasyonlu kristal osilatörü (TCXO) sayesinde çok doğru zaman tutma yeteneğine sahiptir. Genellikle projelerde tarih ve saat bilgilerini kaydetme, olayları zamanlama veya belirli zamanlarda görevleri tetiklemek için kullanılır.

Aşağıdaki Arduino kodu, bir DS3231 RTC modülünden güncel tarih ve saat bilgisini okumayı ve seri portta görüntülemeyi göstermektedir.

#include <Wire.h>
#include "RTClib.h" // Adafruit RTClib kütüphanesi

RTC_DS3231 rtc;

char daysOfTheWeek[7][12] = {"Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"};

void setup () {
  Serial.begin(9600);
  while (!Serial); // Seri portun açılmasını bekle (sadece Leonardo ve türevleri için)

  Serial.println(F("DS3231 RTC Testi Başlatılıyor..."));

  if (! rtc.begin()) {
    Serial.println("RTC bulunamadı! Bağlantıları kontrol edin veya pini değiştirin.");
    Serial.flush();
    abort();
  }

  if (rtc.lostPower()) {
    Serial.println("RTC gücü kaybetti, zamanı ayarlayalım!");
    // RTC'yi derleme zamanına ayarla
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // Veya manuel olarak ayarla: rtc.adjust(DateTime(2025, 6, 30, 12, 0, 0));
  }

  // RTC zaten ayarlıysa ve doğruysa, bu satırı yorum satırı yapabilirsiniz.
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 
}

void loop () {
  DateTime now = rtc.now();

  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(" (");
  Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
  Serial.print(") ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();

  // Her saniye bir kez oku
  delay(1000);
}

Devre Bağlantıları:

  • Arduino Uno/Nano:
    • SDA (A4) -> DS3231 SDA
    • SCL (A5) -> DS3231 SCL
    • VCC (5V) -> DS3231 VCC
    • GND -> DS3231 GND
  • Pull-up Dirençleri: DS3231 modüllerinde genellikle dahili pull-up dirençleri bulunur. Eğer modülünüzde yoksa, SDA ve SCL hatlarına 4.7kΩ pull-up dirençleri eklemeniz gerekebilir.

Kütüphane Kurulumu:

Bu kodu derleyebilmek için Arduino IDE Kütüphane Yöneticisi’nden aşağıdaki kütüphaneyi kurmanız gerekmektedir:

  1. Adafruit RTClib

Çalışma Mantığı:

  1. Başlatma (setup()):
    • Seri haberleşme başlatılır.
    • DS3231 RTC modülü başlatılır. Eğer RTC bulunamazsa veya gücü kaybetmişse (pil bitmişse veya ilk kez çalıştırılıyorsa), RTC otomatik olarak Arduino’nun derleme zamanına ayarlanır. Bu, her program yüklendiğinde zamanın güncellenmesini sağlar.
  2. Zaman Okuma (loop()):
    • Her saniye rtc.now() fonksiyonu ile güncel tarih ve saat bilgisi alınır.
    • Alınan bilgiler DateTime objesi olarak saklanır ve Serial.print() komutları ile seri portta okunabilir bir formatta görüntülenir.

Bu örnek, I²C protokolünün zaman tabanlı uygulamalarda nasıl kullanılabileceğine dair temel bir anlayış sunar. DS3231 gibi RTC’ler, veri kaydı, zaman damgası ekleme veya belirli zamanlarda olayları tetikleme gibi birçok gömülü sistem projesinde kritik rol oynar.

Referanslar

Footnotes

  1. Texas Instruments, “A Basic Guide to I2C,” Application Note SBAA565 – NOVEMBER 2022. [Online]. Available: https://www.ti.com/lit/an/sbaa565/sbaa565.pdf 2 3

  2. SparkFun Electronics, “A Brief History of I2C,” SparkFun Learn, [Online]. Available: https://learn.sparkfun.com/tutorials/i2c/a-brief-history-of-i2c. [Accessed: Jun. 12, 2025]. 2 3 4

← Tüm Yazılara Geri Dön