Kategori arşivi: Referans

Function Pointers in C

Fonksiyon işaretçisi, bir fonksiyonu bildirim referansını kullanarak çağırabilmemizi sağlar. Yani, bir fonksiyonu parametrik olarak çalıştırma imkanımız olur.

void (* func_name) ( ) = function_name;

Bunun bendeki en sık kullanma amacı Fonksiyon Listesi hazırlamak:


 int funcA(void);
 int funcB(void);
 int funcC(void);
 int funcD(void);
 int funcE(void);
 int funcF(void);

 int (* functionList[]) () = 
 {
  funcA, funcB, funcC, funcD, funcE, funcF 
 };

 // bu bildirimin cümle içinde kullanımı şöyle:
 ret_val = functionList[list_index]();

Her ne kadar bir kaynak dosyası içinde kullanılacak bir fonksiyon için deklarasyon yazmak zorunda olmasak da, fonksiyon listesi hazırlarken deklarasyon yazmalıyız. Deklarasyon bize yürütme kısmından önce o fonksiyona dair işaretçiyi kullanma şansı verir. Bu şekilde liste bildirimi yaparken liste elemanlarını sabit olarak kullanabiliriz.

UDP Load Data

Bu soket görevi, belirtilen konumdan itibaren, belirtilen miktarda veriyi W5500 üstündeki ilgili soketin (socket-0) TX buffer’ına yazar.


 

W5500’deki bir soketin TX buffer’ına yazma işlemleri şöyle olmalı:

  1. Buffer’da yeterince yer olup olmadığını Sn_TX_FSR okuması yapıp öğrenmek.
  2. Sn_TX_RD okuması yapıp, yazma işlemine başlayacağımız konumu öğrenmek.
  3. Veriyi buradan itibaren TX buffer’a aktarmak.
  4. Sn_TX_WR içeriğini yazdığımız byte sayısı kadar artırmak.

W5500_TX_bufferSEND komutu verildiğinde W5500, Sn_TX_RD‘den Sn_TX_WR‘ye doğru TX buffer’ındaki verileri sırayla gönderir. İşlem tamamlandığında Sn_TX_RD = Sn_TX_WR olacaktır. O yüzden, yeni bir veri yüklemesi yaptıktan sonra, Sn_TX_WR içeriğini Sn_TX_RD’den gidecek veri miktarı kadar öteye alıyorum.

Bu işaretçilerin tümü 16 bitlik işaretsiz sayılardır. W5500 0xFFFF -> 0x0000 başa sarmasını önemsemiyor. Bu durumda biz de önemsemiyoruz. Paketimiz yüklenirken bu başa sarma olsa bile bu gönderme işlemini etkilemeyecek. Ki bu başa sarma da zaten eninde sonunda olacak..

Ben ayrıca, 1. adımda söz ettiğim Free Size Register’ını da okumuyorum. Çünkü benim uygulamalarda hem yollanan veri boyları küçük, hem de uygulamalarım veri yollama işlemini senkronize olarak kullanıyorlar.

Burada not etmek istediğim son şey, veri YÜKLEME işlemi ile GÖNDER komutunun farklı şeyler olduğuna dikkat etmeniz. Birden çok kez veri yüklemesi yapıp, hepsini tek seferde yollamak istemeniz çok muhtemel.. İşlemci üstünde integral bir çıkış paketi oluşturma zorunluluğunu ortadan kaldırmak için böyle yaptım. Yani, gidecek verinin tam gideceği paket yapısı ile bir yerde üretilmiş olmasına gerek yok. İstediğiniz datayı bir yerden, sonrakini bir başka yerden W5500’e yükleyip, asıl paketi TX Buffer üzerinde oluşturabilirsiniz. Toplamda 16kB TX buffer’ımız var ne de olsa!

W5500 Interrupt Logic

