STM32F407 I2C KULLANIMI

        

 STM32F407 I2C KULLANIMI

    Çoğu zaman sensörler mikroişlemcilerden uzakta kullanılırlar. Bunun için kullanılan ADC’ler I2C, SPI veya Paralel veri yollarından birini kullanarak işlemciye 8 veya 16-bit olarak sensörden okunan analog bilginin dijital değerini gönderirler. Sensörlerin dışında birçok dijital IC’de de I2C iletişim kullanılmaktadır. Bu incelememizde I2C iletişiminin nasıl yapıldığını açıklamaya çalışacağım.
Figür 1. I2C veri yollu kullanılarak bir MASTER cihaz birden fazla SLAVE cihazla veri transferinde kullanılabilir.

I2C veri iletişiminde toplam 3(üç) hat kullanılır.
  1. Ground (GND) işlemci ve kullanılacak olan I2C’nin GND’leri ortak olmalıdır.
  2. SCL (Serial Clock) saat sinyali
  3. SDA(Serial Data) veri sinyali
Bu hat üzerinden birden çok I2C veri hattı olan IC kullanılabilir. Fakat sadece MASTER adı verilen cihaz veri yolu üzerindeki veri akışını kontrol edebilir. Diğer cihazlara da SLAVE adı verilmektedir. 

Tüm I2C veri yoluna bağlı olan cihazlar “open collector” veya “open drain” özelliktedir. bu özellikte olan cihazların kullanılması için veri yolundaki veri hızına bağlı olarak pull-up direnç ile besleme voltajına çekilmesi gerekir. Tipik olarak bu bahsi geçen pull-up dirençlerinin değeri 10KOhm’dur.
I2C veri yolu üzerinden veri transferi I2C protokolünde anlatılmıştır. Biz burada üzerinden kısaca geçmeye çalışacağız.
SCL(Serial Clock) ve SDA(Serial Data) sinyallerinin 4 (dört) tip kombinasyonu vardır.

Start Kombinasyonu

SDA ve SCL lojik “1” yani High olarak kuruludur. Bundan sonraki anlatımlarımızda lojik “1” yerine High, lojik “0” yerine de Low diyeceğiz. Start kombinasyonunda ilk olarak SDA Low’a gider ve ardından SCL Low’a gider. Önemli olan SDA sinyali High durumundan Low konumuna geçerken SCL sinyalinin High konumunda olmasıdır. Figür 2a’da bu net olarak görülmektedir.

Stop Kombinasyonu

SDA ve SCL sinyalleri Low durumundadır. İlk olarak SCL sinyali High konumuna geçer ve daha sonra SDA sinyali High konumuna geçer. Bu durum Figür 2b’de net olarak görülmektedir. Burada önemli olan SDA sinyali Low konumundan High konumuna geçerken SCL sinyalinin High konumunda olmasıdır. Stop kombinasyonu veri yolu üzerindeki veri iletişiminin bittiğini ifade eder.

Bit Transfer Kombinasyonu

SDA sinyali Low veya High konumuna SCL sinyalinin Low konumundan High konumuna her geçişinde değişebilir. SCL sinyali her bir gönderilecek olan bit için Low, High ve Low olmalıdır. Bu durum Figür 2c’de net olarak görülmektedir. 8 Bit data bu yöntemle veri yoluna basılmış olur. SCL sinyalinin her değişiminde SDA sinyali değişmek zorunda değildir. Örneğin arda arda “1” gönderilecek ise SCL sinyali 2 kez Low-High_Low konumuna geçer fakat SDA sinyali ilk anda High olur ve öylece kalır.

Onay(Acknowledge) Kombinasyonu

Hedef cihaz güvenli bir şekilde gönderilen veriyi aldığında bu kombinasyon gerçekleşir. Her 8-bit veride bir onay kombinasyonu gerçekleşir. Veriyi gönderen cihaz 8-bit veriyi gönderdikten sonra SDA hattını giriş olarak ayarlar. SDA hattı pull-up direnci ile High konumunda durur. Bu durumda iken hedef cihaz veriyi doğru aldı ise SDA hattını SCL sinyalinin 1 saat sinyali zamanında Low konumuna alır. Hedef cihaz SDA sinyalinin Low konumda olduğunu algılar 8-bit veri güvenli olarak SLAVE cihaza ulaşmış demektir. Bu durum Figür 2d’de net bir şekilde görülmektedir.

Figür 2. I2C sinyalleri

Veri yolu üzerinde birden fazla Slave cihaz var ise Master cihaz anlık sadece bir Slave cihazla iletişim kurabilir. İletişim kuracağı Slave cihazı adresler. I2C standardında 7-bit adres veya 10-bit adreslemeye izin verilir. Biz burda 7-bit adreslemeyi kullanacağız.
Master cihaz Slave cihaza veri gönderildiğini düşünelim Figür 3’ün üst bölümünde olduğu gibi. Master cihaz ilk olarak Start kombinasyonunu ve ardından 8-bit gönderir. Gönderdiği 8-bit’in 7-bit’i slave cihazın adresidir. 8nci bit Low sinyalidir. Yani Slave cihaza yazma işlemi yapacağını belirtir. Master cihaz 8-bit’in ardından SCL hattında bir clock pulse oluşturarak Slave cihaz SDA hattını Low konumuna çekerek Onay kombinasyonunu oluşturur. Ardından Master cihaz bir veya birden fazla 8-bit veriyi Slave cihaza gönderir. Her gönderilen 8-bit veri için Slave adresten onay(ACK) sinyali alır. Veri gönderme işi tamamlandığında Master cihaz stop kombinasyonunu oluşturur. Bu uygulamamızda slave adres olarak 0x37 (Hex) ve veri olarak ta 0x47(Hex) kullanıldı.
Slave cihazdan okuma işlemi yazma işlemine benzer. Master önce Start kombinasyonu oluşturur ve adres byte’nı gönderir. Figür 3’ün alt kısmında bu durum gösterilmiştir. Start kombinasyonundan sonra gönderilen byte’ın 7-bit’i adres ve 8nci biti ise lojik ‘1’ yani SDA sinyali High yapılır. Bunun anlamı okuma yapılacağıdır. Slave cihaz SDA sinyalini Low konumuna alarak işlemi onaylar(Onay Kombinasyonu). Bu şekilde birden fazla veri Slave cihazdan okunabilir. Okunan her bir byte veri için Onay kombinasyonu oluşur ve Master cihaz bu durumu kontrol eder. Tüm okuma işlemi bittiğinde Master cihaz Stop kombinasyonu oluşturur. 

Figür 3 I2C veri yolu üzerinden Slave cihaza yazma (üst kısım), Slave cihazdan okuma(alt kısım)

Tüm  logic elemanlar içerisinde bir I2C bloğu bulundurur. STM32F407 işlemcisinde 3 adet I2C bloğu bulunur. Bunlar sırasıyla I2C1, I2C2 ve I2C3 ile isimlendirilir. Figür 4’de STM32F407 işlemcisinin içerisindeki I2C bloğu yapısı basit bir şekilde gösterilmiştir.
Mikroişlemci 2 adet pine ihtiyaç duyar SDA ve SCL pinleri. I2C blok yapısında SDA pininden gelen veya SDA pinine gönderilecek veri byte’ı için bit-bit gönderme veya alma yapabilen Data Shift Register bulunur. Bu Data Shift Register’a ulaşmak için DR registerı kullanılır. Mikroişlemci I2C veri yolu üzerinden veri gönderirken veya veri okurken DR register’ına yazar veya okur. I2C veri yolundaki iletişim hızının ayarını da CCR(Clock Control Register) üzerinden yapar. I2C veri yolundaki SDA ve SCL hattının durumlarını SR ve SR2 Status register’ları tutar. Bu register’lar okunarak hat üzerindeki durum kontrol edilir. I2C bloğunda yer alan CR1 ve CR2 Kontrol register’larının gerekli bit’leri set edilerek olaylar ve kesme istekleri yönetilebilir.

Figür 4. Basitleştirilmiş STM32F407 I2C Blok Yapısı

Şimdi STM32F407 mikroişlemcisi için I2C2 veri yolunun kurulumunu inceleyelim.
void I2C2 Init (void) {
// I2C2 için pin kurulumlarının yapıldığı kısım
(1)    RCC->AHB1ENR        |= 0x00000002;   // GPIOB portu için clock aktif edilir.
(2)    GPIOB -> AFR[1]       |= 0x00004400;   // PB10 ve PB11 için AF4 seçilir
(3)    GPIOB -> MODER      |= 0x00a00000;   // PB10, 11 alternatif fonksiyonu set edilir.   
(4) GPIOB -> OTYPE          |= 0x0c00;        // kullanılan bu pinler open-drain çıkış olarak set edilir.

// I2C bloğunun kurulumu
(5)    RCC->APB1ENR         |= 0x00400000;// I2C2 clock aktif edilir
(6)    I2C2 -> CR2              |= 0x0008;      // clock = 8MHz
(7)    I2C2 ->CCR               |= 0x0040;     // Clock kontrol register (270kHz)
(8)    I2C2 ->TRISE             |= 0x0009;     // Rise Time Register
(9)    I2C2 -> CR1              |= 0x0001;     // I2C2 aktif edilir.
}

(1)    STM32f407 mikroişlemcisinde I2C2 veri yolu PB10 ve PB11 pinlerini kullanır. Bunun için GPIOB portunun kullanılabilmesi için clock’u aktif edilir.
(2)    PB10 ve PB11 pinlerini I2C2 veri yolu olarak set eder.
(3)    PB10 ve PB11 pinlerinin Alternatif fonksiyonunun kullanılacağı belirtilir.
(4)    PB10 ve PB11 pinleri open-drain olarak set edilir. Bu konu daha önce anlatılmıştı.
(5)    I2C2 bloğunun clock’u aktif edilir.
(6)    I2C2 bloğunun clock frekansı 8MHz olarak set edilir.
(7)    I2C2 bloğunun veriiii iletişim hızı yani SCL hattındaki frekans 270KHz olarak set edilir.
(8)    CR2 register değeri 0x0008 girilmiş ise TRISE yani SCL hattının yükselme zamanı(rise time) değerinin 0x0009 girilmesi gerektiği STM32F407 “RM0090 Referance Manuel” teknik dökümanında belirtiliyor.
(9)    I2C2 veri bloğu aktif edilir.

I2C2 veri bloğunun kurulmasından sonra STM32F407’nin verilen adres ve veri parametrelerine göre I2C2 veri yolunu kullanarak Slave cihaza veri yazmasını inceleyelim.
Fonksiyon Prototipi
void I2C2_WriteChar (char Adr, char Dat);
Burada;
Adr = Slave cihaz adresi
Dat = Slave cihaza yazılacak olan 1 byte veri

Fonksiyon;
void I2C2_WriteChar (char Adr, char Dat) {
                I2C2 -> CR1 |= 0x0100;                   // Start bit gönderilir
                while (!(I2C2 -> SR1 & 0x0001)) { };   // Start bitinin gönderilmesini bekle
                I2C2 -> DR = 0xd0;                         // Slave adresini ve yazma bitini gönder.
                while (!(I2C2 -> SR1 & 0x0002)) { };   // Adresin gönderilmesini bekle
                int Status2 = I2C2 -> SR2;                // Flag temizlemek için SR2 register’ı oku
                I2C2 -> DR = Adr;                           // chip adresini ve yazma bitini gönder.
                while (!(I2C2 -> SR1 & 0x0080)) { };   // DR register’ın boşalmasını bekle
                I2C2 -> DR = Dat;                           // Gönderilecek veriyi DR register’a yaz
                while (!(I2C2 -> SR1 & 0x0080)) { };   // DR register’ın boşalmasını bekle
                while (!(I2C2 -> SR1 & 0x0200)) { };   // Byte’ın gönderilmesini bekle
                I2C2 -> CR1 |= 0x0200;                   // Stop biti gönder
}
  
I2C2 veri yolunu kullanarak Slave cihaza veri yazmasını inceleyelim.
Fonksiyon Prototipi
char I2C2_ReadChar (char Adr);
Burada;
Adr = Slave Cihazın adresidir.

Fonksiyon;
char I2C2_ReadChar (char Adr) {
                I2C2 -> CR1 |= 0x0100;                    // Start bit gönderilir
                while (!(I2C2 -> SR1 & 0x0001)) { };   // Start bitinin gönderilmesini bekle (SB = 1)
                I2C2 -> DR = 0xd0;                         // Slave adresini gönder. (LSB = 1)
               while (!(I2C2 -> SR1 & 0x0002)) { };    // Adresin gönderilmesini bekle (ADDR = 1)
               int Status2 = I2C2 -> SR2;                // Flag temizlemek için SR2 register’ı oku
               I2C2 -> DR = Adr;                           // chip adresini  gönder
               while (!(I2C2 -> SR1 & 0x0080)) { };   // DR register’ın boşalmasını bekle
               while (!(I2C2 -> SR1 & 0x0004)) { };   // Byte’ın gönderilmesini bekle

               I2C2 -> CR1 |= 0x0100;                   // Start bit gönderilir
               while (!(I2C2 -> SR1 & 0x0001)) { };  // Start bitinin gönderilmesini bekle (SB = 1)
               I2C2 -> DR = 0xd1;                        // Slave adresini gönder. (LSB = 0)
               while (!(I2C2 -> SR1 & 0x0002) { };  // Adresin gönderilmesini bekle (ADDR = 1)
               int Status4 = I2C2 -> SR2;             // Flag temizlemek için SR2 register’ı oku
               while (!(I2C2 -> SR1 & 0x0040)) { }; // byte alınana kadar bekle
               I2C2 -> CR1 |= 0x0200;                 // Stop biti gönder

               return ((char)I2C2 -> DR);            // okunan byte ile geri dön
}

Şimdilik bu kadar arkadaşlar Gülen
Sorularınız ve önerileriniz için m.hakki.kaplan@gmail.com adresinden her zaman bana ulaşabilirsiniz.
Hakkı KAPLAN


2 yorum: