Merhaba,
RF ile ilgili öğrenci projeleri, mikro işlemcili kart tasarımları yapılır. irtibat için m.hakki.kaplan@gmail.com adresinden ulaşabilirsiniz.
5 Eylül 2016 Pazartesi
28 Haziran 2016 Salı
SPARK GAPS TEKNİĞİ
Spark Gap metodu elektronik kartlarda bulunan konnektör
girişlerinden gelebilecek yüksek voltajlı ESD’yi önleyen bir PCB tekniğidir.
Yüksek ESD voltajı oluşturulan boşluklardan Ground’a sıçrayarak devrede bulunan
girişler korunmuş olur.
Formulasyon
V= (3000.p.d)+1350
Burada p atmosfer basıncı, d ise iki gap arasındaki uzaklıktır(milimetre)
Bazı uygulanmış PCB örnekleri;
1 Aralık 2015 Salı
STM32F407’de PID Denetleyicisi
Uygulaması
PID (Proportional Integral Differential – Oransal İntegral
Diferansiyel) denetleyicisi düzenlemede temel yapı taşıdır. Bu birçok
farklı şekilde uygulanabilir. Bu örnekte mikrodenetleyicide nasıl kod yazılır
ve yeteneklerin basit bir gösterimi yapılmıştır.
İyi karıştırılırmış
bir su kabı düşünün (sistem) ki bu sistemin çevre sıcaklığının üstünde bir
sıcaklıkta kalması gerekmektedir (referans değer “R” olsun). Bizim yapmamız
gereken su içerisine bir termometre (sensor) koymak ve bununla sıcaklığı
okumaktır ( anlık değer “X” olsun). Su çok soğuk ise kabın altına
yerleştirilmiş olan ısıtıcı (aktuator) çalıştırılsın. Termometre okunarak
termometre değeri istenilen değere geldiğinde ısıtıcı kapatılsın. Suyun
sıcaklığı hala biraz zaman (aşmayı) için yükselir ve daha sonra azalmaya
başlar. Suyun sıcaklığı istenilen değerin altına düştüğünde, biz ısıtıcıyı
tekrar açalım. Isıtıcı ısınır ve suyu ısıtmaya başlar ama suyun sıcaklığı yine
istenilen değere ulaştığında işlem tekrarlanır. Biz bir düzenleme sistemine
sahibiz, burada denetleyicimiz; gerçek değeri gözlemler referans değer ile
karşılaştırırız ve karşılaştırmanın sonucunda sistemi etkileriz. Bu durum basit
bir şekilde figür 1’de gösterilmiştir.
Figür 1. Düzenlemenin ham ve basitleştirilmiş hali
Yukarıdaki
örnekte su sıcaklığı hiçbir zaman istenen değerde kalmaz, ancak bunun yerine
çevresinde salınımlar oluşur. Salınımlar sistemin “F”,
algılayıcı ve ısıtıcı özelliklerine bağlıdır. Sıcaklık davranışını iyileştirmek
ve salınımı azaltmak için denetleyicide daha karmaşık kararlar tanımlayarak
düzenleme sürecini geliştirebiliriz. Örneğin aşma miktarını biliyorsak
sıcaklığı istenilen değere gelmeden önce durdurabiliriz. Biz gerçek sıcaklığı istenilen sıcaklığa
yakın olduğunda suyu ısıtan sıcaklık miktarını azaltarak da bu bahsi geçen
aşmayı azaltabiliriz. Başka olasılıklarda vardır. Ama bunlar kontrol ünitesinde
ki tanımlamalar ile hayata geçirilebilirler. Bu duruma PID
düzenleme adı verilir.
Yukarıdaki
örnek açısından düzenleme teorisinin bileşenleri ikinci dereceden diferansiyel
eşitlikle açıklanabilir ve düzenlenmiş sistem ikinci dereceden olarak
isimlendirilir. Bunlar PID denetleyici tarafından en iyi şekilde
evcilleştirilmiştir.
Figür 2. PID denetleyici ile kontrol edilen sistem
PID denetleyicisinde ilk istenilen değer ve anlık
gerçek diğer karşılaştırılarak aralarındaki fark hesaplanır. Hesaplanan hata
sinyali oransal (Proportional),integral (Integral) ve farksal bölüme (Differential)
ayrılır. Bu üç bileşen ilgili faktörler (Kp, Kd, Ki)
ile toplanarak final değer elde edilir. Bu final değer (Reg) ısıtıcı tarafından kullanılan değerdir.
Böyle
bir PID denetleyicisi mikrodenetleyicide
periyodik olarak uygulanmalıdır. Yukarıdaki eylemlerin yapılma zamanı
düzenlenmiş sistemin cevap zamanını karşılamalıdır. Yani eylem zamanı yeteri
kadar kısa olmalıdır. Periyodik örnekleme, hesaplama ve değerin üretilmesi için
bu tekrar çağırılmalıdır. Aynı programlama çatısı daha önceki yazılarımızda FIR
ve IIR filtrelemede kullanılmıştır. Mikrodenetleyici kurulumu aynıdır. Tüm
denetleyici hesaplama fonksiyonları kesme fonksiyonu içerisindedir. Programın
listesi figür 3’de verilmiştir.
Program
değişkenlerin tanımlamaları ile başlar. İki integer tipte dairesel tampon
istenen ve gerçek değer için kullanılmıştır.
Ek olarak iki floating tipte işaretçi dairesel tampon (floating point
circular buffers) hata ve bir önceki çıkış değeri için kullanılmıştır. Bileşenler
için üç değişken ve ilgili ağırlık faktörleri için üç floating point sayı ve
kurulum tanımlanmıştır. Sonuç olarak değişim oranı hesaplaması için gerekli bir
değişken tanımlanmış ve kurulumu yapılmıştır.
Ana
program içerisinde ADC ve DAC kurulumları, örnekleme frekansı 10kHz olan her
100us ADC çevrimini başlatan timer
programlanmıştır. NVIC kesme denetleyici ADC kesme isteğini aktif hale getirmiştir. Daha sonra
mikrodenetleyici sonsuz döngüye girer. PID düzenleyicisi
program yürütülürken oransal (Proportional),
integral (integral) ve diferansiyel (Differential) bileşenler için ağırlıkların değişimine
izin verilir. Durum butonu periyodik olarak kontrol edilir, zaman gecikmesi
sonsuz döngü başında döngü tarafından tanımlanmaktadır. Sonraki 3 (üç) program
satırı oransal bileşen (Proportional component)
ağırlık değerini değiştirmek için kullanılır. Buton S370( port E’ bit 0’a bağlı olan buton)
buton S375 (port E bit 5’e bağlı olan buton)
ile birlikte basıldığında, o zaman ağırlık bir artırılır. Buton S370
buton S374 birlikte basıldığında ise (port E, bit 4, 0x10) daha sonra ağırlık
bir azaltılır. Üçüncü satır 0 ile 1000 arasında değerler (dâhil) ile orantılı
ağırlık değerini sınırlar. Sonraki üç satır diferansiyel bileşen için ağırlıklarla
aynı işi yapar ve sonraki üç satır integral bileşen için aynı işi yapar. Sonuç
olarak bu üç ağırlık LCD ekrana yazdırılır.
#include "stm32f4xx.h"
#include "LCD2x16.c"
int Ref[64], x[64], Ptr; // dairesel tamponların tanımlanması
int Error[64], Reg[64]; // hata ve önceki çıkış vektörlerinin
tanımlanması
float Prop, Dif, Int = 0; // üç bileşenin tanımlamaları
float Kp = 1.0, Ki = 0.0, Kd =
0.0; // kurulum parametrelerinin
tanımlamaları
float Ts = 0.0001; // TIM2’nin 8400 sabit değeri için 10kHz’e ayarlanması
int main ()
{
GPIO_setup();
// GPIO kurulumu
DAC_setup();
// DAC kurulumu
ADC_setup();
// ADC kurulumu
Timer2_setup();
// Timer 2 kurulumu
NVIC_EnableIRQ(ADC_IRQn);
// NVIC ADC kesme isteğinin
aktif edilmesi
LCD_init();
LCD_string("Kp:",
0x00); LCD_string("Kd:", 0x09); LCD_string("Ki:", 0x49);
// genlik ve zaman gecikmesinin ayarlanması – belirsiz döngü
while (1)
{
for (int i = 0; i < 2000000; i++) {}; // zaman gecikmesi
if ((GPIOE->IDR & 0x003f) == (0x01 + 0x20)) Kp++; // Kp’nin manuel ayarlanması
if ((GPIOE->IDR & 0x003f) == (0x01 + 0x10)) Kp--;
if (Kp<0) Kp = 0; if (Kp > 1000) Kp = 1000;
if ((GPIOE->IDR & 0x003f) == (0x02 + 0x20)) Kd +=
0.001; // Kd’nin manuel
ayarlanması
if ((GPIOE->IDR & 0x003f) == (0x02 + 0x10)) Kd -=
0.001;
if (Kd < 0) Kd = 0; if (Kd > 1) Kd = 1;
if ((GPIOE->IDR & 0x003f) == (0x04 + 0x20)) Ki +=
0.0001; // Ki’nin manuel
ayarlanması
if ((GPIOE->IDR & 0x003f) == (0x04 + 0x10)) Ki -=
0.0001;
if (Ki < 0) Ki = 0; if (Ki > 1) Ki = 1;
LCD_sInt3DG((int)Kp,0x03,1); // Kp’yi LCD ekrana yaz
LCD_sInt3DG((int)(Kd*1000),0x0c,1); // Kd’yi LCD ekrana yaz
LCD_sInt3DG((int)(Ki*10000),0x4c,1); // Ki’yi LCD ekrana yaz
};
}
// kesme fonksiyonu
void
ADC_IRQHandler(void) // yaklaşık
6us sürer
{
GPIOE->ODR |= 0x0100; // PE08 high yap
Ref[Ptr] = ADC1->DR; // ADC -> dairesel tampon x1
x[Ptr] = ADC2->DR; // ADC -> dairesel tampon x2
// PID hesaplamayı başlat
Error[Ptr] = Ref[Ptr] - x[Ptr]; // hatayı hesapla
Prop = Kp * (float)Error[Ptr]; // oransal kısım (Proportional)
Dif = Kd * (float)(Error[Ptr] - Error[(Ptr-1) & 63]) /
Ts; // diferansiyel kısım
Int += Ki * (float)Error[Ptr]; // integral kısmı
Reg[Ptr] = (int)(Prop + Dif + Int); // üçünü topla
// PID hesaplamasını durdur
if (Reg[Ptr] > 4095) DAC->DHR12R1 = 4095; // DAC çıkış limiti
else if (Reg[Ptr] < 0) DAC->DHR12R1 = 0;
else DAC->DHR12R1 = (int)Reg[Ptr]; // düzenleyici çıkışı-> DAC
DAC->DHR12R2 = Error[Ptr] + 2048; // hata -> DAC
Ptr = (Ptr + 1) & 63; // dairesel tampon işaretçisini artır
GPIOE->ODR &= ~0x0100; // PE08 low yap
}
Figür 3. PID denetleyici uygulaması
program listesi
Tüm
hesaplamalar kesme fonksiyonu içerisinde gerçekleşir. İki ADC’nin çevrim sonuçları önce dairesel tampona
saklanır. Bu iki değerin arasındaki fark hesaplanır. Sonra acilen üçüncü
dairesel tampona hata olarak saklanır. Sonraki üç satırda üç bileşen ve ağırlık
hesaplanır. Dördüncü satırda diğer bileşenler eklenir ve dördüncü dairesel
tampon olan “Reg” içerisine toplanarak saklanır.
Bu değer düzenleyici çıkışı olacaktır ve DAC’a
gönderilecektir. Fakat DAC 0 ile 4095 arasındaki
değerleri kabul edebilir, bu durumda diğer sayılarda bu aralık içerisine
katılır; örneğin 4097 ile DAC’da 1 sayısı
aynıdır ve bu düzenleyicide önemli hataya neden olur. Sayılar kabul edilebilir
değerler için en iyi şekilde sınırlandırılır ve bu sonraki üç kod satırında
yapılır. İkinci DAC hata sinyalinin analog
sürümünü üretmek için kullanılır ve değer hata değerine 2048 ekleyerek DAC aralığının yarısı için kaydırılır. Sonunda
dairesel tampon işaretçisi güncellenir.
Figür 4. Uygulama için bağlantı
Kesme
fonksiyonu içerisinde başlangıçta ve bitişte port e bit-0 high ve low yapılarak
fonksiyon içerisindeki harcanan zaman ölçülür. Bu yaklaşık olarak 6us’dir.
Program
DAC çıkışı (Reg, DAC1) ve ADC girişi
(gerçek değer, ADC2-> DR, ADC3 giriş)
arasında basit ikinci derece sistemini ekleyerek test edilebilir. Diğer ADC (ADC->DR, ADC2 girişi)
istenilen değerin okunması için kullanılır. İki seri bağlanmış RC devreleri
ikinci dereceden sistem için bir yedek olarak kullanılabilir; Bu gösterimi
kolaylaştırır. Ek olarak istenilen değer olan “Ref”
ve parazit sinyali olan “Intf” bir fonksiyon
jeneratörü kullanılarak üretilebilir. Komple örnek devrenin bağlantıları figür
4’de verilmiştir.
Figür
5’den 8’e figür 4’deki devre için gerçek değerler (X,
kırmızı), fark değerleri Kp, Kd ve Ki için
verilmiştir. Parazit sinyali “Intf” sıfır olarak
tutulur ve istenilen değer (Ref mavi) 10Hz
frekansında kare dalgadır. “Ref” sinyalinin
ofseti ADC göstergesinin ortasına yakın ayarlanır. Yatay gösterge çizelgesi
saniyeleri, dikey gösterge çizelgesi volt değerini verir.
Figür 5. Kp=1, Kd=0,Ki=0; oransal kazanç çok düşük ve gerçek
değer(kırmızı) istenilen değere ulaşmıyor.
Figür 6. Kp=50, Kd=0,Ki=0; oransal kazanç yüksek, gerçek değer(kırmızı)
istenilen değere(mavi) yakın, fakat salınımlar görünür halde
Figür 7. Kp=50, Kd=40, Ki=0; diferansiyel kazanç salınımları düzeltir
fakat gerçek değer(kırmızı) hala istenilen değer(mavi) ile aynı değil.
Figür 8. Kp=50, Kd=40, Ki=40; integral kazancı istenilen değerin
ortalamasını gerçek değerin ortalamasına doğru iter
Sonraki
diyagramlar figür 9’dan figür 12’ye kadar olan diyagramlarda düzenlenmiş sistem
cevapları (kırmızı) parazit sinyali (mavi) olarak verilmiştir. Referans sinyali
(gösterilmemiştir) 1.21V değerinde sabit tutulmuştur. Bu düzenleyici işini
düzgün yaparsa yanıtın sabit olduğu tahmin edilmiştir.
Figür 9. Kp=1, Kd=0, Ki=0; oransal kazanç çok düşük ve girişim yapan
sinyalin etkisi önemli
Figür 10. Kp=50, Kd=0, Ki=0; oransal kazanç yüksek, gerçek değer
(kırmızı) 1.21V’ta yakın lakin girişim sinyalinin neden olduğu salınımlar
görünür halde
Figür 11. Kp=50, Kd=40, Ki=0; diferansiyel kazanç salınımı düzeltmiş
lakin gerçek değer(kırmızı) istenilen değer (1.21V) ile aynı değil
Figür 12. Kp=50, Kd=40, Ki=40; integral kazanç istenilen değer (1.21V)
ile aynı olması için gerçek değerin ortalamasını alır.
Şimdilik bu kadar arkadaşlar 
Sorularınız ve önerileriniz için m.hakki.kaplan@gmail.com adresinden her zaman bana ulaşabilirsiniz.
Hakkı KAPLAN
26 Kasım 2015 Perşembe
On-line IIR Filtreleme ve STM32F407
IIR filtreleme, mikrodenetleyici uygulaması olarak
kullanıldığında FIR filtrelemeye oldukça
benzerdir. IIR (Infinite Impulse Response) filtreleme
ile ilgili bir uygulama örneği verilecektir.
Matematiksel olarak IIR Filtreleme şu şekilde ifade edilir;
Burada katsayılar (ağırlıklar) “am” ve “bn”, “x” giriş örnekleri ve “y”
filtre sonucunu gösterir. Katsayılar FIR katsayıları
için kullanılan Fourier dönüşümün tersinden daha
karışık bir algoritma kullanılarak belirlenir. Genel olarak “M” , “N”e eşit
değildir.
Bir
filtre çıkışının hesaplaması için gereken yol figür 1’de gösterilmiştir. 2 adet
dairesel tampon (circular buffer), biri giriş örnekleri için “x” olarak ve diğeri çıkış sonucu içindir “y” olarak isimlendirilmiştir. Yeni sonuç “yk” iki konvolüsyon beraber eklenerek oluşturulmuştur.
Üstteki konvolüsyon giriş örnekleri olan “x”i ve
katsayıları temsil eden “a”yı, alttaki konvolüsyon sonuç olarak belirtilen “y”yi ve katsayıları temsil eden “b”yi içerir. Yeni sonuçlar “yk”
filtreleme sonrası DAC’a gönderilir.
Figür 1. IIR filtrelemenin grafiksel
olarak gösterimi
Mikrodenetleyicide
uygulanan filtreleme FIR filtreleme
uygulamasının temel halidir. Program Figür 2.de listelenmiştir. Özelliklerin
kurulum fonksiyonları FIR filtrelemeyi
anlattığımız önceki yazılarımızdaki ile aynıdır. Bu sebepten dolayı burada
tekrar anlatılmayacaktır.
Kesme
fonksiyonunda da kullanılacak olması sebebi ile genel (global) olarak
tanımlanmış 2 (iki) dairesel tampon bulunur. Giriş örnekleri için olan “x1” tamponu integer tipte tanımlanmıştır buna rağmen
sonuç çıkış tamponu “y1” float tipte
tanımlanmıştır. Hassasiyet şunun için gereklidir; IIR integer
sonuçların kullanımdan kaynaklı sayısal hatalar, performansı düşük filtreleme,
sayısal istikrarsızlıklar ve osilasyonlara (salınımlara) yol açabilir. Çıkış
tamponu da integer olabilir, ama katsayılar FIR filtreleme
örneğindeki olduğu gibi yukarı ölçekli olmalıdır.
2 (iki)
sinyalinde de eş zamanlı filtrelemesine izin vermek için 2 (iki) parça dairesel
tampon bulunur. Tampon uzunlukları biraz abartılmıştır.
Neyse ki bizim
için, katsayılar tabloda verilmiştir. Bu örnek programda kullanmak amacı ile katsayı
değerleri referanstan kopyalanmıştır[1]. 4ncü dereceden “Chebishew” filtre karakteristikleri uygulanmıştır. Örnekleme
frekansının köşe frekansı 0.025 ve bant geçiren kısmın ripple oranı %0.5 olarak
kullanılmıştır. Katsayılar; Figür 1. IIR filtrelemenin grafiksel
olarak gösterimi
Mikrodenetleyicide
uygulanan filtreleme FIR filtreleme
uygulamasının temel halidir. Program Figür 2.de listelenmiştir. Özelliklerin
kurulum fonksiyonları FIR filtrelemeyi
anlattığımız önceki yazılarımızdaki ile aynıdır. Bu sebepten dolayı burada
tekrar anlatılmayacaktır.
Kesme
fonksiyonunda da kullanılacak olması sebebi ile genel (global) olarak
tanımlanmış 2 (iki) dairesel tampon bulunur. Giriş örnekleri için olan “x1” tamponu integer tipte tanımlanmıştır buna rağmen
sonuç çıkış tamponu “y1” float tipte
tanımlanmıştır. Hassasiyet şunun için gereklidir; IIR integer
sonuçların kullanımdan kaynaklı sayısal hatalar, performansı düşük filtreleme,
sayısal istikrarsızlıklar ve osilasyonlara (salınımlara) yol açabilir. Çıkış
tamponu da integer olabilir, ama katsayılar FIR filtreleme
örneğindeki olduğu gibi yukarı ölçekli olmalıdır.
2 (iki)
sinyalinde de eş zamanlı filtrelemesine izin vermek için 2 (iki) parça dairesel
tampon bulunur. Tampon uzunlukları biraz abartılmıştır.
Neyse ki bizim
için, katsayılar tabloda verilmiştir. Bu örnek programda kullanmak amacı ile katsayı
değerleri referanstan kopyalanmıştır[1]. 4ncü dereceden “Chebishew” filtre karakteristikleri uygulanmıştır. Örnekleme
frekansının köşe frekansı 0.025 ve bant geçiren kısmın ripple oranı %0.5 olarak
kullanılmıştır. Katsayılar;
Bu değerler,
programın başında tanımlama bölümü içinde kodlanmıştır.
ADC ve DAC’ın ana
program içerisinde kurulumları yapılmıştır. Periyodik örnekleme ve sinyal
üretimi için Timer kullanılmıştır. Kesme denetleyicisi
NVIC, ADC’den
gelen kesme isteği için aktif hale getirilmiştir. Daha sonra mikrodenetleyici
sonsuz döngü içerisinde zaman harcayarak çalışmasına devam etmektedir(bu
döngüde periyodik olarak butonları ve LED’leri kontrol eder).
#include "stm32f4xx.h"
int x1[4096], x2[4096], xyPtr; // giriş için dairesel tamponların
tanımlanması
float y1[4096], y2[4096]; // çıkış için dairesel tamponların tanımlanması
// IIR katsayılarının
kurulumu: 4ncü derece Chebishew Alçak geçiren [1]
// 0.5%, 250Hz’de -3
dB örnekleme frekansı
float a[5] = {1.504626e-5,
6.018503e-5, 9.027754e-5, 6.018503e-5, 1.504626e-5};
float b[5] = {0 , 3.725385e0 ,
-5.226004e0 , 3.270902e0 , -7.705239e-1};
int main ()
{
GPIO_setup();
// GPIO kurulumu
DAC_setup();
// DAC kurulumu
ADC_setup();
// ADC kurulumu
Timer2_setup();
// Timer 2 kurulumu
NVIC_EnableIRQ(ADC_IRQn);
// ADC kesmesi için NVIC aktif
edilir
// zaman gecikmesi
while (1)
{
//LED yak
if (GPIOE->IDR & 0x0001) GPIOA->ODR |= 0x0040;
// değil ise LED söndür
else GPIOA->ODR &= ~0x0040;
};
}
// kesme fonksiyonu
void
ADC_IRQHandler(void) // bu kısım
yaklaşık 6uS sürer
{
GPIOE->ODR |= 0x0100; // PE08 high yap
x1[xyPtr] = ADC1->DR; // ADC -> dairesel tampon x1
x2[xyPtr] = ADC2->DR; // ADC -> dairesel tampon x2
float conv = 0; // toplama işlemi için tanımlama
for (int i = 0; i < 5; i++) // 0’dan 4’e kadar olan katsayılar için
conv += a[i] * x1[(xyPtr-i) & 4095]; // konvolüsyon girişleri
for (int i = 1; i < 5; i++) // 1’den 4’e kadar olan katsayılar için
conv += b[i] * y1[(xyPtr-i) & 4095]; // konvolüsyon çıkışları
y1[xyPtr] = conv; // filtre sonucu saklanır
y2[xyPtr] = (float)x1[xyPtr]; // orijinal olan saklanır
DAC->DHR12R1 = (int)y1[xyPtr]; // filtrelenmiş değer -> DAC
DAC->DHR12R2 = (int)y2[xyPtr]; // orijinal değer -> DAC
xyPtr = (xyPtr + 1) & 4095; //dairesel tampon işaretçisini bir artır
GPIOE->ODR &= ~0x0100; // PE08 low yap
}
Figür 3. IIR filtreleme uygulamasının
program kodları
Önemli
işlemler kesme fonksiyonu içerisinde yapılır. Burada iki ADC’nin sonuçları önce dairesel tampona saklanır. Ardından
bir giriş sinyali için iki konvolüsyon hesaplaması yapılır. Konvolüsyonun birinde
mevcut ve geçmiş giriş örnekleri ve “am” katsayıları ve diğer konvolüsyon
geçmiş filtreleme sonuçları ve “bn” katsayıları ile hesaplanır. Tamamlanan
hesaplamada gerekli hassasiyeti sağlamak için kayan nokta aritmetiği kullanılarak
yapılır. Çıkan sonuç dairesel tamponda saklanır. Filtre edilmemiş giriş sinyali
karşılaştırma yapmak için başka bir çıkış tamponuna saklanır. Son olarak çıkış
tamponlarındaki mevcut değerler DAC’a gönderilir ve dairesel tampon işaretçisi
güncellenir.
Tüm
hesaplamalar basit olarak port E’de bulunan pin’in kesme fonksiyonu
başlangıcında high yapılması ve hesaplama bitiminde yani fonksiyon çıkışında
low yapılması ile geçen süre hesaplanır. Bu süre bu uygulamamızda yaklaşık 6uS
civarındadır.
[1] Steven W.Smith: The Scientist and Engineer’s Guide to
Digital Signal Processing
Şimdilik bu kadar arkadaşlar 
Sorularınız ve önerileriniz için m.hakki.kaplan@gmail.com adresinden
her zaman bana ulaşabilirsiniz.
Hakkı KAPLAN
25 Kasım 2015 Çarşamba
On-Line FIR filtre ve STM32F407
Figür 1’de
gösterilen bir ADC, bir işlemci ve bir DAC bulunan uygulamada gerçek zamanlı olarak analog
sinyallerin dijital filtrelemesidir. Bu yazımızda bir FIR
filtre örneği verilecektir.
Figür 1. FIR filtrelemeye dâhil olan bloklar
FIR (Finite Impulse Response) filtreleme giriş
sinyalinin önceden belirlenmiş katsayılar ile konvolüsyon (convolution)
kullanılarak yapılır. Konvolüsyon formülü aşağıda verilmiştir.
“hm” katsayısı filtrenin
özelliklerini tanımlar ve istenilen filtrenin istenilen frekans
karakteristiğinin Fourier dönüşümünün tersinin hesaplanmasıdır. “hm” katsayıları “-M”den “+M”e (teoride “M” sonsuza yakındır), “m” indeksi negatif ve
pozitif için bu metot kullanılarak hesaplanmıştır. Efektif olarak bunun anlamı;
“k” zamanında filtre cevabının hesaplanması, “k” zamanındaki “x”
örneklemesinin bilinmesi daha önceki örneklemeler (xk-1,
xk-2, … , xk-m) ve gelecek örneklemeler ile hesaplanır. Biz uygulamada
konvolüsyon formülünü direkt olarak kullanmayacağız fakat formülü değiştirerek
veya yeniden düzenleyerek bir gecikme ile hesaplayacağız.
Bu durum grafiksel olarak figür
2’de gösterilmiştir. Örnek girişleri “x” çizimde
kutu başına bir dairesel tampon (circular buffer) içerisinde saklanır. “k-1M”den “k” ya olan
geçmiş indeksli örnekler konvolüsyon hesabı için kullanılır. Filtreden anlık
çıkış olarak kullanılan “yk” sonucu aslında filtrelenmiş
“x” giriş sinyalinin gecikme eklenmiş sonucudur.
Gereksinim duyulan bu gecikme konvolüsyon hesaplamasında kullanılan katsayı
sayısına bağlıdır.
Figür 2. FIR filtrelemede kullanılan
örneklemeler
“hm” katsayıları alçak geçiren (low pass) FIR filtre için aşağıdaki şekilde hesaplanır;
Burada “fs” örnekleme frekansı ve “fc”
filtrenin köşe frekansıdır. Filtrenin katsayılarının fazla olması filtrenin keskinliği
için en iyisidir. Ancak, konvolüsyon formülündeki birçok çarpma işleminde
kullanılan katsayıların sayısının fazla olmasıdır bu durumda “m” seçilirken dikkatli olunmalıdır.
Köşe
frekansları üzerindeki frekanslarda zayıflama düşük olabilir fakat “M” indeksine yakın katsayı değerlerinin azaltılması
ile geliştirilebilir bu süreç “windowing”
olarak da bilinir. Ortak pencere fonksiyonu yükseltilmiş kosinüs fonksiyonudur.
Bu “von Hann window” olarak
isimlendirilmiştir.
FIR filtreleme gerçek zamanlı olarak
uygulanabilir. Giriş sinyali düzenli aralıklarda örneklenmiş olmalıdır. Bu
mikrodenetleyicinin bünyesinde bulunan bir timer ile elde edilebilir. Timer’ı
başlatarak örneklemeyi başlatabilirsiniz ve daha önceki yazımızda anlattığımız
“dairesel tampon” kullanarak alınan örnekleme değerleri “x” dairesel tamponunda saklanabilir. Kayıt edilmiş
olan örnekleme değerleri yeni bir örneklemeden sonra konvolüsyon hesaplaması
ile hemen hesaplanacaktır. Konvolüsyon sonucu bir DAC kullanılarak geri analog
sinyale çevrilir veya “y” dairesel tampon
içerisine kayıt edilir. Önemli olan nokta mevcut olan giriş sinyalinin yeni
örneklemesinden sonra acil bir şekilde konvolüsyon hesaplamasının yapılmasıdır.
Bu işlem kesme fonksiyonu içerisinde yapılır. Bir sonraki kesme isteğinden önce
hesaplama bitirilmek zorundadır. Her çarpımda harcanan zamandan sonra
konvolüsyon formülündeki katsayıların sayısı bunu limitleyecektir.
Konvolüsyon
formülünde kullanılan katsayı değerleri filtreleme boyunca aynı kalmalı ve
filtreleme öncesinde bir defaya mahsus hesaplanması gerekmektedir.
Figür
3’de bahsedilen program listelenmiştir. Programda ilk olarak 2 (iki) dairesel
tampon “x1” ,“x2”
ve “xPtr isimli işaretleyici tanımlanmıştır.
Bu değişkenler diğer kesme fonksiyonlarında da kullanılacağından dolayı genel
(global) olarak tanımlanmak zorundadır. Dairesel tampon uzunlukları abartılıdır
fakat bu mikrodenetleyici için uzun tamponlar problem teşkil etmez. Tamponlar
32-bit integer olarak tanımlanmıştır. Bu durumda ADC’lerden
gelen sonuçlar 16-bit olduğu için RAM tarafında tasarruf sağlanmış olacaktır.
Daha sonra “w” ile isimlendirilen katsayı
dizini tanımlanır ve veri tipi olarak “foat (kayan
noktalı)” değer atanır. Katsayılar yukarıdaki formülü takiben az bir
değere sahip olacağından dolayı floating yani kayan noktalı sayı olmak
zorundadır. Bu dizin uzunluğunda abartılı olarak verilmiştir.
Programın
“main” kısmında ilk olarak özelliklerin kurulumu gerçekleştirilir. “main”
kısmında ADC ve DAC
kurulumlarının fonksiyonları işlemin ana çatısının daha iyi
anlaşılabilmesi için gizlenmiştir. Daha önceki ADC ve
DAC ile ilgili yazılarımızda bu fonksiyonlar
verilmişti. ADC 100uS zaman aralıklarında
çevrime başlaması için timer bu çalışma şekline göre kurulumu yapılmıştır ve ADC’nin kesme istekleri aktif edilmiştir.
Ana program
konvolüsyonun kullandığı katsayı değerlerini hesaplayarak devam eder.
Yukarıdaki gördüğümüz formüldeki katsayı değerleri 0 (sıfır) indeksi ile
merkezi katsayısı etrafında simetriktir, buna rağmen sadece pozitif “m” indeksleri için katsayıları hesaplamaya
ihtiyacımız vardır ve sonraki 3(üç) program satırında bu işlem yapılmıştır.
Pozitif indeksler ve indeks 0(sıfır) için bir merkez için 63 adet katsayı
vardır.
Mikrodenetleyici
filtreleme işlemini başlatmaya hazırdır. Program zaman gecikmesi ile beraber
sonsuz döngü içerisinde devam eder.
#include "stm32f4xx.h"
#include "math.h"
#define pi 3.14159
int x1[4096], x2[4096], xPtr; // dairesel tamponların tanımlamaları
float w[1024]; // FIR ağırlıklarının tanımlaması
int main ()
{
GPIO_setup();
// pin kurulumlarının yapılması
DAC_setup();
// DAC kurulumunun yapılması
ADC_setup();
// ADC kurulumunun yapılması
Timer2_setup();
// Timer 2 kurulumunun yapılması
NVIC_EnableIRQ(ADC_IRQn);
// NVIC ADC kesmesinin aktif
edilmesi
w[0]
= 2.0 * 100.0 / 10000.0;
for
(short k = 1; k < 64; k++) //
FIR ağırlıkları
w[k]
= (w[0] * (sin(pi * k * w[0])) / (pi * k * w[0]));
for
(short k = 1; k < 64; k++) //
hanning window oluşturuluyor
w[k]
= (w[k] * cos(pi/2 * k / 62.0));
// zaman gecikmesi
while
(1)
{
if
(GPIOE->IDR & 0x0001) GPIOA->ODR |= 0x0040; // LED yak
else
GPIOA->ODR &= ~0x0040; //
değil ise LED söndür
};
}
// kesme fonksiyonu
void ADC_IRQHandler(void) // geçiş yaklaşık 42us’dir
{
GPIOE->ODR
|= 0x0100; // PE08 high yap
x1[xPtr]
= ADC1->DR; // ADC1 sonucunu
x1 dairesel tamponuna at
x2[xPtr]
= ADC2->DR; // ADC2 sonucunu
x2 dairesel tamponuna at
float
conv = (float)x1[(xPtr - 100) & 4095] * w[0]; // merkezi ağırlığı al
for
(int k = 1; k < 64; k++)
conv
+= w[k] * (x1[(xPtr - 100 + k) & 4095] + x1[(xPtr - 100 - k) & 4095]);
DAC->DHR12R1
= (int)conv; //sonucu DAC’a at
DAC->DHR12R2
= x1[(xPtr - 100) & 4095]; //
orijinal değeri DAC’a at
xPtr
= (xPtr + 1) & 4095; //
dairesel tampon işaretçisini artır.
GPIOE->ODR
&= ~0x0100; // PE08 low yap
}
Figür 3. FIR filtreleme uygulaması
program kodları
Önemli
işlemler kesme fonksiyonunda işletilir. ADC’den
yeni bir örnekleme geldiğinde kesme fonksiyonu çağırılır. Her iki ADC’nin sonuçları “xPtr”
işaretçisi kullanılarak dairesel tamponlarda saklanır. Sonra float değişken
olan “w[0]” merkezi ağırlığı içerisinde ve
önceki merkezi örnek “x1[(xPtr-100)&4095]”
buraya saklanır. Burada biz anlık örneklemeden değerini 100 olarak vermeyi
konvolüsyon uzunluğunu kapsamaya yeterli olduğunu varsayıyoruz. Bu değer “M” değerinde büyük olmak zorundadır.
Konvolüsyonun
geri kalanı 63 dahil “for” döngüsü
içerisinde uygulanmaktadır. Katsayı değerleri merkezi katsayı etrafında
simetriktir ve katsayının aynı değeri ile giriş örneklerinin çarpım bölümlerini
oluşturmak için zaman gecikmesi gerekmektedir. İlk iki giriş örneklemesini
eklemek ve sonra katsayı ile toplayıp çarpmak en iyisidir. Simetriyi vurgulamak
ve AND fonksiyonu dairesel tamponu içerisinde
işaretçiyi tutmak için “[xPtr-100+k]” ve “[xPtr-100-k]” girdi örnekleri olarak endekslenir.
İlk
konvolüsyon hesaplanır, sonuç integer değere çevrilir ve DAC çevrimine gönderilir. Filtrelenmemiş orijinal
sinyal ikinci DAC çevrimine karşılaştırma
amaçlı olarak gönderilir. Bu sinyal filtrelenmiş olan sinyallin gecikmiş
haliyle eşit olmak zorundadır. Konvolüsyonun kullandığı merkezi örnek DAC’a gönderilir.
İki GPIOE fonksiyonu port E üzerinde bir darbe sinyali
oluşturmak için kullanılır. Darbe sinyali kesme fonksiyonunun işletilmesinde ne
kadar zaman geçtiğini ölçmek için kullanılmaktadır (bu uygulamada 42uS).
Yürütme hızı
integer tipte değişkenlerin ve integer matematik işlemlerinin kullanılmasıyla artırılabilir.
Sadece figür 3’te gösterilen birkaç program satırını değiştirmek gerekebilir ve
bunlar figür 4’de gösterilmiştir. Katsayılar için tanımlanmış olan dizi “int” olarak değiştirildi. Bundan önceki durumda
katsayı değerleri 1 (bir) den küçüktü ve bu değerleri direkt olarak integer’a
çevrilemezler. Fakat bu değerler 65536 ile çarpılabilir ve integer olarak
saklanır (katsayı değerini 16-bit sola kaydırmak eşdeğerdir). Bundan sonra
integer tipte katsayılara sahibiz ve integer tip giriş örneklerine tüm
hesaplamalarda integer kullanabiliriz.
Beklendiği gibi bu 65536 kez daha büyük olan konvolüsyon sonucunu üretir
ve 65536’ya bölünerek (16-bit sağa kaydırma ile) doğru sonuca ulaşılır.
int w[1024]; // FIR
ağırlıklarının tanımlanması
// ana program içerisine
float w0 = 2.0 * 100.0 / 10000.0;
w[0] = (int)(w0 * 65536);
for (short k = 1; k < 64; k++) // FIR ağırlıkları
w[k] = (int)(w0 *
65536 * (sin(pi * k * w0)) / (pi * k * w0));
for (short k = 1; k < 64; k++) // Hanning window
w[k] =
(int)((float)w[k] * cos(pi/2 * k / 63.0));
// kesme fonksiyonu içerisine
int conv = x1[(xPtr - 100) &
4095] * w[0]; // merkezi ağarlık al
for (int k = 1; k < 64; k++) //
zaman gecikmesi
conv += w[k] *
(x1[(xPtr - 100 + k) & 4095] + x1[(xPtr - 100 - k) & 4095]);
DAC->DHR12R1 = conv
>> 16; // sonuç -> DAC
Figür 4. Integer filtreleme
Katsayıların
hesaplanması ana program içerisinde değiştirildi. İlk olarak merkezi katsayı
değeri “float” olarak hesaplandı fakat hemen
integer tipteki değere çevrildi ve katsayı dizininin 0 (sıfır)ncı pozisyonuna
saklandı. Sonra 1’den 63’e kadar indekslenen katsayı değerleri hesaplandı tümü
65536 ile çarpıldı. Hesaplanan katsayıları windowing
yapmak amacıyla kayan nokta sayıları tekrar çevrilir, ağırlık ile
çarpılır ve tekrar integer’a çevrilir.
Kesme fonksiyonu
içerisinde ara değişken olan “conv” integer
olarak tanımlanır ve hesaplama integer aritmetik belirtilmeden düzenli bir
şekilde yapılır. Tün değerler integer olduğunda otomatik olarak bitmiş olur. Tek
fark konvolüsyon sonucunun DAC’a gönderilmesi
noktasındadır. O ilk 65536 değerine bölünür (16-bit sağa kaydırılır) ve
ardından DAC’a yazılır.
Hızdaki önemli
gelişme; kesme fonksiyonunun integer aritmetik kullanmasıyla birlikte sadece
10uS’dir.
FIR filtrelemede gerçekleştirilebilecek ilginç bir
fonksiyon, 90 derece için bilinmeyen bir frekans ile bir sinyalin
kaydırılmasıdır. Bu fonksiyon “Hilbert dönüşümü” olarak isimlendirilir. Hilbert dönüşümü için katsayılar şu formül ile
hesaplanır.
FIR filtrelemenin integer sürümü için aynı program
kullanılabilir fakat katsayılar yukarıdaki formül ile hesaplanmalıdır. Bu durum
figür 5’de gösterilmiştir. Buradaki katsayılar 65535 ile çarpılır ve
konvolüsyon sonucu aynı faktöre bölünmelidir.
// ana program içerisinde
w[0] = 0;
for (short k = 1; k < 64; k++) // FIR ağırlıkları
w[k] =
(int)(65536.0 * (-1.0 + cos(pi * k)) / (pi * k));
for (short k = 1; k < 64; k++) // Hanning window
w[k] =
(int)((float)w[k] * cos(pi/2 * k / 63.0));
Figür 5. Hilbert dönüşümü ile katsayıların hesaplanması
Figür 6’da
özelliklerin kurulumu ile ilgili fonksiyonlar gösterilmiştir. Figür 3’de bunlar
ana program dışına atılmıştı.
void ADC_setup(void)
{
RCC->APB2ENR |= 0x00000100; // ADC1 için clock
RCC->APB2ENR |= 0x00000200; // ADC2 için clock
ADC->CCR = 0x00000006; // Regular simultaneous mode
ADC1->CR2 = 0x00000001; // ADC1 açık
ADC1->SQR3 = 0x00000002; // PA02 giriş olarak kullan
ADC2->CR2 = 0x00000001; // ADC2 açık
ADC2->SQR3 = 0x00000003; // PA03 giriş olarak kullan
GPIOA->MODER |= 0x000000f0; // PA02, PA03 analog giriş yap
ADC1->CR2 |= 0x06000000; // TIM2, TRG0’da SC kaynağını kullan
ADC1->CR2 |= 0x10000000; // harici SC kaynağı, yükselen kenar
ADC1->CR1 |= 0x00000020; // EOC için ADC kesmesi aktif
}
void DAC_setup(void)
{
RCC->APB1ENR |= 0x20000000; // DAC için clock aktif
DAC->CR |= 0x00010001; // DAC kontrol register, her iki kanalda
açık
GPIOA->MODER |= 0x00000f00; // PA04, PA05 analog çıkışlar
}
void GPIO_setup(void)
{
RCC->AHB1ENR |= 0x00000001; // GPIOA için clock aktif
RCC->AHB1ENR |= 0x00000010; // GPIOE için clock aktif
GPIOE->MODER |= 0x00010000; // PE08 pin’i çıkış: zaman izleme için
GPIOE->MODER |= 0x00040000; // PE09 pin’i çıkış: toggle
GPIOA->MODER |= 0x00001000; // PA06 pin’i çıkış: LED
}
void Timer2_setup(void)
{
RCC->APB1ENR |= 0x0001; // Timer 2 için clock aktif
TIM2->ARR = 8400; // otomatik geri yükleme değeri: 8400 ==
100us
TIM2->CR2 |= 0x0020; // TRGO’ı güncelleme olayı olarak seç (UE)
TIM2->CR1 |= 0x0001; // sayacı başlat
}
Figür 6. Kurulum fonksiyonları listesi
Şimdilik bu kadar arkadaşlar 
Sorularınız ve önerileriniz için m.hakki.kaplan@gmail.com adresinden her zaman bana ulaşabilirsiniz.
Hakkı KAPLAN
Kaydol:
Kayıtlar (Atom)