Arduino Ders #15: I2C Haberleşmesi

Inter Integrated Circuit kelimelerinin baş harflerinden oluşmaktadır. Arduino, diğer Arduino veya sensörlerle haberleşmek için bazı haberleşme protokolleri kullanır. Bu protokollerden birisi de I2C’dir.

I2C seri haberleşme türlerinden senkron haberleşmeye bir örnektir. Haberleşme için toprak hattı dışında SDA ve SCL olmak üzere iki hatta ihtiyaç duyulmaktadır. Hat sayısının fazla olması nedeniyle, uzun mesafeli haberleşmelerde tercih edilmez. Genellikle kısa mesafeli ve düşük veri aktarım hızının yeterli olduğu yerlerde kullanılır.  I²C ile 10 bit adresleme ile neredeyse 1024 (1008) cihaza kadar kullanılabilir ve iletişim için sadece iki tel kablo yeterlidir.

Aşağıdaki tabloda farklı arduinolardaki SDA ve SCL pinleri gösterilmiştir.

Arduino türüSDA piniSCL pini
Arduino UnoA4A5
Arduino Mega2021
Arduino Leonardo23
Arduino Due2021
Arduino NanoA4A5

I2C BUS İletişim ve Haberleşme Protokolü Nasıl Çalışır?

I2C protokolünde, aygıtlar master slave ilişkisi içerisinde haberleşirler. Yani aygıtlar ya master ya da slave olarak ayarlanmalıdırlar. Genellikle aygıtlardan biri master diğerleri slave olarak ayarlanır. Ayrıca I2C protokolünün multimaster özelliği de mevcuttur. Bu durumda hatta birden fazla master olabilir. Haberleşme master olarak seçilen aygıtın kontrolündedir. Haberleşmeyi başlatan ve bitiren master’dır.

I2C protokolünde haberleşme için iki hat kullanılır. Bunlar SCL ve SDA hatlarıdır. SCL (Serial Clock) veri senkronizasyonunu sağlayan, clock sinyallerinin iletildiği hattır. SDA (Serial Data) ise verilerin iletildiği hattır. SCL ve SDA hatlarına birer pull-up direnci bağlanmalıdır.

I²C Protokolü

Veri sinyali 8 bitlik diziler halinde aktarılır. Başlatma koşulu meydana geldikten sonra, verinin gönderildiği slave’in adresini gösteren ilk 8 bitlik sıra gelir. Her 8 bitlik sekanstan sonra Acknowledge adlı bir bit takip eder. I2C iletişimin veri yolu 8 bittir. Yani veriler 1 byte’lık bölümler halinde iletilir.

Clock sinyali lojik-1 iken, data sinyali lojik-1’den lojik-0’a geçmesi ile start komutu verilir. Aynı şekilde clock sinyali lojik-1 iken, data sinyalinin lojik-0’dan lojik-1’e geçmesi ile de stop komutu oluşturulur. Veri iletimi start komutuyla başlar stop komutuyla biter. Start – stop komutlarını master aygıt oluşturur. Master aygıttan slave aygıta veri gönderme işleminde; ilk önce start biti, ardından adres bitleri, ardından yazma/okama biti, daha sonra veri bitleri, en son olarak da stop biti gönderilir. 

Her veri gönderiminde, 8 bit gönderildikten sonra, slave aygıttan verileri aldığına dair onay biti olan ACK (acknowledge) master’a gönderilir. Bazı durumlarda (veri iletiminde hata oluşması durumunda) slave master’a ACK bitinin değili olan NACK bitini gönderir. Bu durumda master aygıt stop biti göndererek haberleşmeyi durdurur, veya tekrar start biti göndererek yeniden haberleşmeye başlar. u sırada ana cihaz SDA hattının kontrolünü slave cihaza devreder ve slave cihaz önceki diziyi başarılı bir şekilde aldıysa, SDA hattını Acknowledge adı verilen duruma çekecektir. Slave, SDA hattını aşağı çekmezse, koşul called Not Acknowledge olarak adlandırılır ve birkaç nedenden kaynaklanabilecek önceki diziyi başarıyla almadığı anlamına gelir.

