nRF24L01+

Nordic Semiconductor her ne kadar son zamanlarda bluetooth’a odaklanmış gözükse de uzun yıllardır sub-ghz rf çiplerini kullandığımız bir marka.
İnsanlar artık “uysa da uymasa da” bir protokol seçip, onu çalıştıran stack’in yüklü olduğu işlemciyi barındıran bir SOC alıp, ilave bir-iki özelliğini kurcalayıp proje üretiyorlar. Özellikle farklı cihazlarla beraber çalışma gibi durumlar söz konusu olduğunda bu elbette doğru bir yaklaşım. Öte yandan, eğer bir bluetooth uzaktan kumandası kullanıyorsanız, bunun kitlesel olarak üretilmiş cihazlarda bile bazen ne kadar başarısız sonuçlar verdiğini de yaşayarak görmüşsünüzdür.

Tam çalışma şeklini, iletişim protokolünü, güç yönetimini ve tüm radyo parametrelerini kendimiz ayarlayarak, tam olarak uygulamanızın ihtiyacına göre kablosuz haberleşme gerçekleyebildiğimiz çözümler de var. Bunlar genelde üreticilerin Proprietary RF (uygulamaya özel protokol) başlığı altında ele aldığı çözümlerdir. Teorik olarak herhangi bir RF SOC’unu alıp, dökümantasyonla mücadele edip bu şekilde çalıştırmak mümkün. Ancak, anlaşılır sebeplerle, 1GHz altında çalışan modüller protokolü size bırakılmış, böyle uygulamalar için oluyor genelde. Bizim bu yazıda ele alacağımız 24L01+ bu tarzın 2,4GHz’deki temsilcisi olması sebebiyle de ilginç.

Aslında bu çipin uygulama devresinde çok az harici eleman var, bu yüzden bunu board’a eklemek zor bir iş değil. Ama hazır modüller o kadar ucuza bulunabiliyor ki çipi temin etmeye çalışmaya (ki muhtemelen modülü bulmaktan daha zor) buna değmez.

nRF24L01+ modüllerle ilgili, ilk gördüğümde dikkatimi çeken şey çok ucuz olmaları olmuştu. Eğer uygar ve gelişmiş bir ülkede yaşıyor olsak, para birimimizin bir kesiri ile belirtilen bir fiyat etiketiyle bu ürüne sahip olabilirdik. Muhtemelen bir bardak çay parasından ucuza. İçinde olduğumuz duruma rağmen, bu modüller hâlâ herhangi bir uygulamaya eklemek için çok da düşünmeyeceğiniz kadar ucuzlar. Öte yandan bu ucuzluğuna rağmen pek çok kablosuz habereşme işinde kullanmaya da uygun gibi gözüküyorlar. Yani, bunlara bir bakmaya değer.

Donanım

Gerekenler;
– 3 port SPI,
– iki çıkış : CE ve CS
– bir tane de giriş (isteğe bağlı) IRQ.

Elimde nRF24L01+ modülünü doğrudan takmak için bir board var. Bunu bir ana dinleyici olarak tasarladım. Görevi, sürekli dinlemek ve kendine yollanan verileri RS485 üzerinden aktarmak. Daha otonom çalışma için board’un üzerinde röle çıkışları da var.

Alıcısı olan şeyin bir de göndericisi olmalı elbette. Bu taraf, uygulamaya göre şekillenir. Çünkü biz kablosuz haberleşmeyi data-agnostik işlerde kullanmıyoruz. Elimizde, benim eskiden kalma alışkanlıklarla “saha” dediğim, bir şey ölçmek ya da kontrol etmek istediğimiz bir uzak noktada bir modül var. Radyo haberleşmesi bize bu uzak modül ile, daha merkezi bir kontrol noktası arasında iletişim kurmak için gerekiyor. Yani bizde radyo haberleşmesi, yollanacak veriden bağımsız bir iş değil. Bu yüzden bir alıcı pek çok farklı işte kullanılabilir ancak gönderici kapasitif bir sıvı sensörü de olabilir, radar kullanan bir mesafe sensörü de olabilir, bir sıcaklık sensörü de olabilir, bir kapıya takılmış güvenlik anahtarı da olabilir. Ve işi bu modül tanımlar.
Ben, gönderici tarafın çalışma şeklini denemek için daha önce, bambaşka bir proje için hazırladığım bir prototip board’unu kullandım. Bu kartı seçmemin sebebi boyutları değil, üzerinde bir header ile bağlantısı olan SPI portu olmasıdır.

