Kablosuz bağlantı kullanan bir şeyler üstünde çalışıyorsanız ve daha da kötüsü donanım da size aitse işe basit haberleşme denemeleri yapmakla başlamak mantıklı. Burada, nRF9e5 sub-GHz SoC ‘u test etmek için basit bir uygulama paylaşıyorum.
Gerekenler: * Test edeceğiniz nRF9e5’li board (device under test), * Bunun üstündeki EEPROM’u programlamak için bir programlayıcı, * PCT, PC’ye bağlanan bir radyo terminali * PCT’yi izlemek için bir PC programı veya bir seri terminal programı, * Vereceğim firmware’ler
Bu programda, radyo cihazları rol değiştirmiyor. PCT daima dinlemede, test edeceğimiz 9e5 cihazı da sadece gönderme yapıyor. Program 8MHz kristal kullanıyor. Program 9e5’in P02 portunu (Pin:2) her göndermede kısa bir süreliğine açıp kapatıyor. Buraya LED bağlayabilirseniz TX anlarını gözlemleyebilirsiniz. Her 786,4ms’de bir veri gönderiliyor. Bunun zamanlamasını Timer1 yapıyor. Veri gönderilen kanal 866MHz bandında 218. kanal. Hedef adresi : E2.E2.E2.E2 Alıcının 8 byte payload beklemesi, bu ayarlarda olması ve 16 bit CRC mode’un açık olması yeterli. PCT bu ayarlarla yüklü biçimde dinleme modunda çalışmaya başlayacak ve bir radyo paketi aldığında bunu 62500bps hızında host PC’ye aktaracak. Veri paketinin yapısı :
byte index
veri içeriği
0
0x47
1
tx_counter[H]
2
tx_counter[L]
3
0x11
4
0x19
5
0x35
6
0x62
7
0x01
Aşağıda, bootloader yaması yapılmış nRF9e5 firmware hex dosyasını bulabilirsiniz. Bu program yüklenir yüklenmez çalışmaya başlar:
PC Terminal Radyosu için de aşağıdaki firmware’i kullanabilirsiniz. Bu program başlamak için bir komut istemeden, PC’ye bağlar bağlamaz çalışmaya başlayacaktır:
Benim PCT’lerden kullanmıyorsanız, EFM8SB1 kullandığınız sürece, yukarıdaki firmware’i kendi donanımlarınızda da çalıştırabilirsiniz. LED_U (P0.3) ve LED_R (P1.5) çıkışlarının kullanımına dikkat edin!
Alınan verileri gösteren bir PCT PC arayüzünü de burada paylaşacağım.
Girişlerin kontak durumlarını ya da butonları okumamız gereken durumlarda sık sık giriş düzeylerini filtrelememiz gerekir. Buna iyi bir örnek, push-buttonlara tepki veren programların tuş kontaklarının yarattığı sıçramalar yüzünden yanlış sayıda basmalar algılamasıdır. Bu sorun özellikle butonun anlık durumunun program içinde çeşitli yerlerde doğrudan sorgulandığı küçük uygulamalarda oldukça can sıkıcı olabilir.
Bu yazıda ilk olarak bir projemizde kullandığım basit bir tuş okuma arayüzünü anlatacağım. Sonra da tuş sayısının arttığı durumlar için etkin bir çözüm olarak kullanılabilecek vertical counter uygulamasının kodunu paylaşacağım. İlerleyen zamanda, kendi tasarımım olan bir klavyenin girişlerini nasıl okuduğum bilgisini de bu yazıya ekleyeceğim.
Projemizde üretimi pek de başarılı olmayan, ayrı bir tuş takımı levhası var. Bunun üzerindeki 10 adet buton işlemcimizin üstünde olduğu kontrol board’una giriyor. Programın çalışması sırasında belli anlarda belli butonlar okunmalı. Butonlardan bazıları basılı tutulduklarında düz basma işevinden başka işlevler yerine getiriyorlar.
Buton tarama işlerini farklı buton grupları için benzer işler yapan 4 farklı fonksiyon olarak yazdım. Bu uygulamada iki farklı buton grubu sanki birbirinden bağımsız iki arayüzmüş gibi çalışıyor.
Fonksiyonlardan her biri sorumlu olduğu butonlar için kabaca yukarıda gösterdiğim basit algoritmayı çalıştırıyor. Aşağıda bir buton okuması için çalışan lojiği (ve aynı zamanda kullanılan yerel değişkenleri) görüyorsunuz:
// A1: basma durumu sorgulama
if ( button_mask & 0x0001 )
{
if ( button_pressed.A1 == 0 )
{
if ( pcnt_A1 > BTN_PRESSCNT )
{
// TODO: burada birden çok seferde sıfırlanma eklentisi olabilir.
if ( btn_A1 ) pcnt_A1 = 0;
}
else
{
if ( btn_A1 ) pcnt_A1 = 0;
else
{
++pcnt_A1;
if ( pcnt_A1 > BTN_PRESSCNT ) button_pressed.A1 = 1;
}
}
}
/*
* kullanıcı programının pressed flag'i sıfırlaması sonrası butonu bırakmadan
* işlemin tekrarlanmasını istiyorsak burada
* if ( btn_A1 ) pcnt_A1 = 0; sıfırlamasını ekleyebiliriz.
*/
}
Burada tüm butonların taramasının tek bir fonksiyon içinde değil her grup için iki fonksiyonda yapıyorum. Çünkü benim uygulamada tuş takımındaki butonlardan ikisinin basılı tutma için ayrı işlevler yaptırması gerekiyor (button hold fonksiyonu). Buna konu butonların tarama işlerini ayrı bir fonksiyonda yazdım. Ayrıca, basılı tutma okuma mantığı yukarıdaki mantıktan biraz farklı:
Yukarıdaki algoritmanın yürütülmesi şu şekilde oluyor:
if ( button_mask & 0x0008 )
{
if ( btn_A4 ) // buton basılı değilse
{
if ( pcnt_A4 )
{
if ( pcnt_A4 > BTN_PRESSCNT ) button_pressed.A4 = 1;
pcnt_A4 = 0;
}
}
else if ( button_hold.AM == 0 )
{
++pcnt_AM;
if ( pcnt_AM > BTN_HOLDCNT )
{
button_hold.AM = 1;
pcnt_AM = 0;
}
}
}
Bu task fonksiyonlarını bir fonksiyon pointer dizisi olarak grupladım:
Ana programımda 5ms periyotlu bir sistem timer’ı var. Bu timer’ı taramaları zamanlamada kullanıyorum. Timer tick’i geldiğinde ana programdaki buton okuma indeks değişkenini 1 yapıyorum: button_read_index = 1;
Bunun sıfırdan farklı bir değere ayarlamak ButtonRead_Task[ ] fonksiyon dizisinin ilgili elemanının işaretlediği task’in o taramada çağrılmasını sağlıyor. Bu sıralama son task (4 indeksli olan) çağrılana kadar arka arkaya her çevrimde yapılıyor ve sonra thread kapanıyor (one-shot operation).
if ( button_read_index )
{
ButtonRead_Task[button_read_index]();
++button_read_index;
// one-shot bir task sıralaması olduğu için tüm task'ler taranınca işlemler biter:
if ( button_read_index > 4 ) button_read_index = 0;
}
Burada butonları sistem çevrimi içinde, programın en sonunda, başka her şeyden bağımsız olarak okuduğuma dikkat edin. Programım içinde herhangi bir yerde bana herhangi bir butonun basma durumu gerekirse artık butonun bağlı olduğu portun lojik değerine değil yukarıda gördüğünüz task’in çıktısına bakacağım. Ki bu da, programım dahilindeki tüm modüllerin erişimine açık olan; BUTTON_FLAGS button_pressed; değişkenidir. Buradaki BUTTON_FLAGS tip bildirimi, uygulamamızda butonlara verdiğimiz isimlerin tanımlandığı bir struct veri tipidir. Son bir not da buton okumasının yetkilendirilmesi ya da devre dışına alınması mekanizması hakkında yazayım: extern void Enable_Button( unsigned int button_index ); extern void Disable_Button( unsigned int button_index ); isminde iki fonksiyon tanımladım. Bunlar, macro olarak tanımladığım buton isimlerini parametre olarak alıp, o butonu taramaya açıyor ya da devre dışına alıyorlar. Bir butonu taramaya almak yukarıda lojiğini verdiğim koddaki ilgili button_mask bitini 1 ya da 0 yapmaktan ibaret. Ben bu değişkeni kendi ünitesi içinde static olarak saklıyorum. Ek olarak, o kanal devreye alınırken dahili sayacı ve durum flag’lerini de sıfırlıyorum.