I2C Protokolü ile İki Arduino’un Haberleşmesi

Master görevindeki Arduino, slave görevindeki Arduino’ya bağlı LED’leri kontrol edecek ve slave görevindeki Arduino’dan veri alacak. Slave görevindeki Arduino, master görevindeki Arduino’dan gelen veriyi yorumlayacak. Gelen veriye göre de LED’leri kontrol edecek ve diğer Arduino’ya veri yollayacak.

Bu uygulamayı yapmak için ihtiyacımız olan malzemeler:

  • 2 x Arduino
  • 2 x 4.7K ohm direnç
  • 1 x LED
  • 1 x 220 ohm direnç
  • 1 x Breadboard

Not: Devredeki iki Arduino’da ayrı ayrı veya aynı besleme kaynağından beslenmelidir. Eğer Arduino’lar ayrı kaynaklardan besleniyorsa, toprak hatlarının birleştirilmesi gerektiğini unutmayın. Slave üzerinden gelen mesajları okumak için Master görevindeki Arduino’yu bilgisayara bağlayarak Seri Monitörü açın.

Master görevindeki Arduino kodu:

/* I2C haberleşmesinde Master olarak görev yapan Arduino kodu */

#include <Wire.h>
/* 
 * I2C fonksiyonlarını kullanabilmek için 
 * Wire.h kütüphanesini projemize ekledik
 */
 
void setup()
{
  Wire.begin();
  /* I2C haberleşmesi master olarak başlatıldı */
  
  Serial.begin(9600);
  /* Bilgisayara veri yazdırabilmek için seri haberleşme başlatıldı */
}

void loop()
{
  Wire.beginTransmission(1);
  /* 1 adresine sahip Slave (köle) cihazına veri yollanacağı bildiriliyor */
  Wire.write("a");
  /* a karakteri slave cihaza yollanıyor */
  Wire.endTransmission();
  /* Yollanacak verilerin bittiği bildiriliyor */
  /* a karakteri slave cihazda LED'i yak anlamına gelecektir */
  
  delay(1000);
  
  Wire.beginTransmission(1);
  /* 1 adresine sahip Slave (köle) cihazına veri yollanacağı bildiriliyor */
  Wire.write("b");
  /* b karakteri slave cihaza yollanıyor */
  Wire.endTransmission();
  /* Yollanacak verilerin bittiği bildiriliyor */
  /* b karakteri slave cihazda LED'i sondur anlamına gelecektir */
  
  delay(1000);
  
  Wire.requestFrom(1, 7);
  /*  1 adresine sahip slave (köle) cihazından 7 BYTE'lık veri bekleniyor */
  char gelenKarakter;
  /* I2C hattından gelen veriler gelenKarakter değişkenine yazdırılacak */
  while(Wire.available()){
    /* I2C hattında yeni veri olduğu sürece döngü devam edecek */
    gelenKarakter = Wire.read();
    /* I2C hattından gelen veriler okunuyor */
    Serial.print(gelenKarakter);
    /* Gelen veriler ekrana yazdırılıyor */
  }
  Serial.println();
 
  delay(1000);
}

Master görevindeki Arduino kodunda öncelikle I2C fonksiyonlarının kullanılabilmesi için Wire.h kütüphanesi çalışmaya dahil edilmiştir. Daha sonra haberleşme Wire.begin() komutuyla master olarak başlatıldı. Slave cihazdan gelecek verilerin ekrana yazdırılabilmesi için Serial.begin(9600) komutu ile Arduino ve bilgisayar arasındaki iletişim başlatıldı.

Loop fonksiyonu içinde, bir saniye aralıklarla slave cihaza ‘a’ ve ‘b’ karakterleri yollandı. Bu karakterler slave cihazda işlenerek LED’in konumu değiştirilecek. Bu karakterlerin yollanabilmesi için öncelikle Wire.beginTransmission(1) fonksiyonuyla hangi slave cihaza veri aktarılacağı seçildi. Tüm veriler cihaza yollandıktan sonra Wire.endTransmission(); komutuyla cihaza veri aktarımının bittiği bildirildi.