Bu arada ben gündelik konuşmadaki anlamıyla “alıcı” ve “gönderici” tabirlerini kullanıyorum. Gerçekte, modüller hangi tarafta olurlarsa olsunlar hem alma hem de yollama radyo fonksiyonlarını yürütmek zorundadırlar. Bizim alıcı ve verici derkenki kastımız “data” (veri) yönünü ima etmiyor, “information” (bilgi) yönünü ima ediyor.

Şimdi kullanacağımız pinlerden bahsedelim:
SPI = SCK, MISO, MOSI, CSN
Bu, bildiğiniz SPI faz 0. CSN de 0 iken etkin SPI slave select çıkışıdır.
Ben 1MHz SPI clock ile modüllerle sorunsuz haberleşiyorum. Gönderici tarafta salkım saçak kablolarla bağlantı olduğu için çok hızlandırmayı düşünmedim.

CE = Chip enable çıkışı. Nordic bu tabiri çiplerine dışarıdan bir işlem başlatan girişlerini tanımlarken kullanıyor. Mesela veri yollama işlemini başlatmak ya da alıcıyı etkinleştirmek gibi.

IRQ = Gönderme ya da alma işlemlerinin tamamlanışını, eğer ilgili maske bitini ayarlanmışsanız bu pinin 1->0 geçişinden anlayabiliyorsunuz. O yüzden “kesme” ismi verilmiş. Donanımda pin kıtlığı varsa bunu bağlamak için bir giriş ayırmadan da çipi çalıştırabiliriz ama o zaman periyodik olarak SPI konuşması yapmamız gerekir.

Sürücünün Tasarımı

Sürücü ile kastettiğim, bizim kartlar üzerinde çalışan firmware’in radyo modülleri üzerinden iletişim kurmasını sağlayan fonksiyonlar ve değişkenler. Bunu doğru tasarlarsak donanımımız asıl işini yaptıktan sonra (bir sensörden veri okumak gibi) ileteceği veya alacağı veriyi UART’tan yollamaktan çok da farklı olmayan bir şekilde iletip çevrimini tamamlayacaktır.

Burada konu ettiğimiz radyo çipi gibi amatörlerin beğenisini kazanmış, bilindik ürünlerle ilgili internette firmware araştırmaları yaptığımda karşıma çok fazla boş içerik çıkıyor. Bunun en basit nedeni, adamın birinin (hatta bazen çipin üreticisinin) bir arduino kütüphanesi yazması. Konuyla ilgilenen çoğu kişi bu kütüphaneyi referans gösteriyor. “Nasıl kullanılacağını açıklama” amaçlı pek çok makale gidip bir arduino kütüphanesine dair nesneyi main.c içine koyup .begin demekten öteye geçmiyor.

