22 Kasım 2015 Pazar

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 Gülen
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