Slave cihazdan veri alınmak istendiği için Wire.requestFrom(1, 7); komutu kullanıldı. Bu komutla slave cihaz 7 byte’lık veriyi master cihaza aktaracağını anladı. Yeni veri geldiği sürece işlemin devam edebilmesi için, while döngüsünün koşulu Wire.available() yapılır. I2C data hattından gelen veriler Wire.read() fonksiyonuyla okunarak seri porta yollandı.

Slave görevindeki Arduino kodu

/* Slave (köle) görevindeki Arduino'nun kodu */
#include <Wire.h>
/* 
 * I2C fonksiyonlarını kullanabilmek için 
 * Wire.h kütüphanesini projemize ekledik
 */
 
 const int LED = 13;
 /* LED 13. pinde bulunmaktadır */
 
void setup()
{
  Wire.begin(1);
  /* I2C haberleşmesi, haberleşme adresi 1 olan bir slave cihaz olarak başlatıldı */
  Wire.onRequest(istekGeldiginde);
  /* 
  Master olan cihaz bu Arduino'dan veri istediğinde gerçekleşecek işlem seçildi
  */
  Wire.onReceive(veriGeldiginde);
  /*
  Master olan cihazdan bu Arduino'ya veri geldiğinde yapılacak işlem seçildi
  */
  
  pinMode(LED,OUTPUT);
  /* LED pini çıkış olarak ayarlandı */
}
 
void loop()
{
  /*
  * Tüm işlemler veri isteği geldiğinde veya yeni veri geldiğinde 
  * yapılacağı için loop fonksiyonunun içi boş bırakılmıştır
  */
  delay(1);
}
 
void veriGeldiginde(int veri)
{
  /* I2C hattında bu cihaz için yeni veri olduğunda bu fonksiyon çalışır */
  char gelenKarakter;
  /* Hattaki veri okunarak gelenKarakter değişkenine kaydedilir */
  while(Wire.available()){
    gelenKarakter = Wire.read();
  }
  /* Eğer gelen veri 'a' ise LED yakılır, 'b' ise LED söndürülür */
  if(gelenKarakter == 'a')
    digitalWrite(LED,HIGH);
  else if(gelenKarakter == 'b')
    digitalWrite(LED,LOW);
}
 
void istekGeldiginde()
{
  /* 
  * Eğer master bu cihazdan veri istiyor ise master cihaza "Merhaba" verisi yollanılır 
  * Eğer bu bir sensör olsaydı "merhaba" yerine sıcaklık veya ivme verisi yollanıyor olacaktı
  */
  Wire.write("Merhaba"); 
}

Slave görevindeki Arduino kodunda öncelikle I2C fonksiyonlarının kullanılabilmesi için Wire.h kütüphanesi çalışmaya dâhil edildi. Daha sonra haberleşme Wire.begin(1) komutuyla 1 adresine sahip slave cihaz olarak başlatıldı. Master cihazından veri geldiğinde “veriGeldiginde()” fonksiyonu, istek geldiğinde “istekGeldiginde()” fonksiyonunun çalışması için “Wire.onReceive(veriGeldiginde)” ve “Wire.onRequest(istekGeldiginde)” fonksiyonları kullanıldı.

Programın ana yapısı bu iki fonksiyon ile çalışır. Arduino’ya master cihazdan yeni veri geldiğinde bu veri okunarak ‘gelenKarakter’ değişkenine yazdırılır. Gelen karakter eğer ‘a’ harfine eşitse LED yakılır, ‘b’ harfine eşitse LED söndürülür. Eğer master cihazdan veri isteği geldiyse, master cihaza “Merhaba” yazısı geri döndürülür. Eğer bu Arduino bir sensör olsaydı, burada “Merhaba” yazısı yerine ortam sıcaklığını veya ivme verisini döndürecekti.

UYGULAMA 2

Şimdi ikinci uygulamamıza geçelim.İkinci  uygulamamız şu şekilde olacak; Master kartımızdan iki karakter yollayacağız bunlar ‘k’ ve ‘y’ karakterleri olacak.Master kartımıza konsoldan girdiğimiz bu karakterler Slave kartımıza bağlı olan kırmızı ve yeşil renkteki ledleri yakacak.