Bu çip için geçerli değil ama daha dehşet verici olan ise şu: Pek çok durumda, yazılmış arduino kütüphanesinin “kendisi” de ürünün pek çok özelliğini kullanmayan, dökümantasyonla tam uyuşmayan hasbel kader çalışmış bir kod oluyor. Ürünün üstünde olan ve benim tam da onu seçme sebebim olan bazı yönlere dair hiçbir ayrıntı görmüyorum. Yani, internetten çalışan bir kod arama macerası genellikle konuya ilgi duyan ama ayrıntıları çözecek motivasyonu ya da zamanı olmayan kişileri yanlış yönlendirecek ya da asıl işe yarar bilgiden yalıtacak şekilde çalışıyor. Ben bir şeyleri çalıştırmaya çabalarken de, yaptığım bir şeyle ilgili bilgiler paylaşırken de bundan uzak durmaya özen gösteriyorum. Açık dökümantasyonu olan ve geliştirme araçlarına sahip olduğumuz her şeyi nasıl çalıştığını anlayıp, kendi ihtiyacımız için en optimum ayarlarla kullanabilmeliyiz. Aksi halde başkasının gelinliğiyle/damatlığıyla düğün yapmış oluruz. En sonunda ortaya çıkan sonuç da çoğu zaman “optimal” bir çözüm olmaz.

Benim hemen hemen her yeni işte karşılaştığım bu can sıkıcı duruma dair düşüncelerimi de paylaşmamın ardından nRF24L01+ ‘e nasıl erişeceğimiz konusuna dönebiliriz:
SPI’ı , ilk byte’ı bir komut olan master paketlerine yanıt vererek haberleşiyor. Okuma, yazma, register/fifo erişimi gibi tüm olasılıklar ilk byte’ta verilen komuta göre belirleniyor:

Bu da demek oluyor ki, çipe erişme işlerimiz daima bir komutu yazarak başlamalı:

static unsigned char NRF_Command(unsigned char cmd);

Komutlar nrf24l01.h içinde tanımlı bir byte’lık sabitlerdir. Datasheet’in 51. sayfasında listesi verilmiştir. Yukarıdaki fonksiyonda CSN=0 yapıp, verilen komut byte’ını yazıp bu esnada MISO’dan da STATUS içeriğini okuyup sonraki eylemi beklemeye koyuluruz.

nRF24L01+ ‘e yaptıracağımız işler şunlar:
– Çipi konfigüre etmek;
– Çipin çalışma durumunu (MODE) değiştirmek,
– STATUS’ü okumak
– Veri buffer’larına gidecek veriyi yazmak ya da onlardan gelen veriyi okumak.

Bu görevler kısa süren eşzamanlı işlemlerle halledildikleri için generic bir sürücü, gerektiğinde çağrılacak fonksiyonlardan ibaret olabilir.

KONFİGÜRASYON:

Power-up sonrası nRF24’ün lojik kısımlarının çalışmaya başlaması için 100ms geçmesi gerekir. Bu yüzden, program başlar başlamaz çipi konfigüre edeceksek önce bir 100ms beklemek zorundayız. Sonra, çipin POWER_DOWN mode’da olduğundan emin olabiliriz.

nRF24L01+’de çalışma öncesi yapılması gereken ayarlamalar şunlar:
Auto-Ack :
Firmware Sabiti.
Bu özelliği kullanıyorum, o yüzden hem alıcıda hem de göndericide pipe-0 için ack enable yapıyorum.
RX Address Enable:
Firmware sabiti.
Alıcıda pipe-0 ve pipe-1 yollayıcıda ise pipe-0 (ack’i almak için) etkin yapıyorum.
Address Width:
Firmware sabiti.
4 byte adresleme kullanıyorum.
Auto-Retransmission:
Firmware sabiti.
Alıcıda 0 ama göndericide sıfırdan farklı değerlere ayarlanmalı.
RF Channel:
Uygulama için parametrik.
7 bitlik radyo kanalı tanımı
RF Setup:
Uygulama için parametrik.
250kbps veri hızı kullanıyorum. Çıkış gücü ayarlanabilir olabilir.
Payload Width:
Firmware sabiti.
Sadece alıcı tarafta tanımlanması gerek. Yollayıcı tarafta TX payload olarak ne yüklersek radyo onu yolluyor.
P0 için RX Adresi:
Uygulama için parametrik
Alıcıda, bu ağı tanımlayan master adresi budur. Yollayıcıda burada alıcının adresi olmalı (ACK almak için böyle yapmak gerekiyor).
P1 için RX Adresi:
Uygulama için parametrik
Alıcıda, bu konfigürasyon çağrı adresi. Normal veri mesajlaşması dışındaki işlemler için rezerve tutuluyor. Yollayıcıda kullanılmayabilir veya konfigürasyon adresi olarak tanımlanabilr.
TX adresi:
Uygulama için parametrik.
Yollayıcıda bu ağın tanımlayıcısı olan master RX adresi (alıcının P0 adresi). Alıcı için ise tanımlanması gerekmiyor (konfigürasyon için adres gereksin dersek kullanılır).

