Dairesel Tampon (Circular Buffer)
Kullanımı
Dairesel bir
tampon veri için geçici tampon görevi yapan ayrılmış bir hafıza
parçasıdır. Özel bir şekilde
düzenlenmiştir; gelen veri tamponu tampon tam dolana kadar doldurur ve daha
sonra gelen veriler tekrar tamponu eski verilerin üzerine yazarak doldurmaya
devam eder. Yeni veriler eski verilerin üzerine yazılmadan eski verileri
kullanmak yazılımın görevidir.
Genelde bir
dizi (array) ve bu dizi içerisinde bir işaretleyici (pointer) kullanarak
dairesel tampon oluşturulur. Ancak işaretleyici dizine bağlı olmalı ve dizi
dışında bir yeri göstermemelidir. Bu durum programın çökmesine neden olabilir.
İşaretçi dizin dışında bir alana gelen veriyi yazar ise bu başka fonksiyonların
kullandığı bir hafıza alanındaki değeri veya sistem hafıza alanında bir yerdeki
veriyi bozacaktır. Bunun önlenmesi gerekmektedir.
Diyelim ki 2
üzeri N uzunluğunda bir dizi kullanmaya karar verdik. Burada ki N doğal bir sayıdır.
“N” 3 (üç)’e eşit olsun. Biz 8 elemanlı bir dizi ile ilgilenmiş oluruz. Bu
dizinin indeks değer aralığı 0 ile 7 arasında desimal veya “000” ile “111”
arasında binary’dır. Biz binary olarak
yazılmış Ptr olarak isimlendirilmiş bir integer sayı alır isek ve sadece en alt
üç bit’ini işaretleyicide kullanır isek bu üç bit noktası dizi içeresinde birer
elemana karşılık gelecektir. Bu nedenle 8-bit bir işaretçinin sadece en alt 3(üç)
bit’ini kullanıp diğer bit’leri AND işlemine tabi tutarak işaretçiyi hep dizi
adresi içerisinde tutabiliriz. Böylece “111” son kullanılabilir indeks değeri
olur.
Şimdi Ptr
sayısını 0 (sıfır)’dan başlayarak 1(bir) artırıldığını ve Ptr’nin dizi
içerisinde işaretçi olarak kullanıldığını düşünelim. Ptr’nin değeri maksimum
7(yedi) olacak şekilde AND işlemine tabi tutulur ise Ptr her artırıldığında
dizinin eleman 0, eleman 1, eleman 2 ve eleman 7 ve tekrardan eleman 0’a
döndüğü görülecektir.
Düşünün Ptr
değeri 2(iki)’den başlayarak 1(bir) azaltılsın ve daha kolay anlayabilmemiz
için 8-bit binary kullanalım. Azaltılan sayılar 00000010b den 00000001b ve daha
sonra 00000000b olur. Devam edilir ise 11111111b, 11111110b ve 11111101b vb.
devam eder. İşaretli integer sayıların 2(iki)’nin tamlayanı olarak yazıldığını
hatırlayın. 11111111b işaretli tam sayı olarak -1’e denk gelir ve 11111110b ise
-2’ye denk gelir. Ptr’nin en düşük 3(üç) bit’ini dizi sınırlarının içinde
tekrar koruyalım ve 2, 1, 0 ,7, 6 gibi Ptr değeri almaya çalışalım. Yani
0(sıfır)’dan azaltma yaptığımızda 11111111b yerine 7 elemanına ulaşalım. Bu
şekilde kullanılan bir dizi dairesel tampon olarak adlandırılır.
Hüner basit
AND lojik fonksiyonuna dayanır ve 2 üzeri N elemanlı bir dairesel tampon için
etkilidir. Diğer uzunluklarda işaretleyici için karmaşık bir sınırlayıcı
gerekir ve en iyisi bu durumdan kaçınmaktır.
Biz bu
uygulamada DAC sinyalinin üretilmesini geciktirmek için bir dairesel tampon
kullandık. Bu programda donanım kurulumlarını önceki yazılarımızda anlattığımız
için burada tekrar anlatılmayacaktır. Giriş sinyalini periyodik olarak
örnekleyen bir ADC kullanılmıştır(gerçekte her iki ADC’de kurulur ve
çalıştırılır, fakat sadece birinin sonucu kullanılmıştır). Örneklenen veriler
“Result” ismi verilen bir dairesel tamponda kayıt edilmektedir. 2(iki) DAC’tan
bir ADC sonucuna direkt bağlıdır. Diğer DAC, ADC sonucunun 100 örnek öncesini verecektir.
Tam kod listesi Figür 1’de verilmiştir.
#include "stm32f4xx.h"
int Result[1024], Ptr = 0;
void main () {
// GPIO clock aktif etme, dijital pin tanımlamaları
RCC->AHB1ENR
|= 0x00000001; // GPIOA clock aktif edilir.
RCC->AHB1ENR
|= 0x00000010; // GPIOE clock aktif edilir.
GPIOE->MODER
|= 0x00010000; // PE08 çıkış pin’i: zaman işareti
GPIOA->MODER
|= 0x00001000; // PA06 çıkış pin’i: LED
// DAC kurulumu
RCC->APB1ENR
|= 0x20000000; // DAC clock aktif et.
DAC->CR
|= 0x00010001; // DAC kontrol register, her iki kanal
açık
GPIOA->MODER
|= 0x00000f00; // PA04, PA05 analog çıkışlar
// ADC kurulumu
RCC->APB2ENR
|= 0x00000100; // ADC1 için clock
RCC->APB2ENR
|= 0x00000200; // ADC2 için clock
ADC->CCR
= 0x00000006; // sadece eş zamanlı mod
ADC1->CR2
= 0x00000001; // ADC1 açık
ADC1->SQR3
= 0x00000002; // PA02’yi giriş olarak kullan
ADC2->CR2
= 0x00000001; // ADC1 açık
ADC2->SQR3
= 0x00000003; // PA03’ü giriş olarak kullan
GPIOA->MODER
|= 0x000000f0; // PA02, PA03 analog girişler
ADC1->CR2
|= 0x06000000; // TIM2, TRG0 SC kaynağını kullan
ADC1->CR2
|= 0x10000000; // harici SC aktif, yükselen kenar
ADC1->CR1
|= 0x00000020; // EOC için ADC kesmesi aktif
// NVIC kesmesi aktif
NVIC_EnableIRQ(ADC_IRQn);
// ADC için NVIC kesmesi aktif
// Timer 2 kurulumu
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ı için TRG0 seçili(UE)
TIM2->CR1
|= 0x0001; // sayıcı aktif
// zaman gecikmesi
while
(1) {
if
(GPIOE->IDR & 0x0001) GPIOA->ODR |= 0x0040; //
LED açık
else
GPIOA->ODR &= ~0x0040; // else LED kapalı
};
}
// kesme fonksiyonu
void ADC_IRQHandler(void) //geçiş süresi yaklaşık 400ns!
{
GPIOE->ODR
|= 0x0100; // PE08 high yap
Ptr
= (Ptr + 1) & 1023; // işaretçiyi artır ve değeri
limitle
Result[Ptr]
= ADC1->DR; // ADC değerini dairesel tamponda sakla
ve EOC bayrağını temizle
DAC->DHR12R1
= Result[Ptr]; // anlık sonucu DAC’a at.
DAC->DHR12R2
= Result[(Ptr - 100) & 1023]; // önceki sonucu
DAC’a at.
GPIOE->ODR
&= ~0x0100; // PE08 low yap
}
Ş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
Hiç yorum yok:
Yorum Gönder