W5500’den durum sinyali almanın bir yolu çipin /EC_INT pinini okumaktır. Benim uygulama periyodik SPI okuması yerine bu pinin durumunu izleyerek soketlere dair durum güncellemelerinden haberdar olur.
Her kesme kaynağının bir de mask biti var. Mask biti 1 yapılmadıkça o kaynak bir sonraki kademeye kesme iletmez.
W5500’den PC’ye bağlantı veren /EC_INT girişinin etkin olmasını sağlayan iki kaynak var:
* Çip kesmeleri : IR register’ındaki bir bitin aktif olması ile oluşur.
Benim uygulamada çip kesmelerini kullanmıyorum.
* Soket kesmeleri: SIR register’ındaki bir bitin aktif olması ile oluşur. 8 soket kesmesi register’ına (Sn_IR) ait kesme kaynaklarından birinin aktif olması SIR üzerindeki o sokete ait bitin aktif olmasını sağlar.

w5500_interrupt_logic

Benim uygulama, bir soket açıldığında o sokete ait TIMEOUT, RECEIVE ve DISCON kesmelerini otomatik olarak yetkilendirir. Ayrıca, açılan soketin Sn_IMR maske bitini de etkinleştirir. Kullanıcının bu kesmelerin devreye alınması üzerinde bir denetimi yoktur.
Yani, EC_INT pininin aktif olması bize açık soketlerden birinde zaman aşımı, veri alma ya da bağlantının sonlandırıldığı bilgilerini verir.

W5500’ün kesme pininin izlenmesi işini W5500_SPI_Thread( ) halleder. Bu thread aynı zamanda, kesme algılar algılamaz SIR register içeriğini de okur.

W5500_SPI_Thread( ) kesme durumu algıladığında işlemesi gereken bir komut yoksa derhal SIR okuma işlemine atlar.
Bu işlem esnasında; hEC.status = 200 olur.
İşlem tamamlandığında; hEC.interrupt_flag = 255 yapılarak üst thread uyarılır.
hEC.socket_interrupt içeriğine de SIR değeri yüklenmiş olunur.

W5500 Embedded Ethernet

WizNet markasını ilk kez bir PLC’nin içini açtığımda görmüştüm. O zamandan aklımda kalmış. Bir süre sonra embedded ethernet gerektiren bir iş masama geldiğinde aklıma bu marka geldi ve küçük bir araştırmayla W5500 çipini kullanmaya karar verdim. Şimdiye dek üç projede bu çipi kullandım ve deneyimlerimi burada paylaşacağım.

W5500’den önce üzerinde MAC modülü olan işlemcilerle ethernet uygulaması da yapmıştım. Kişisel görüşüm, W5500 gibi bir harici tümleşik çözümle çalışmak daha esnek ve konforlu.

W5500 bir donanıma asgari düzeyde kodlama yükü ile ethernet bağlanabilirliği özelliği kazandırmak için çok uygun bir çip. Bunun avantajı, PHY çipi kullanmak zorunda olmamanız, işlemcinizde MAC olmuş olsa bile TCP ya da UDP protokolünün handler’larıyla CPU’nuzu meşgul etmekten kurtulmanızdır. Ayrıca, kişisel gözlemim, W5500 daha önce kullandığım PHY çiplerine göre daha az ısınan bir şey.

W5500 ürün sayfasında bu malzeme hakkında açıklamaları görebilirsiniz.

Donanım

W5500 Schematic
W5500 schematic
RJ45 Ethernet Jack
RJ45 Ethernet Jack

W5500’ün işlemci bağlantıları;
* 4 pin SPI : SCK, MISO, MOSI, CS
* Giriş: INT, Çıkış: RST

SPI Erişimi