Malzeme Listesi:

  • x2 Arduino Uno
  • Jumper
  • Yeşil ve Kırmızı Led

Bağlantı Şeması:

Arduino unoda SDA pini A4 pini,SCL pini ise A5 nolu pindir.Arduino Mega’da ise SDA ve SCL pinleri, 20 ve 21 nolu pinlerdir.

KOD:

Master olarak seçilen arduino kodları:

//Master Code
#include<Wire.h>

void setup()
{
  Wire.begin();
  Serial.begin(9600);
}

void loop()
{
Wire.beginTransmission(1); //master aygıtın veri gönderimini baslatır.parametre slave adresidir. 
if(Serial.available()>0){  //Konsoldan karakter girdiğimiz bölüm
char c = Serial.read();
Wire.write(c);
}
Wire.endTransmission(); // master aygıtın veri gönderimini durdurur.
}

Slave olarak seçilen Arduino kodları:

//Slave
#include<Wire.h>
int led1=13;
int led2=12;
void setup()
{
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
Wire.begin(1);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
}
 
void loop()
{
delay(100);
}
 
void receiveEvent(int howMany)
{
 if( Wire.available()>0) // Haberleşmenin olup olmadıgını kontrol eder
  {
    char c = Wire.read(); 
    Serial.print(c);         
  
  if(c == 'k'){digitalWrite(led1,HIGH);
delay(1000);
digitalWrite(led1,LOW);}
  if(c == 'y'){digitalWrite(led2,HIGH);
delay(1000);
digitalWrite(led2,LOW);}
}
  
}

Masterdan göndereceğimiz karakteri konsoldan girmemiz gerekir.Bunu arduino’nun IDE ‘sinin sağ üstünde bulunan Serial Monitor’dan yapabilirsiniz

Wire.h Kütüphanesi

https://www.arduino.cc/en/reference/wire

Arduino ile I2c haberleşmesinde Wire.h kütüphanesini kullanmaktayız.bu kütüphanenin bazı fonksiyonlarına kısaca değinecek olursak:
Wire.begin()

Haberleşmeyi başlattığımız fonksiyondur. Haberleşmeyi master olarak başlatmak istiyorsak, parametre girmeyiz. Eğer haberleşmeyi slave olarak başlatmak istiyorsak parametre olarak adres bilgisi gireriz.
Wire.beginTransmission(adres)

Master aygıttan adresini parametre olarak girdiğimiz slave aygıta veri gönderimini başlatmak için bu ifadeyi kullanırız.
Wire.endTransmission()

Master aygıtın veri gönderimini bitirmek için kullanılan fonksiyondur.
Wire.onReceive(işlem)

Master’dan slave’e veri geldiğinde yapılacak olan işlemleri belirttiğimiz ifadedir. Parametre olarak bir fonksiyon alır. Biz  salave’e veri geldiğinde ne yapmak istiyorsak bu fonksiyonda tanımlarız. Bu oluşturduğumuz fonksiyon da parametre olarak integer bir parametre ile tanımlanmalıdır.
Wire.available()

Biz verilerin gelmeye başladığını bu fonksiyon ile anlarız. Bu fonksiyon okunmamış (okunmayı bekleyen) veri sayısını dönderir.
Wire.read()

Gelen veriyi okumak için kullanılan fonksiyondur.
Wire.write()

Parametre olarak girilen ifadeyi ileten ifadedir.
Wire.requestFrom()

Slave’lerden veri istediğimiz ifadedir. Parametre olarak üç değer alır. İlk parametre veri isteyeceğimiz slave’in adresidir. İkinci parametrede kaç byte’lık veri istediğimizi bildiririz. Son parametresi ise boolean bir değişkendir.
Wire.onRequest()

Veri istendiğinde slave’in ne yapacağını belirttiğimiz ifadedir. Parametre olarak bir fonksiyon alır. Bu fonksiyonda neler yapılması gerektiğini tanımlarız.

kaynak: robocombo.com, arduino.cc/en/reference/wire, gelecegiyazanlar.com https://elektrikelektronikprojeleri.blogspot.com/2015/05/arduino-i2c-haberlesme.html

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir