Arduino – Haberleşme protokolleri – SPi – Senkron Haberleşme hakkında detaylara yer vereceğiz.
Daha önceki yazılarımızda UART ve i2C haberleşmelere değinmiştik.
SPI Nedir? Serial Peripheral Interface/ Seri Çevre Birimi Arayüzü (SPI), mikrodenetleyiciler, shift registerlar, sensörler ve SD kartlar gibi küçük çevre birimleri arasında veri göndermek için yaygın olarak kullanılan bir haberleşme veriyoludur. İletişime geçmek istediğiniz cihazı seçmek için bir seçim hattı ile birlikte ayrı saat ve veri hatları kullanır. Master ve çevresel cihazlara bağlanan MISO (Master In Slave Out), MOSI (Master Out Slave In) ve SCK (Serial Clock) olmak üzere üç adet SPI hattı bulunur.
SPI Tarihçesi
SPI (Serial Peripheral Interface) bir seri iletişim protokolüdür. SPI arabirimi Motorola tarafından 1970 yılında bulunmuştur. SPI, tam çift yönlü bağlantıya sahiptir, bu da verilerin aynı anda gönderilip alınması anlamına gelir. Yani bir master, bir slave’e veri gönderebilir ve bir slave, aynı anda master’a veri gönderebilir. SPI, senkron seri iletişimdir, iletişim amacıyla saatin gerekli olduğu anlamına gelir.
Seri iletişim, bir iletişim kanalı veya veri yolu üzerinden sırayla, her seferinde bir bit veri gönderme işlemidir. UART, CAN, USB, I2C ve SPI iletişimi gibi birçok seri iletişim türü vardır.
- MISO: Çevresel cihazlardan (slave) yollanan verilerin master cihaza aktarıldığı hattır.
- MOSI: Master cihazdan yollanan verilerin çevresel cihazlara aktrıldığı hattır.
- SCK: SPI haberleşmesinde senkronu sağlayan saat sinyalinin bulunduğu hattır. Saat sinyali master cihaz tarafından üretilir.
MISO ve MOSI hatlarından da anlaşıldığı gibi SPI protokolünde I2C’den farklı olarak veri hatları tek yönlüdür. Ayrıca çevresel cihazların (slave) adreslerinin olmasına gerek yoktur. Her çevresel cihazın seçim ayağı bulunur. Bu ayağa, SS (Slave Select) denir. Bu hattın sayısı kullanılan çevresel cihazların sayısı kadardır. Her cihaz için master cihazından ayrı SS hattı çıkar. SS hattı LOW (0 volt) düzeyinde olan çevresel cihaz, master cihaz ile iletişime başlar.
Örnek bir SPI haberleşme hattı aşağıdaki resimde gösterilmiştir. Resimde de görüleceği üzere, Master cihazdan çevresel cihaz sayısı kadar SS çıkışı bulunur. Master cihaz iletişime geçmek istediği çevresel cihazın SS pinini LOW (0 Volt) düzeyine çeker.
SPI pinleri Arduino türüne göre değişiklik gösterir. Arduino türlerine göre bu pinleri aşağıdaki tabloda gösterilmiştir.
Arduino türü | MOSI | MISO | SCK | SS (slave) | SS (master) |
Arduino UNO | 11 veya ICSP4 | 12 veya ICSP1 | 13 veya ICSP3 | 10 | – |
Arduino Mega | 51 veya ICSP4 | 50 veya ICSP1 | 52 veya ICSP3 | 53 | – |
Arduino Leonardo | ICSP-4 | ICSP-1 | ICSP-3 | – | – |
Arduino Due | ICSP-4 | ICSP-1 | ICSP-3 | – | – |
SPI fonksiyonları
SPI protokolünü öğrendiğimize göre şimdi haberleşmenin Arduino kısmına bakalım. Arduino’da SPI fonksiyonlarını kullanabilmemiz için öncelikle “SPI.h” kütüphanesini projemize eklememiz gerekir. Kütüphane projeye eklendiğinde aşağıdaki fonksiyonlar kullanılabilir.
- SPI.begin(): SPI haberleşmesini başlatır ve SPI pinlerini başlangıç konumlarına alır.
- SPI.setClockDivider(): Bu fonksiyon ile SPI haberleşmesinin saati ayarlanabilir. Fonksiyon değer olarak saat değişkenlerini almaktadır. Eğer hiçbir değişiklik yapılmazsa SPI saati “SPI_CLOCK_DIV4” olarak çalışır. Fonksiyonun alabileceği değişkenler; SPI_CLOCK_DIV4, SPI_CLOCK_DIV8, SPI_CLOCK_DIV16, SPI_CLOCK_DIV32, SPI_CLOCK_DIV64, SPI_CLOCK_DIV128’dir.
- SPI.transfer(): SPI hattına veri yollamak veya veri almak için bu fonksiyon kullanılır.
Verinin Alınması
Kendi kendinize, bunun tek yönlü iletişim için kulağa harika geldiğini düşünüyor olabilirsiniz, ancak verileri ters yönde nasıl geri gönderirsiniz? Burada işler biraz daha karmaşık hale geliyor.
SPI’de, yalnızca bir taraf saat sinyalini üretir (genellikle Seri Saat için CLK veya SCK olarak adlandırılır). Saati oluşturan tarafa “kontrolör/controller”, diğer tarafa “periferik/peripheral/çevre birimi” denir. Her zaman yalnızca bir denetleyici vardır (neredeyse her zaman mikro denetleyicinizdir), ancak birden fazla çevre birimi olabilir.
Denetleyiciden bir çevre birimine veri gönderildiğinde, “Kontrolör Çıkışı / Çevre Birimi Girişi ya da Controller Out / Peripheral In” için COPI adlı bir veri hattına gönderilir. Çevre biriminin denetleyiciye bir yanıt göndermesi gerekiyorsa, denetleyici önceden ayarlanmış sayıda saat döngüsü oluşturmaya devam edecek ve çevre birimi verileri “Kontrolör Girişi / Çevre Birimi Çıkışı ya da Controller In / Peripheral Out” için CIPO adlı üçüncü bir veri hattına koyacaktır.
Dikkat edin, yukarıdaki açıklamada “önceden düzenlenmiş” dedik. Denetleyici her zaman saat sinyali ürettiği için, bir çevre biriminin ne zaman veri döndürmesi gerektiğini ve ne kadar veri döndürüleceğini önceden bilmesi gerekir. Bu, herhangi bir zamanda her iki yönde de rastgele miktarda verinin gönderilebildiği asenkron seriden çok farklıdır. Pratikte bu bir sorun değildir, çünkü SPI genellikle çok özel bir komut yapısına sahip sensörlerle konuşmak için kullanılır. Örneğin, bir cihaza “veri oku” komutunu gönderirseniz, cihazın size her zaman örneğin iki bayt göndereceğini bilirsiniz. (Değişken miktarda veri döndürmek isteyebileceğiniz durumlarda, verilerin uzunluğunu belirterek her zaman bir veya iki bayt döndürebilir ve ardından denetleyicinin tam miktarı almasını sağlayabilirsiniz.)
SPI’nin “tam çift yönlü” olduğunu (ayrı gönderme ve alma hatlarına sahip olduğunu) ve bu nedenle, belirli durumlarda aynı anda veri gönderip alabileceğinizi unutmayın (örneğin, verileri alıcıdan alırken yeni bir sensör okuması talep etmek). Cihazınızın veri sayfası bunun mümkün olup olmadığını size söyleyecektir.
Chip Select (CS) Eski Adıyla Slave Select(SS)
Dikkat etmeniz gereken önemli bir yer daha var, Chip Select (CS). Bu, çevre birimine uyanması ve veri alması/göndermesi gerektiğini söyler ve ayrıca konuşmak istediğiniz çevre birimini seçmek için birden fazla çevre birimi mevcut olduğunda kullanılır.
CS hattı normalde yüksek tutulur, bu da çevre birimini SPI veri yolundan ayırır. (Bu mantık türü “etkin düşük” olarak bilinir ve genellikle hatları etkinleştirmek ve sıfırlamak için kullanıldığını göreceksiniz.) Veriler çevre birimine gönderilmeden hemen önce, hat alçaltılır, bu da çevre birimini etkinleştirir. Çevre birimini kullanmayı bitirdiğinizde, hat tekrar yükselir. Bir shift register’da bu, alınan verileri çıkış hatlarına aktaran “latch/mandal” girişine karşılık gelir.
Çoklu Çevre Birimi
Birden fazla çevre birimini bir SPI veri yoluna bağlamanın iki yolu vardır:
Genel olarak, her çevre biriminin ayrı bir CS hattına ihtiyacı olacaktır. Belirli bir çevre birimiyle konuşmak için, o çevre biriminin CS hattını düşük yapacak ve geri kalanını yüksek tutacaksınız (aynı anda iki çevre biriminin etkinleştirilmesini istemezsiniz, aksi takdirde ikisi de aynı CIPO hattında konuşmaya çalışabilir, sonuç olarak bozuk veriler elde edilir). Çok sayıda çevre birimi, çok sayıda CS hattı gerektirecektir; çıkışlarınız azalıyorsa, CS çıkışlarınızı çoğaltabilen ikili kod çözücü entegreler vardır.
Öte yandan, bazı parçalar, birinin CIPO’su (çıktı) diğerinin COPI’sine (girişine) giderken, birbirine zincirleme bağlanmayı tercih eder. Bu durumda, tek bir CS hattı tüm çevre birimlerine gider. Tüm veriler gönderildiğinde, CS hattı yükseltilir ve bu da tüm enteglerin aynı anda etkinleştirilmesine neden olur. Bu genellikle zincirleme shift register ve adreslenebilir LED sürücüleri için kullanılır.
Bu düzen için, bir çevre biriminden diğerine veri taşar, bu nedenle herhangi bir çevre birimine veri göndermek için hepsine ulaşmak için yeterli veri iletmeniz gerektiğini unutmayın. Ayrıca, ilettiğiniz ilk veri parçasının son çevre biriminde sona ereceğini unutmayın.
Bu tür yerleşim, tipik olarak, herhangi bir veriyi geri almanız gerekmeyen LED sürme gibi yalnızca çıktı durumlarında kullanılır. Bu durumlarda kontrolörün CIPO hattının bağlantısı kesilmiş halde bırakabilirsiniz. Bununla birlikte, verilerin denetleyiciye döndürülmesi gerekiyorsa, bunu zincirleme döngüyü kapatarak yapabilirsiniz (yukarıdaki şemada mavi kablo). Bunu yaparsanız, çevre birimi 1’den gelen dönüş verilerinin denetleyiciye geri dönmeden önce tüm çevre birimlerinden geçmesi gerekeceğini unutmayın, bu nedenle ihtiyacınız olan verileri almak için yeterli sayıda alma komutu gönderdiğinizden emin olun.
İki Arduino Arasında SPI Haberleşme
Kısa mesafeli maksimum 1-5m aralığında uzaklıklar için düşük veri transferi hızlarında kullanılabilir.
SPI Hattı | Arduino UNO |
COPI(MOSI) | 11 ya da ICSP-4 |
CIPO(MISO) | 12 ya da ICSP-1 |
SCK | 13 ya da ICSP-3 |
CS(SS) | 10 |
Bağlantı Şeması
Şimdi iki Arduino UNO kartını birbirine bağlayacağız; biri ana kontrolcü, diğeri çevre birimi olacak.
(CS/SS) : pin 10
(COPI/MOSI): pin 11
(CIPO/MISO) : pin 12
(SCK) : pin 13
GND ortak olmalıdır.
Her iki geliştirme kartı arasındaki bağlantının şematik gösterimi aşağıdadır:
Kod ve Fonksiyon Açıklamaları
SPI.h kütüphanesi projeye dahil edilince gelen fonksiyonların açıklamalarına göz atalım:
- SPI.begin(): SCK, MOSI ve SS’yi çıkışlara ayarlayarak, SCK ve MOSI’yi düşüğe ve SS’yi yükseğe çekerek SPI veri yolunu başlatır.
- SPI.setClockDivider(bölücü): SPI saat bölücüyü sistem saatine göre ayarlamak için. AVR tabanlı kartlarda, mevcut bölücüler 2, 4, 8, 16, 32, 64 veya 128’dir. Varsayılan ayar, SPI saatini sistem saatinin frekansının dörtte birine ayarlayan SPI_CLOCK_DIV4’tür. (20MHz’de çalışan kartlarda 5MHz olur.)
- bölücü: (SPI_CLOCK_DIV2, SPI_CLOCK_DIV4, SPI_CLOCK_DIV8, SPI_CLOCK_DIV16, SPI_CLOCK_DIV32, SPI_CLOCK_DIV64, SPI_CLOCK_DIV128) olabilir.
- SPI.transfer(val): SPI aktarımı, eşzamanlı gönderme ve alma işlemine dayalıdır: alınan veriler receivedVal’de döndürülür.
- SPI.beginTransaction(SPISettings(speedMaximum, dataOrder, dataMode)): speedMaximum saattir, dataOrder(MSBFIRST veya LSBFIRST), dataMode(SPI_MODE0, SPI_MODE1, SPI_MODE2 veya SPI_MODE3) olabilir.
SPI’de aşağıdaki gibi dört çalışma modu vardır:
- Mode 0 (varsayılan): Saat normalde düşüktür (CPOL = 0) ve veriler düşükten yükseğe geçişte (ön uç) örneklenir (CPHA = 0).
- Mode 1: Saat normalde düşüktür (CPOL = 0) ve veriler yüksekten düşüğe geçişte (arka kenar) örneklenir (CPHA = 1).
- Mode 2: Saat normalde yüksektir (CPOL = 1) ve veriler yüksekten düşüğe geçişte (ön uç) örneklenir (CPHA = 0).
- Mode 3: Saat normalde yüksektir (CPOL = 1) ve veriler düşükten yükseğe geçişte (arka kenar) örneklenir (CPHA = 1).
- SPI.attachInterrupt(handler): Bir bağımlı cihaz ana cihazdan veri aldığında çağrılacak fonksiyon.
Kontrolcü Kodu(Master)
//Görsel İşitsel Teknoloji Kanalı //wwww.teknikerler.com //Kerim Arı Youtube kanalı SPi Kodları - kullanımı örnek Kodları #include <SPI.h> void setup (void) { Serial.begin(115200); //usart için baud hızını 115200 olarak ayarla digitalWrite(SS, HIGH); // Slave Select'i devre dışı bırak SPI.begin (); SPI.setClockDivider(SPI_CLOCK_DIV8);//saat hızını 8'e böl } void loop (void) { char c; digitalWrite(SS, LOW); // Slave Select'i aktifleştir // string yolla for (const char * p = "görsel işitsel teknoloji\r" ; c = *p; p++) { SPI.transfer (c); Serial.print(c); } digitalWrite(SS, HIGH); // Slave Select'i devre dışı bırak delay(2000); }
Çevre Birimi Kodu(Slave)
//Görsel İşitsel Teknoloji Kanalı //wwww.teknikerler.com //Kerim Arı Youtube kanalı SPi Kodları - kullanımı örnek Kodları #include <SPI.h> char buff [50]; volatile byte indx; volatile boolean process; void setup (void) { Serial.begin (115200); pinMode(MISO, OUTPUT); // kontrolcüye göndermek zorunda, bu yüzden output olarak ayarlandı SPCR |= _BV(SPE); // çevre biriminde(slave) SPI'yi aç indx = 0; // boş buffer process = false; SPI.attachInterrupt(); // kesmeyi(interrupt) aç } ISR (SPI_STC_vect) // SPI kesme rutini { byte c = SPDR; // SPI Data Register'dan bayt oku if (indx < sizeof buff) { buff [indx++] = c; // dizi buff'ında bir sonraki dizine verileri kaydet if (c == '\r') //kelimenin sonunu kontrol et process = true; } } void loop (void) { if (process) { process = false; //işlemi sıfırla Serial.println (buff); //diziyi seri monitörde yazdır indx= 0; //buffer sıfırla } }