W5500’e erişim SPI üzerinden oluyor. Elemanlar yukarıda linkini verdiğim ürün sayfasında 80MHz SPI clock destekliyoruz diyorlar ancak bir yerlerde 33MHz ile garanti çalışıyor gibi bir şeyler de yazdığını hatırlıyorum. Benim SPI hattı EEPROM çipiyle paylaşıldığı için ben yüksek hıza fazla odaklanmadım, 10MHz SPI clock ile yetindim. Genel olarak şöyle bir şey var: Daha yüksek SPI hızı, SPI buffer erişim fonksiyonlarınızın “blocking” çalışmasına olanak tanır ve işlemcinin toplam performansını iyileştirebilir. Ben bu uygulamada non-blocking spi erişimi kullanıyorum ve spi modülünü başka görevlerle müşterek kullanmaya da açık çalışıyorum. O yüzden yüksek spi hızını fazla dert etmiyorum. Sözü daha fazla uzatmadan W5500’e nasıl erişiliyor, onunla ilgili notlarıma link vereyim:

W5500 Erişimi İle İlgili Notlarım

Controller Configuration

Şu veri tipi, cihaz sıfırlandıktan sonra common register block’taki temel W5500 ayarlarının yapılması için gerekli bilgileri saklar:

typedef struct
{
    unsigned char       gateway_IP[4];      
    unsigned char       subnet_mask[4];
    unsigned char       MAC[6];
    unsigned char       device_IP[4];
    WORD                retry_period;   // Little-endian olarak yeniden deneme periyot süresi
} ECCONFIG;

w5500.c içindeki cEC değişkeni reset sonrası, çipin kullanacağı konfigürasyon değerlerini içeriyor olmalıdır:

ECCONFIG    cEC;       // ec configuration: (reset sonrası yüklenir)

NOT: retry_time hariç tüm değişkenler BIG ENDIAN olarak saklanıyor olmalı!

I/O Config. on PIC24

PIC24 işlemcilerinde her bir port için aşağıdaki register’lar tanımlanmış:

PORTx, LATx, TRISx, ODCx, ANSELx, CNPUx, CNPDx, CNENx

Bunlardan, PORT, LAT, TRIS zaten tüm PIC’lerde olan register’lar.. Bunlar hakkında bir şey dememiz gerekmiyor.
UART pinlerinden TX olacak olanın TRIS’ini 0 yapıyorum. (Tam hatırlamıyorum ama ya PIC18 ya da uğraştığım başka bir mcu’da uart pinlerini TX de olsa RX de olsa giriş olarak konfigüre ediyordun, bu öyle değil!)

Bir pin analog giriş olarak kullanılacaksa ilgili TRIS biti 1 olmalı, ANSEL biti de 1 olmalı (bu zaten reset sonrası varsayılan durumdur).
ANSELx.y = 0 yaptığımız zaman x portunun y. bitini digital I/O ya da uart gibi bir peripheral olarak kullanabilir oluruz.
Bu arada, ben kullanmadığım işlemci portlarını donanımı tasarlarken herhangi bir nete bağlamıyorum (en iyi ihtimal bir header konnektöre çıkar bırakırım) ve bu portları ÇIKIŞ yapıp LAT değerine de 0 yazarım.

Peripheral Remapping
Pin sayısı az, çevresel fonksiyonları çok işlemcilerde pinleri fonksiyonlara paylaştırmak için kesinlikle bir çeşit pin-peripheral multiplexing’e ihtiyaç var. Silabs işlemcilerdeki priority crossbar’a benzer iş yapan bir özellik PIC24’lerde de var ve Peripheral Pin Select fonksiyonu olarak adlandırılıyor.

Bu yapı iki kısımdan oluşuyor: Input Mapping, Output Mapping.
Eğer şu PIC24EP işlemcisini bir-iki projede daha kullanacak olursam port konfigürasyonu için kendim bir program hazırlayacağım. (Harmony Configurator diye bir şey zaten var ama bildiğim kadarıyla 24EP desteklemiyor, hem ben kendi kodlama tarzıma ve kullanımıma uygun bir şey yapacağım) Bunu burada paylaşırım. Çip konfigürasyonunu hızlandırmamıza yardımcı olur.

Input Mapping
Bu, peripheral temelli bir kontroldür. Yani her peripheral’in kendisine ait bir register alanı (7 bit) vardır. O alana, peripheral’i hangi pine route edeceğimizi söyleyen değeri yazarız. Bu durumda her bir giriş pini için de bir adres değeri tanımlanmıştır. Bu, datasheet’te tablo olarak verilmiştir.
Böyle olmasının mantığı açık: Bir giriş birden çok kaynağa bağlanabilir. Sonuçta hedefin (peripheral) bir tane olması yeterli.
Bazı remappable pinler sadece giriş olabilirken bazıları hem çıkış hem de giriş olabilirler. İsimlendirmeden bunu ayırt edersiniz. RPIx ya da RPy gibi..

Örnek olarak, ben UART1 RX fonksiyonunu RF0/RPI96 pinine atayacaksam gider tablodan RPI96 adres değerine bakarım: 0x60 gördüm.
Sonra da bu değeri U1RX’in adres tanım register’ına yazarım: RPINR18 = 0x0060

Özetle, giriş mapping’de kullanacağım pinin adres değerini bulurum, bunu kullanacağım fonksiyonun register’ına yazarım…

Output Mapping
Bu, giriş mantığının tam tersi ile çalışır. Yani her bir pinin kendisine ait bir register alanı vardır. O alana, pini hangi fonksiyona route edeceğimizi söyleyen değeri yazarız. Her bir fonksiyonun bir remap değeri vardır. Bu, tablo olarak verilmiştir. Bunun mantığı bence daha da açık: Bir çıkış ancak tek bir fonksiyona atanabilir. Hedefn (pin) bir tane olması gereklidir.

Benim UART1 ‘in TX pinini RF1/RP97’ye atayacağım. Tabloya bakıyorum: U1TX için peripheral değeri = 1
Tamam.. Sonra gidiyorum, RP97’yi ayarlayan register’ı buluyorum: RPOR7
RPOR7bits.RP97R = 1;

Konfigürasyon Kilidi
Bazı register’lara erişimin ancak özel bir kilit açma işlemi sonrası münkün olduğunu bilirsiniz (mesela on-chip EEPROM). Benzer şey bu peripheral mapping’de de var. Yukarıdaki register’lara erişebilmeniz için önce
OSCCONbits.IOLOCK = 0;
yapmak zorundayız. Öte yandan IOLOCK bitine yazabilmek için önce özel bir kilit açma yazması yapmak gerek.

XC16’da bunu yapan iki builtin fonksiyonu var. OSCCONL ve OSCCONH için built-in yazma:

__builtin_write_OSCCONL (OSCCON | 0x40) ; // IOLOCK, OSCCON’daki 6. bit.

İşler bitince kilidi yine devreye almak için:

__builtin_write_OSCCONL (OSCCON & 0xBF);

Bu, IOLOCK bitinin PIC24’lerin hepsinde olmadığını okudum. Emin olmak için kullandığınız çipin datasheet’indeki OSCCON register açıklamasına bakın..

Titreşim Analizi ile Hata Öngörüsü

İvme ölçerden aldığım ivme değişimi değerlerini bir mekanik sistemin “titreşimi” olarak kabul ediyorum. Çalışmamız çok basit bir iki kabule dayanıyor:
** İvme ölçerin ölçtüğü ivme sinyali, cihazın üzerine tam olarak sabitlendiği sistemin titreşimi ile bire bir bir sinyal üretir.
** Bu sinyalin bir (ya da iki) eksendeki ölçümlerinin belli bir frekans aralığındaki değerleri bana belli bir mekanik durum hakkında bilgi verir. Çünkü toplam titreşim sinyali sisteme etki eden hareket bileşenlerinin süperpozisyonudur. Dönen bir motoru sallanan bir salıncağa bindirirsem iki ivmelenmenin kompozisyonunu okurum.
** İvme ölçerin ölçtüğü genlik potansiyel olarak “istenmeyen” durumun şiddetini belirtirken frekans spektrumu durumun kimliği hakkında bilgi içeriri. Belli bir frekans aralığındaki bileşenler belli bir değerin üstüne çıktığında o mekanik sistemde “normal olmayan” ya da “bozulmuş” bir hareket olduğu sonucunu çıkarabilirim.
**Bu frekans aralığı ve hata için kabul edeceğim eşik her sistem için farklı olacaktır, o yüzden bunları ayarlanabilir yapmam gerekebilir.

Bu durumda, elimde iki şey olması gerekiyor:
1) Belli bir hızda sürekli olarak okuyabildiğim bir ivme ölçer…
2) Ondan aldığım sinyallerin frekans analizini yapabilecek bir hesaplayıcı

İşe PC üzerinde başlıyorum. Önce, bir sinyalin frekans bileşenlerini birbirlerine göre nasıl kıyaslarım sorusunu ele alıyorum. Bu, elbette Fourier analizi yapmam demek.

Elimdeki bir sinyal dizisinin FFT’sini almak için bir program hazırladım. Ama önce bu işlerin nasıl olduğuyla ilgili birkaç deneme yapmak istiyorum.

Deneme amaçlı olarak 3 bileşenli bir sinyal üretiyorum:

fx[i]:= DC + (A * sin(2*pi*i*f1/k) + (B * sin(2*pi*i*f2/k)) );

Buradaki k, benim örnekleme frekansım oluyor. Zaman domeninde sinyali çizdiğimde ekranın kaç saniyelik örnekleme içerdiğini bu oran belirliyor (çünkü ben sample’ları indeksleriyle sayabiliyorum, bağımsız değişkenim zaman değil).

Bu fonksiyonu 4000 noktalı olarak üretiyorum. (Gerçekte 16 bitlik tam sayılarla ve bundan daha uzun sinyallerle uğraşacağım).

f1 ve f2, sinyalin içerdiği iki sinüsoidalin frekansını gösteriyor.. DC, A ve B bileşenlerin genliklerini belirtiyor. DC bir offset her zaman karşılaşacağım bir durum çünkü sistemim yeryüzüne yakın bir noktada çalıştığı sürece mutlaka 1g’lik bir yerçekimi ivmesine maruz olacak..

Örnek değerler:

k = 400;
DC = 1;
A = 10;
B = 4;
f1 = 25;
f2 = 50;

Bu değerler için sinyal şöyle bir şey oluyor:

sine_synth_signal

Bu sinyalin 512 noktalı FFT’sini alırsak ve dikdörtgen (uniform) ağırlıklandırma penceresi uygularsak şöyle bir genlik spektrumu görüyoruz:

sine_synth_freq_spec

  1. bileşen DC component
  2. bileşen 25 Hz ‘e karşılık gelen sinüs.
  3. bileşen de 50 Hz olan sinüs..

Benim örnekleme frekansım 400Hz olduğu için, sinyalin spektrum genişliği 200Hz oluyor.
BW = fs / 2;

Spektrum çözünürlüğü de;
H = BW / (spect_size / 2)
oluyor.

Bant genişliğim 200Hz, spektrum çözünürlüğüm de 200/256 oluyor. Yani spektrum penceremdeki her bir çubuk 0,781Hz’lik bir harmoniği gösteriyor.

Yukarıdaki örnekte 200’ü tam sayıya bölen frekanslar seçmiştim. Şimdi öyle olmayan bileşenler içeren bir sinyal üretelim:

sine_synth_signal_2

sine_synth_freq_spec_2

Artık harmonikler, gerçekte bir tane saf sinüsten oluşsalar da burada birden çok komponent olarak gözüküyorlar. 
Sonrası FFT spektrum boyu, sinyal boyu ve pencereleme fonksiyonu ile ilgili bir tercih olacak.
Bizim için genliklerin doğru gösterilmesi frekansların doğru gösteriminden daha önemlidir. Şu anda dikdörtgen pencere fena gözükmüyor diyebiliriz.

Genel olarak ihtiyacımız olan şeyler şunlar;
Bant genişliği sınırlanmış sürekli bir sensör verisi kaydı.
Hatadan kaynaklanan frekans bileşenini biliyor olmamız.
Bu bileşenin değerine göre hata ya da sağlıklı durumuna karar verebilmemiz için güvenilir bir eşik değeri