Çip POWER-DOWN durumundayken konfigüre edilebilir. Ben de bunu tercih ediyorum. Çip yollama veya alma yapmadan önce STAND_BY-1 durumuna geçmiş olmalıdır. Bunu yaptırmak için CONFIG register’ına yazma işlemi yapan bir fonksiyon kullanıyorum:

void Radio_Stby(void)

CONFIG register’ında sürekli değiştirmemiz gereken bit PWR_UP biti (bit-1) olsa da burada CRC ayarlarını, TX/RX çalışmasını ve IRQ pinini sürecek kesme kaynaklarını belirleyen bitler de var. Her Stand_By’a geçişte bunları da doğru değerlere ayarlamak gerekiyor.

GÖNDERİCİ İÇİN İŞLEMLER:

Radyo STAND_BY -1 durumundayken;
– Yollanacak veriyi TX FIFO’ya yüklemek,
– CE = 1 yapmak, 15us beklemek ve CE=0 yapmak,
Yollama işlemini başlatır.

Bu işleri

void Radio_TX(unsigned char xdata *ptx)

isimli fonksiyonumda hallediyorum. Bu fonksiyon her çağrıldığında TX FIFO’ya belirtilen konumdaki paketi yükler ve sonra CE pulse’ını üretir. Burada TX adresini yeniden yüklemiyorum. Flash’tan yüklenen radyo ayarları bellek alanında master adresi tanımlanmıştır ve konfigürasyon aşamasının ilgili adımında bu adres TX adresi olarak yüklenir. Radyo üzerinden yeniden konfigürasyon işlemi için config çağrı adresinin yeninden yüklenmesi gerekecektir.

Auto-Retransmit fonksiyonunu kullandığımız için, belirtilen süre içinde ACK almazsa radyo tanımlanmış sayı kadar tekrar yollama denemesi yapacaktır:


TX için radyoyu STAND_BY-1 durumuna getirirken config register’ına ;

#define CONFIG_TX  0x4E

sabit değerini yazıyorum. Bu durumda nRF24L01+ ‘in IRQ pini veri yollandığında veya max. retransmit count deneme sayısına ulaşıldığında 0 olacak demektir. Ana programın IRQ pininin 1 -> 0 geçişini beklemesi gerekir.

Bu olduktan sonra

void Radio_Clear_IRQ(void)

fonksiyonu ile kesme nedeni okunup hemen sonra da flag’ler sıfırlanmış olunur. Yollayıcı program MAX_RT kesmesi gelmişse alıcının veriyi almadığı varsayımıyla uygun işlemi yapabilir. Tipik kullanımda ben her durumda veri yollaması sonrası donanımı düşük enerjili moda alıp işlemciyi de rtc kesmesi ile uyanacak şekilde kurup kapatıyorum. Gönderme işlemimiz MAX_RT kesmesi ile sonuçlanmışsa daha kısa, TX_DS kesmesi ile tamamlanmış ise de normalde düşündüğümüz uyku süremizi ayarlıyoruz.

Pil Seviyesi Ölçümü:

Kablosuz cihazlar genellikle pille çalıştıkları için, pil düzeyini ölçmek, bu programlarda çoğu zaman olmasını isteyeceğimiz bir özelliktir.
Pil düzeyini doğru ölçmenin basit bir yolu, pilden her seferinde belli bir akım geçerken onun gerilimini ölçmek olabilir. Kablosuz bir uç nokta cihazında nRF24, muhtemelen en çok akım akıtan parça olacağı için, nRF etkinken pil seviyesini ölçmek bize mantıklı bir okuma sonucu verecektir (kullandığımız işlemciler radyo modülüyle kıyaslanmayacak kadar az akımla çalışırlar).
İşte bu yüzden, radyo tam gönderme işleminin ortasındayken pil gerilimini okumak mantıklıdır.

TX işlemi sırasında radyo 7mA ile 11.3mA arasında değişen bir akım çeker (değer, çıkış gücüne bağlı olarak değişir).
Bu durumda, pil seviyesi ölçme işlemini
Radio_TX() fonksiyonu içinde,
CE pulse’ı ürettikten hemen sonra başlatabiliriz.
Yukarıda belirttiğim 710us Time on Air süresi, benim protokoldeki paket süresidir.

ALICI İÇİN İŞLEMLER

Bu tür düşük enerjili saha node’larının master-slave çalıştıkları ağlarda alıcı sürekli açık olsa daha uygun olur gibi gözüküyor. Yani, en basit başlangıçta, bize sürekli dinleme durumunda olan bir radyo gerekli. Bunun için öncelikle nRF24L01+ ‘i başlatıyorum ve ardından onu STAND_BY-1 durumuna sokuyorum. Sonra biraz bekleyip CE pinini 1 yapıyorum. Bu durumda radyo daha önce konfigürasyon aşamasında bahsettiğim üzere, pipe-0 ve pipe-1 için tanımladığımız adreslere gelecek paketleri beklemeye başlıyor. Ayrıca kesmeyi de DATA_READY flag set edildiğinde pini sürmek üzere

#define CONFIG_RX  0x3F      

konfigürasyon değeriyle ayarlamış oluyorum. Bu durumda bir veri geldiğinde IRQ = 0 olacağını varsayıp bekliyoruz.

IRQ=0 algılandığında hemen çipin STATUS register’ını okuyorum. Aslında bu durumda iken tek kesme kaynağı veri alınması olsa da hangi dinleme pipe’ına veri geldiğini ayırt etmemiz için status içeriğini biliyor olmamız gerek.

void Radio_Read_RXData(unsigned char xdata *dest)

fonksiyonu RX FIFO’nun geçerli içeriğini dest ile belirtilen ram konumuna aktarır. Status ile belirlediğimiz dinleyici kanalına uygun kayıt konumunu seçeriz. Bu kısım gelen veriyi işlemeyle ilgili..

Read_RXData() fonksiyonu ile FIFO’yu okuduktan sonra radyo üstündeki gelen veri buffer’ının boş olduğundan emin olmak için bir kez daha STATUS register’ını okuyorum. Buffer boş ise işlemler bitmiştir, dolu ise bu kez hangi pipe’tan veri almışsak onunla ilgili hedef konumuna fifo içeriğini okuyorum. Bu şekilde, birbirine yakın zamanlarda gelmiş olan iki paketi de atlamadan alacağımızı ümit ediyorum.

Yeri gelmişken yazayım: Bu, birden çok dinleme “pipe” ‘ı mevzusu alıcının toplam iş çıkarma kapasitesini arttırmıyor. Çünkü, radyo belli bir anda tek bir kanalda dinleme yapıyor ve sadece bir paketi alabiliyor. Birden çok RX adresi olması otomatik ACK üretme işinde ve bir seviyeye kadar gelen paketleri sınıflandırmada işe yarar.

FIFO boş olduktan sonra kesme bayrağını TX işleminde yaptığım gibi STATUS register’ına yazarak sıfırlıyorum ve alma işlemi bitiyor.

Test programında yapılan son iş gelen veriyi seri porttan yollamak.

Düşüncelerinizi yazın...

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.