ARM Cortex-M SysTick Zamanlayıcısının Basit Gecikmeler İçin Kullanımı

ARM Cortex-M tabanlı mikroişlemcilerde çekirdeğin bir parçası olarak bir sistem tik zamanlayıcısı bulunur. Nispeten basit olan bu zamanlayıcının ana amacı, bir RTOS’un tik zamanlayıcısı olarak kullanılmaktır. Gelişmiş özellikleri olmayan bu zamanlayıcının özelliklerini şöyle listeleyebiliriz:

  • Sadece aşağı yönde sayar (azalarak)
  • 24 bit
  • Otomatik yükleme saklayıcısı
  • Kesme
  • Kesme periyodu için kalibrasyon saklayıcısı
  • Dahili veya harici saat girişi (*)
  • Giriş ölçme veya çıkış üretme gibi özellikler yok

(*) SysTick zamanlayıcısının özellikleri üreticiden üreticiye fark etmektedir. Dolayısıyla dökümantasyonu kontrol etmenizi tavsiye ederim. STM32F4 mikroişlemcisinde, harici saat girişi aslında sistem saatinin 8 ile bölünmüş halidir. Daha fazla bilgiyi STM32F4 Programlama Manualinde PM0214 bulabilirsiniz.

SysTick zamanlayıcısının konfigürasyonu için kullanılan saklayıcılar şöyledir.

  • CTRL : zamanlayıcı konfigürasyonu, saat seçimi, kesme aktivasyonu, 0’a düşme kontrolü
  • LOAD : otomatik yükleme saklayıcısı, sayıcı 0’a düştüğünde, VAL’a bu değer otomatik olarak yüklenir
  • VAL : sayıcının anlık değeri
  • CALIB : kalibrasyon saklayıcısı, sadece kesmeler için kullanılır

Aşağıda verilen tik sayısı kadar gecikme yapan bir fonksiyon verilmiştir.

void delay_ticks(unsigned ticks)
{
    SysTick->LOAD = ticks;
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

    // COUNTFLAG biti sayıcı 0'a ulaştığında 1 olur.
    // Saklayıcı okunduğunda otomatik olarak 0 olur.
    while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
    SysTick->CTRL = 0;
}

Zamanlayıcı başlangıçta sistem saatinin 8’e bölünmüş halini kullanır. Örneğin, 84MHz’lik bir sistem saati için, zamanlayıcı saati 10,5MHz’tir. Bu durumda zamanlayıcı tik süresi 0,095µs~=0,1µs’dir. Bu fonksiyonu ticks=105 parametresi ile çağırmak, 10µs’lik bir gecikme oluşturacaktır.

Şimdi parametre olarak mikrosaniye ve milisaniye değerleri kabul eden fonksiyonlar yazalım.

static inline void delay_us(unsigned us)
{
    delay_ticks((us * (STM32_SYSCLK / 8)) / 1000000);
}

static inline void delay_ms(unsigned ms)
{
    delay_ticks((ms * (STM32_SYSCLK / 8)) / 1000);
}

Oldukça basit fonksiyonlar oldukları için bu fonksiyonları, bir başlık dosyasına yukarıda tanımlandığı gibi inline fonksiyonlar olarak eklemenizi tavsiye ederim. Böylece derleyici iyi bir optimizasyon yaparak, çarpma ve bölme işlemlerini ortadan kaldırabilir. Bu arada STM32_SYSCLK, sistem saatini belirten bir tanımlamadır. Örneğin benim sistemimde bu tanımlamanın değeri 84000000’dir. Kullandığınız platform veya kütüphanenin mutlaka benzer bir tanımlaması vardır.

pcb2blender – PCB dosyalarınızı Blender ile 3D modelleyin

pcb2blender bir süredir üzerinde çalıştığım, pcb dosyalarını Blender ile 3D modellere dönüştürüp havalı renderler almanızı sağlayan bir araç. Blender ücretsiz, harika bir 3D tasarım programı. Araç şimdilik sadece Eagle dosyalarını desteklese de ileride farklı programları desteklemesi çok zor olmayacak.

Yukarıdaki resimde pcb2blender ile oluşturabileceğiniz çok kötü bir görüntü görüyorsunuz. Blender son derece kabiliyetli bir modelleme aracı. Yeterince uğraştığınızda çok gerçekçi renderler almanız mümkün olabilir. Hedefim bunu tamamen otomatik yapmak.

pcb2blender’ın nasıl çalıştığına gelirsek… Şu an pcb2blender, Blender içerisinden çağırabileceğiniz bir python script olarak yazılmış durumda. Şöyle bir komutla Blender’ı çağırdığınızda bir kaç dakika süren bir büyüden sonra 3D modelinizin hazır olması gerekiyor.

>> blender -P eagle2blender.py — boardfile.brd

pcb2blender, pcb dosyanızı okuyup kartın dış çizgisini alıyor  ve kartın modelini oluşturuyor (delikler-vialar bayağı iş aslında). Daha sonra komponentleri kendi kütüphanesinde tarayarak karşılık gelen 3D modelleri tasarıma ekliyor. Ve 3D modeliniz hazır. Artık Blender’ın gelişmiş yeteneklerini kullanarak modeliniz istediğiniz gibi düzenleyip, çok güzel renderlar alabilirsiniz.

Koda https://bitbucket.org/hyOzd/pcb2blender adresinden erişebilirsiniz. Henüz geliştirme aşamasında olduğu için problemlerle karşılaşmaya hazır olun. Model kütüphanesi de boş denecek durumda olduğu için elinizdeki bir dosyayı denemeniz pek bir sonuç üretmeyecek. Ama üzerinde çalışıyorum.

Şu an model kütüphanesi için sağlıklı bir standart oluşturmaya uğraşıyorum. Modelleri bir defa oluşturmaya başladıktan sonra pek geri dönüşü olan bir süreç değil, bu sebeple biraz dikkatli olmam gerekiyor.

Kodu deneyip çalıştırabilirseniz veya çalıştıramazsanız lütfen benimle paylaşın. README dosyasını okumayı unutmayın. Bir de düzgün bir işletim sistemi kullanın ; ) .

VHDL – CORDIC modülü

VHDL ile tasarladığım CORDIC modülü. Bu ilk sürümde bazı hatalar/sorunlar çıkabilir. İleride bu modülü çalışmalarımda kullandıkça düzeltmeyi düşünüyorum. ‘pi’den daha büyük döndürme değerleri için düzgün çalışmadığını gözlemledim. Sanırım matematiksel işlemlerde bir sorun var.

Not: Kodda iki basit hata buldum. Kısa zamanda çözmeye çalışacağım. Çıkışın genliği yanlış geliyor. Açı da sorun yok.

Kodda çok bariz bir hata vardı. CORDIC’te her kademe atan(2^(-i)) kadar döndürme yapabiliyor. İlk kademe 45 derece olan atan(2^0) ile başlıyor. Ben yanlışlıkla ilk kademeyi atan(2^-1)’den (yaklaşık 26 derece) başlatmıştım. Kademeler ilerledikçe açı küçüldüğü için modül toplamda 90 derece bir dönme yapamıyor. Bunu çözmek için başlangıca ekstradan iki adet atan(2^-1)’lik kademe ekledim (Kafam nerdeyse!). Bu da işleri iyice karıştırdı. Şimdi o kademeleri kaldırdım. İlk kademeyi i=0’dan başlattım. Sorun ortadan kalktı.

Dosyayı indirmek için tıklayın.

/*

	CORDIC module by Hasan Yavuz ÖZDERYA ( hy [at] ozderya.net )
	
	Trabzon KTU DSPLAB 2013
	
	v0.2 , 08.04.2013	: corrected barely wrong implementation
	v0.1 , 19.03.2013
	
*/

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use ieee.fixed_pkg.all;
/*
	Main CORDIC module
*/
entity cordic is
	
	Generic(n:integer := 16;	-- bus width (n=16 -> Q.15), internals work with Q1.15
			ns:integer := 10);	-- number of stages
	Port(
		rotation: in	sfixed(3 downto -(n-4));	-- rotation input
		d_r		: in	sfixed(0 downto -(n-1));	-- inputs
		d_c		: in	sfixed(0 downto -(n-1));
		q_r		: out	sfixed(0 downto -(n-1));	-- outputs
		q_c		: out	sfixed(0 downto -(n-1));
		clk		: in	std_logic;
		reset	: in	std_logic
	);
	
	subtype tfixed is sfixed(1 downto -(n-1));		-- type used for internals
	subtype rtfixed is sfixed(3 downto -(n-4));		-- type used for radians
	
end entity cordic; 

architecture single_unit of cordic is

	-- first stage (#-1,jumper stage) rotates 90|180|270 degree if required
	
	type tfixed_array is array(-1 to ns-1) of tfixed;
	type rtfixed_array is array(-1 to ns-1) of rtfixed;
	type rtfixed_array2 is array(0 to ns-1) of rtfixed;
	
	-- stage input output signals
	
	signal 	s_beta,s_phi,							-- phi: target rotation
			s_beta_o,s_phi_o : rtfixed_array;		-- beta: rotation so far
	
	signal 	s_d_r, s_d_c,							-- d_r,d_c: stage input real and complex
			s_q_r, s_q_c : tfixed_array;			-- q_r,q_c: stage output real and complex
	
	-- constants
	signal atans	: rtfixed_array2;	-- holds arctangents of 2^-i (i=0,1,2..)
	
	constant c_K		: tfixed := to_sfixed(0.607253031529134,1,-(n-1));
	constant pi_3_2		: rtfixed := to_sfixed(MATH_3_PI_OVER_2,3, -(n-4));
	constant pi			: rtfixed := to_sfixed(MATH_PI,3, -(n-4));
	constant pi_1_2		: rtfixed := to_sfixed(MATH_PI_OVER_2,3, -(n-4));
	constant pi_1_4		: rtfixed := to_sfixed(MATH_PI_OVER_4,3, -(n-4));

begin
	
	-- generate required constants for iterative stages atan(2^-i)
	gen_atans: for i in 0 to ns-1 generate
		constant c_atan : real := arctan(2.0**(-i));
	begin
		atans(i) <= to_sfixed(c_atan,rotation);
	end generate;
	
	-- generate intertal connections between stages
	gen_internals:for i in 0 to ns-1 generate  -- except first stage which is input
				
		s_beta(i) <= s_beta_o(i-1);
		s_phi(i) <= s_phi_o(i-1);
		
		s_d_r(i) <= s_q_r(i-1);
		s_d_c(i) <= s_q_c(i-1);
					
	end generate;
	
	process(clk)
		variable a,b,p,k	: tfixed;
		variable beta,phi	: rtfixed;
	begin
		
		if reset = '1' then
			
			q_r <= to_sfixed(0,q_r);
			q_c <= to_sfixed(0,q_c);
			s_beta_o <= (others => to_sfixed(0,q_r));
			s_phi_o <= (others => to_sfixed(0,q_r));
			s_q_r <= (others => to_sfixed(0,q_r));
			s_q_c <= (others => to_sfixed(0,q_r));
			
		elsif rising_edge(clk) then
			
			-- register inputs for first stage
			s_beta(-1) <= to_sfixed(0,d_r);
			s_phi(-1) <= rotation;
			s_d_r(-1) <= resize(d_r,a);
			s_d_c(-1) <= resize(d_c,a);	
			
			for i in -1 to ns-1 loop
				
				a := s_d_r(i);
				b := s_d_c(i);
				
				beta := s_beta(i);
				phi := s_phi(i);
				
				if i=-1 then		-- jumper stage for r>pi/2
					
					if phi > pi_3_2 then
						p := b;
						k := resize(-a,a);
						phi := resize(phi-pi_3_2,phi);
					elsif phi > pi then
						p := resize(-a,a);
						k := resize(-b,a);
						phi := resize(phi-pi,phi);
					elsif phi > pi_1_2 then
						p := resize(-b,a);
						k := a;
						phi := resize(phi-pi_1_2,phi);
					else
						p := a;
						k := b;
					end if;
					
				else				-- iterative stages
					
					if  phi>beta then
						
						p := resize(a-scalb(b,-i),a);	-- scalb shifts number
						k := resize(b+scalb(a,-i),a);	-- does binary division
						beta := resize(beta+atans(i),beta);
						
					else
						
						p := resize(a+scalb(b,-i),a);
						k := resize(b-scalb(a,-i),a);
						beta := resize(beta-atans(i),beta);
						
					end if;
					
				end if;
				
				s_q_r(i) <= p;
				s_q_c(i) <= k;
				
				s_beta_o(i) <= beta;
				s_phi_o(i) <= phi;
												
			end loop;
			
			-- last stage to output (notice 'c_K' scaler)
			q_r <= resize(c_K*s_q_r(ns-1),q_r);
			q_c <= resize(c_K*s_q_c(ns-1),q_r);
			
		end if;
		
	end process;
		
end single_unit;

Stellaris Launcpad ve yeni GUI Composer

Dün akşam ön siparişle aldığım Stellaris Launchpad geldi ve ilk denediğim şeylerden biri Code Composer Studio 5.3 ile birlikte gelen GUI Composer özelliğini denemek oldu. GUI Composer, CCS üzerinden işlemcinizi kontrol ve takip etmek için basit arayüzler hazırlamanızı sağlayan bir eklenti. Kullanması o kadar kolay ki, göstermek için bir blog yazmadan edemedim.

Önce Stellaris Launcpad için StellarisWare kütüphanesini indirmelisiniz: http://www.ti.com/tool/sw-ek-lm4f120xl

Bu paket gerekli donanım kütüphanelerini ve örnek projeleri içeriyor.Paketi c:\StellarisWare gibi bir dizine açın.

Daha sonra Code Composer Studio’dan Project->Import Existing CCS Eclipse Project’i seçiyoruz.

“Browse”a tıklayarak C:\StellarisWare\driverlib (veya nereye kurduysanız) seçiyoruz. İki proje listelenecek. “driverlib-cm4f”i seçip finish diyoruz. Aynı şekilde C:\StellarisWare\usblib’den usb kütüphanesini de import etmenizde fayda var.

Daha sonra C:\StellarisWare\boards\ek-lm4f120xl dizininden “hello” örnek programını import ediyoruz. Şu adreste bu adımlar daha ayrıntılı bir şekilde açıklanmış: http://www.ti.com/lit/ml/spmu132b/spmu132b.pdf .

“hello” projesini yükledikten sonra bir debug tuşuna tıklayarak bir deneme yapmanızda fayda var.

Şimdi GUI Composer’a geçmeden önce görsel olarak takip etmek istediğimiz bir değişken oluşturalım. “hello.c” dosyasını açarak

int sayi;

satırı ile global bir değişken oluşturalım. (“main” fonksiyonunun hemen üzerine yazabilirsiniz)

main fonksiyonunun içerisinde bir while döngüsü var. Bu döngü bekleme yapıyor ve ledi yakıp söndürüyor. Bizde bu döngünün içine sayi değişkeniyle oynayacak bir kaç satır yazıyoruz.

sayi = sayi+1;

if(sayi>100) {
sayi = 0;
}

Şimdi sıra GUI Composer’da . View menüsünden GUI Composer’a tıklıyoruz. Birazcık yükleme yapıyor. Daha sonra yeni proje oluştur diyoruz. Sol taraftaki paletten “Instrumentation” sekmesini seçiyoruz.  AnalogGauge‘u seçerek arayüzümüze bir analog gösterge ekliyoruz. Daha sonra bu yeni eklediğimiz nesne seçili iken arayüzün sağ tarafından Binding  sekmesini seçiyoruz. Value kısmına bu göstergeyi bağlamak istediğimiz değişkenin adını yazıyoruz, yani bu durumda “sayi” değişkeni. Bu aşamada ekranınız şöyle gözükmeli;

Ctrl+S ile kaydettikten sonra çalıştırmaya geliyoruz. Neredeyse hiçbir şey yapmamak kadar basit, Debug tuşuna tıklayarak işlemcimizi çalıştırıyoruz. Daha sonra GUI Composer arayüzünden sağ üst taraftaki Preview tuşuna tıklayarak hazırladığımız arayüzü çalıştırıyoruz.

Bu kadar! Her şey yolunda gittiyse göstergenin 0’dan 100’e doğru hareket ettiğini görmeniz gerekiyor.

Code Composer Studio’nun hastası değilim, bazı yönlerini sevmiyorum fakat bu özelliği gerçekten harika ve kullanması eğlenceli olacak.

Merhaba wordpress

Uzun süren bir Flash macerasının ardından blogumu artık WordPress’le devam ettirmeye karar verdim. Flash güzeldi. Doğrusu insanlardan genel olarak olumlu tepkiler aldım. Bitmemiş ve karışık bir tasarımdı. Yazıların okunması pek konforlu değildi ama 3-boyut efekti sayfaya başka bir hava katıyordu. Farklıydı velhasıl. Güzeldi.

Ama şartlar değişti. Ben değiştim. Şöyle bir bakayım nedir diye girdiğim linuks varsayılan işletim sistemim oldu ve yakın zamanda vazgeçmeyi de düşünmüyorum. Linuks demişken özgür yazılım, açık kaynak kod demeden olmaz. Ve bunlar Flash’ın çok uzak olduğu şeyler. Adobe linuks için Flash desteğini durdurdu. Mobil platformlara da güncelleme gelmeyeceğini duyurdu. İleride ne olur bilemem ama ben Flash’la olmayacağım.

İlk olarak birkaç ay önce sıfırdan HTML birşeyler yapmaya çalıştım. CSS aslında çok güzel bir sistem fakat. Her şeyi programlamayla veya çizerek yapmaya alışmış biri için sinir bozucu olabiliyor. Denemeden sonucun ne olacağını kestirmek zor. ‘Syntax’ı tanımak yetmiyor. Tarayıcıları da tanımanız gerekiyor. Duvara tosladım yani, vazgeçtim. Fakat birkaç hafta önce nasıl olduysa ‘yeter artık, wordpress kuracağım’ (bkz) dedim ve hemen kafama göre bir tema aramaya başladım. Buldum da (şimdikine hiç benzemiyordu). Yerele bir kurulum yaptım. O temayı biraz kafama göre düzenlemeye çalıştım. İstediğim şeyi elde edemedim. Ben de kendi temamı oluşturmaya karar verdim.

Web’de wordpress teması nasıl tasarlanır diye arama yaptığınızda karşınıza sıfırdan değil de basit bir temadan başlamanızı salık veren bir çok tavsiyeyle karşılaşıyorsunuz. Denedim. Edemedim. Biraz daha araştırınca aslında sıfırdan tema oluşturmanın o kadar da zor olmadığını anlatan güzel bir ders buldum. Onu takip edip ilk denemelerimi yaptım. WordPress teması oluşturmayı anlatan bir çok yazı en az 7-8 adet dosya gerektirdiğinden bahsediyor fakat aslında sadece bir “index.php” ve bir “style.css” dosyası çalışır bir sayfa oluşturmak için yeterli. Evet, düzgün tasarlanmış, kapsamlı bir wordpress teması için zibilyon tane dosya gerekiyor ama yeni başlayanlar için bence en doğrusu (sıfırdan yazıyorsanız) tek dosyadan başlamak.

Neyse, bir iki denemenin ardından tasarımlara başladım. İlk önce böyle futuristik bir tema yapmak vardı aklımda. Fakat ‘sayfamı’ beğendiğini söyleyen bir mail daha alınca fikrim hemen değişti. Klasiğe devam dedim. İlk önce inkscape’te bir tasarım yaptım ve onun üzerinden html-css’i oluşturmaya başladım. Başlangıçta herşey yolunda gidiyordu lakin beni nelerin beklediğini iyi kestirememiştim. Tasarım ilerledikçe neleri unuttuğumu-eklemem gerektiğini gördüm. Eğer zamanında inkscape’te yaptığım tasarım tam olsaydı ortaya daha düzgün birşeyler çıkarabilirdim diye düşünüyorum. Ama sonlara doğru yamalamaya dönünce iş istediğim estetik de kayboldu. Hala bir çok eksiği var ama wordpress’in güzelliği de bu herşeyi kolaylıkla ekleyebiliyorsunuz.

Eh umarım beğenilir : )

Bu arada merak edenler için; eski bloguma buradan erişebilirsiniz.

Qucs 0.0.16’yı Ubuntu 12.10 altında derleyip çalıştırma

Qucs (Quite Universal Circuit Simulator) linuksta kullanabileceğiniz en iyi devre simülatörlerden biri. Ne yazıkki program uzun bir süredir güncellenmiyor ve çalışan paketler bulmak gitgide zorlaşıyor. Ben de çözüm olarak kaynaktan derlemeyi buldum. Ama gördüm ki ubuntu'nun en son sürümüyle bu da mümkün değil. Tabi her şeyin bir çözümü vardır. O da şöyle;

Qucs Qt 3 ile yazılmış. Şu an Qt 4 yürürülükte ve Qt 5'in de eli kulağında. Qucs'u derleyebilmek için qucs kaynak kodunun yanı sıra Qt 3 geliştirme kütüphanelerine ihtiyacınız var. Qucs kaynak kodunu buradan indirebilirsiniz. Qt 3 geliştirme kütüphanelerinin debian paket adı qt3-dev-tools . Maalesef bu paket an itibariyle 12.10 depolarında bulunmuyor. En son paket 12.04'te bulunuyordu. Ben de paketi 12.04 depolarından indirme yoluna gittim. Şimdi adım adım nasıl yaptığımı anlatayım;

gksu gedit /etc/apt/sources.list

komutu ile paket kaynaklarının bulunduğu dosyayı açıyoruz. Bu dosyanın en sonuna şu satırları ekleyip kaydediyoruz.

# Precise

deb http://tr.archive.ubuntu.com/ubuntu/ precise main

deb http://tr.archive.ubuntu.com/ubuntu/ precise universe

Bu aşamaya dikkat! Eski ubuntu sürümünden gelen paketler Ubuntu'yu karıştırabilir. Hiç bir sorumluluk kabul etmiyorum 🙂 . Size tavsiyem, qt3-dev-tools paketini kurduktan hemen sonra bu satırları # ile iptal etmeniz.

Daha sonra 'synaptic'i kullanarak veya;

sudo apt-get update

sudo apt-get install qt3-dev-tools

ile qt3-dev-tools paketini kuruyoruz.

Bunu yaptıktan sonra Qucs kaynak koduyla gelen README dosyasındaki talimatları izleyerek programı derleyebilirsiniz. Fakat eğer siz de benim gibi GCC derleyicilerinin 4.7 sürümünü (muhtemelen 4.5,4.6 da) kullanıyorsanız "call of overloaded ‘conj(nr_complex_t)’ is ambiguous" gibi bir hata verecektir. Bunun da aslında şurada bulduğum kolay bir çözümü var.

/usr/include/c++/4.7/tr1

dizininin adını

".tr1" olarak değiştirin

Böylece derleyici hataya sebep olan kütüphaneleri bulamayacak ve Qucs'u (inşallah tabi) sorunsuz bir şekilde derleyeceksiniz.

eaglelibgen – Eagle için sembol oluşturma aracı

eaglelibgen bir metin dosyasındaki bilgileri alarak Eagle PCB programı için sembol oluşturan küçük bir araç. Özellikle bol pinli entegreler (FPGA gibi) için sembol oluşturmayı kolaylaştırmak için bir kaç günde yazdığım bir program. Örnek bir giriş dosyası şu şekilde oluyor:

#İlk olarak cihazın adı tanımlanıyor

>DEVICE

ADC0561

#cihazın paketi tanımlanıyor, paket çalıştığınız kütüphanede bulunmalı

#veya @kutuphaneadı şeklinde, hangi kütüphanede olduğunu belirtmelisiniz

>PACKAGE

TQFP80@ref-packages

#Eagle’da bir cihaz bir kaç farklı sembolden oluşabiliyor

#Böylece FPGA gibi elemanlarla çalışmak kolaylaşıyor

#Bu sembollerin her birini “GATES” komutu ile tanımlıyoruz

>GATES

BANK1

BANK2

#son olarak da cihazın pinlerini tanımlıyoruz

#her bir pinin hangi bank’a ait olduğunu da belirtiyoruz

#pinler sırayla yazıldığında ‘kılıftaki’ karşılıklarını yazmamıza gerek yok

>PINS

CMA BANK1

AGND BANK1

CLKPA BANK1

CLKMA BANK1

AGND BANK2

REFMA BANK2

REFPA BANK2

….

….

eaglelibgen bu dosyayı alıyor ve size Eagle’da çalıştırabileceğiniz bir .scr-script dosyası oluşturuyor. Bu dosyayı çalıştırdığınızda Eagle terimleriyle “device” ve “symbol”ler oluşturuluyor. Daha sonra paket eklenerek pin bağlantıları da otomatik olarak yapılıyor. Programı deneyebilmek için kaynak kodla birlikte bir kaç tane örnek dosya da geliyor.

Şu an programın derlenmiş halini sunmuyorum. Fakat herhangi bir kütüphaneye bağımlılığı olmadığı için modern C++ derleyicilerinin herhangi biriyle çalıştırabilirsiniz. Code::Blocks IDE’si için bir de proje dosyası kaynak kodla birlikte geliyor.

Bu linkten kodun son halini indirebilirsiniz. İşinize yaraması dileğiyle 😉

MSP430 Assembly diliyle DS18B20 sıcaklık sensörü iletişimi

DS18B20, Dallas tarafından üretilen dijital bir sıcaklık sensörü. 3 bacaklı bir TO-92 (tipik transistör paketi) şeklinde olan sensörün tek bir bacağı üzerinden iletişim yaparak sıcaklık verisini okuyabiliyorsunuz. Bu özelliğe “One-Wire” protokolü deniyor. Bu protokol sayesinde aynı hatta bir kaç adet DS18B20 sensörü yerleştirerek hepsini aynı anda kullanmanız da mümkün.

Ben bu yazımda bir kaç sensörün aynı anda kullanılması gibi konulara değinmeyip sadece sensör üzerinden sıcaklık datasının okunması için gerekli MSP430 assembly komutlarını vereceğim. Buradan indereceğiniz programı MSP430 Launchpad kitine (g2231 işlemcisi ile) yükleyerek DS18B20’den sıcaklık verisini okuyabilirsiniz. Program çalıştırıldığında tek bir okuma yaparak, sensörden aldığı 9 baytı (sensörün “scratchpad” denilen belleği) RAM belleğe 0x200 adresinden itibaren yazıyor.

One-Wire iletişim protokolünden bahsedeyim biraz. Bu protokolde bütün iletişim tek bir kablo üzerinden sağlanıyor. Yani ayrı bir TX-RX veya data-clock hatları sözkonusu değil. Dolayısıyla bütün iletişim master tarafından kontrol ediliyor. Master veri yazarken, ilk olarak hattı 0’a çekiyor. Kısa bir süre bekledikten sonra 1 veya 0 yazmak istemesine göre hattı kaldırıyor veya 0’da tutuyor. Burada zamanlama önemli bir faktör oluyor.

Önemli bir not: İletişimin sağlandığı data hattı 4k7 gibi bir direnç değeri ile pull-up edilmeli ve master hiç bir zaman data hattına ‘1’ yazma suretiyle data hattını kaldırmamalı. Sadece data hattının bağlandığı port pinini giriş yaparak, pull-up etme suretiyle hattı kaldırmalı. Programı incelerseniz, MSP430’un data pinine hiç bir zaman ‘1’ yazmadığını göreceksiniz.

Peki okuma yaparken?? Burada da bütün kontrol masterda. Master her bir biti tek tek kendi istediği zamanda okuyabiliyor. Yani DS18B20 bitleri seri bir şekilde peşpeşe göndermiyor. Sadece master bir istek gönderdiğinde sıradaki biti gönderiyor. Master bir bit okumak istediğinde data hattını indiriyor, 1 us kadar tutarak daha sonra hattı bırakıyor. Bu ‘0’ı gören DS18B20’de sıradaki bitin 1 ya da 0 değerinde olmasına göre hattı 0’a çekiyor veya 1 yapıyor. Master da bu sırada okuma yaparak bitin değeri almış oluyor. Diyagramları incelerseniz daha açık bir şekilde anlayacaksınız. Bu arada master okuma isteklerini göndermeden önce gerekli komutları yazmış olmalı aksi taktirde DS18B20 masterın isteklerine cevap vermeyecektir.

Biraz’da programdan bahsedeyim. Program DS18B20 ile ilgili 7 adet alt program içeriyor. Bunlar;

WRITE_R: Her işlemin başında ilk olarak bir Reset darbesi gönderilerek sensörün varlığı tespit ediliyor. Eğer sensör bağlı değilse veya bir sebepten dolayı cevap vermiyorsa program burada sonsuz döngüye girip kalıyor

WRITE_0: Sensöre ‘0’ bitini yazmak için kullanılan alt program

WRITE_1: Sensöre ‘1’ bitini yazmak için kullanılan alt program

READ_x: Sensörden bir bit okumak için kullanılan alt program, cevap DR (R8 olarak tanımlanmış) saklayıcısının 7. bitine yazılıyor.

WRITE_B: Sensöre bir bayt data yazan alt program, WRITE_0 ve WRITE_1 alt programlarını kullanıyor. Yazılacak data (bu genelde bir komut olur) DR saklayıcısından okunuyor.

READ_B: Sensörden bir bayt okuyan alt program. READ_x alt programını kullanıyor. Okunan baytı DR saklayıcısına yerleştiriyor.

READ_SP: Sensörden bütün “scrathcpad”i okuyan alt program. Aslında çoğu zaman ilk 2 baytı okumak yeterli. Sadece bu ilk iki bayt sıcaklık verisi taşıyor.

MSP430 IAR ile çalıştırabileceğiniz programı buradan indirebilirsiniz. “ds18b20.h” dosyasını da projeye eklemeyi unutmayın.

VHDL ile “moving average” filtre

Analog bir işareti yumuşatmanız, küçük genlikli gürültülerden kurtarmanız gerektiğinde yapabileceğiniz en basit şeylerden biri “moving average” filtreden geçirmektir. Türkçe “yürüyen ortalama” diyebileceğimiz bu filtre basitçe şöyle çalışır. 4 seviyelik (tab) bir filtre düşünelim. Böyle bir filtrenin çıkışında birim anda en son 4 örneğin ortalaması görünür.

t=0 anı için filtre çıkışını şöyle yazabiliriz.

q(0) = (d(0)+d(-1)+d(-2)+d(-3))/4

Burada d(-1), bir önceki örnek, d(-2) ondan da önceki örnek demektir. Formülü ‘n’ anı için yazarsak;

q(n) = (d(n)+d(n-1)+d(n-2)+d(n-3))/4

olur.

Peki bunu VHDL ile FPGA üzerinde nasıl gerçekleştireceğiz. Başlamadan önce bitmiş kodu görmek isteyenler için movaverage.vhdl.

Formülden görebileceğiniz gibi 4 adet örneğin saklanması (‘register’larda veya RAM’de olabilir), toplanması ve bölünmesi gerekiyor. Saklama kısmı tamam. Örnekleri saklayacağız. Orayı kurcalamıyoruz. Fakat 4 girişli bir toplayıcı büyük olmasa da, mesela 128 seviyelik bir ortalama istersek 128 girişli bir toplayıcı gerekecek bize. Böyle bir toplayıcı arda arda bağlanmış çok fazla, onlaaaarca lojik birim gerektireceğinden, hem çok fazla alan kullanılmış olacak hem de böyle bir toplayıcının hızı çok düşük olacaktır. O zaman formüle bakıyoruz ve filtre çıkışının aslında arada saklanan örneklere bağlı olmadığını, sadece giren ve çıkan örnekler tarafından değiştirildiğini görüyoruz. Öylese formülümüzü şöyle yeniden yazabiliriz.

q(n) = q(n-1)+d(n)/4-d(n-4)/4

Açıklarsak, q(n-1) çıkışın bir önceki değeri, d(n) ortalamaya yeni eklenen örnek d(n-4) ise artık filtreden çıkan ve sonuca etkisi olmayan örnek. Formülü şekillendirip şöyle yazıyoruz;

q(n) = q(n-1)+(d(n)-d(n-4))/4

Şimdi gördüğünüz gibi, seviye sayısı ne kadar fazla olursa olsun bir adet çıkarıcıya (aslında bu da bir toplayıcı) ve bir adet toplayıcıya ihtiyacımız var.

Bölme işlemine gelirsek, yapı itibariyle bölücüler büyük bileşenler olduklarından bölücülerden olabildiğince kaçıyoruz. Yalnız filtrenin seviye sayısını 2’nin kuvveti(4,8,16..) olacak biçimde seçersek bölme işlemi oldukça kolaylaşıyor. Zira ikili sistemde bir sayıyı 2’ye bölmek için bir defa sağa kaydırmak, 4’e bölmek için 2 defa sağa kaydırmak yeterli. Filtre tasarlarken boyutları 2’nin kuvveti olacak şekilde seçiyoruz ve fazla düşünmüyoruz.

Şimdi VHDL kısmına başlayalım. Tasarım kısmına girmeden ‘entity decleration’ımızı yazalım ve modülümüzün dışarıdan neye benzeyeceği belli olsun.

library ieee;

use ieee.std_logic_1164.all;

use ieee.numeric_std.all;

entity movaverage is

Generic(n: natural :=8; ntaps: natural :=4);

Port(datain: in std_logic_vector(n-1 downto 0);

dataout: out std_logic_vector(n-1 downto 0);

average: out std_logic_vector(n-1 downto 0);

clk: in std_logic);

end entity;

‘Generic’ tanımlamalarındaki n filtremizin kullanacağı sayıların büyüklüğü, ntaps ise seviye sayısıdır.

Şimdi modülümüzün iç kısmını tanımlayalım. Hesaplama kısmına geçmeden önce depolama kısmını halletmeliyiz. Yapacağımız şey aslında bir “shift register” fakat bitler yerine her defasında bir bit vektörü (sayılar binary olduklarından, sayı değil de bit vektör diyorum) kaydıracağız. Dolayısıyla bizim vektörleri yan yana saklayabileceğimiz bir diziye (array) ihtiyacımız var. Buyrun;

architecture unsiginput of movaverage is

–kullanılacak sinyalleri tanımlıyoruz

–bu iki satırda vektörlerimizi saklayacağımız ve kaydıracağımız

–diziyi tanımlıyoruz

type array_taps is array(0 to ntaps-1) of unsigned(n-1 downto 0);

signal taps:array_taps;

–toplam sonucu burada tutulacak

–sayılar toplandıkça büyüyeceklerinden bu vektör

–giriş vektöründen daha büyük

signal sum:unsigned(n-1+ntaps downto 0);

–ortalama sonucu da burada

signal ave:std_logic_vector(n-1+ntaps downto 0);

begin

–tasarım buraya yazılıyor, önce biraz açıklama yapacağız

end unsiginput

Dizimizi tanımlarken std_logic_vector yerine unsigned veri türünün kullanılması kafanızı karıştırmasın. Unsigned türü yapı olarak std_logic_vectorle aynı olan bir bit vektördür. Tek bir komutla std_logic_vector türüne dönüştürülebilir. Yalnız numeric_std kütüphanesinin operatörleri, fonksiyonları (toplama vs) unsigned türü için tanımlandığından bunu kullanıyoruz. Yoksa her ikisi de bellekte aynı şekilde saklanacaktır.

Şimdi kaydırma işlemini yapacak ‘process’imizi yazalım.

architecture unsiginput of movaverage is

begin

process(clk)

begin

if rising_edge(clk) then

taps(0) <= unsigned(datain);

taps(1 to taps’right) <= taps(0 to taps’right-1);

dataout <= std_logic_vector(taps(taps’right));

end if;

end process;

end unsiginput

Bu kısmın nasıl çalıştığına fazla giremeyeceğim. VHDL kodunun nasıl çalıştırıldığını ben de yeni yeni anlamaya başlıyorum. Ve maalesef kolayca açılayabilecek kadar iyi bilmiyorum. Ama satırları şöyle bir açıklayayım.

taps(0) <= unsigned(datain);

Bu satırda yeni data dizinin ilk elemanına atanıyor.

taps(1 to taps’right) <= taps(0 to taps’right-1);

Burada kaydırma işlemi yapılıyor. 4 seviyeli bir sistem için sözel ifade şöyle olurdu. 0,1 ve 2. seviyeler sırasıyla 1,2 ve 3. seviyelere yerleştirilerek kaydırma yapılıyor.

dataout <= std_logic_vector(taps(taps’right));

Ve dizinin sonundaki örnek çıkışa veriliyor. Dikkat bu ortalama sonucu değil.

Şimdi toplam ve ortalamayı hesaplayan kısımları da ekleyelim;

architecture unsiginput of movaverage is

begin

process(clk)

begin

if rising_edge(clk) then

taps(0) <= unsigned(datain);

taps(1 to taps’right) <= taps(0 to taps’right-1);

dataout <= std_logic_vector(taps(taps’right));

–toplama ve çıkarma işlemlerinin

–yapıldığı satır, vektörlerin toplanmadan önce

–resize() fonksiyonu ile en büyük vektörün boyutuna

–getirilmesine dikkat

sum <= sum+resize(unsigned(datain),sum’length)-resize(taps(taps’right),sum’length);

end if;

–bölme işlemi yapılıyor ve sonuç

–std_logic_vector’e dönüştürülüyor

ave <= std_logic_vector(sum/ntaps);

–hesaplama için kullanılan vektörlerin boyutları daha

–büyük olduğu için bu ara işlem yapılıyor

average <= ave(n-1 downto 0);

end process;

end unsiginput

Burada resize(unsigned(datain),sum’length) filtreye yeni giren data, resize(taps(taps’right),sum’length) filtreden çıkmakta olan datadır. Bölme işleminin bölme operatörü ile yapılmasına dikkat edin. VHDL’de aslında bu şekilde bölme yapmak mümkün değildir (an itibariyle bölme operatörünü destekleyen sentezleyici yok). Yalnız ‘ntaps’ yani bizim seviye sayımız her zaman 2’nin kuvvetleri şeklinde olacağından sentezleyici burada aslında basitçe bir kaydırma işlemi yapacaktır. ‘ntaps’ değerini 2’nin kuvveti olmayan bir sayıya atarsanız sentezleyici hata verecektir. Yanii vermesi lazım.

Vee bu kadar. Umarım birşeyler anlamışsınızdır. Hatta umarım bu yazıyı sonuna kadar okumuşsunuzdur. Dosyayı indirirseniz içerisinde birden fazla tasarım göreceksiniz. siginput olan unsiginput ile aynı tasarıma sahip fakat negatif sayılarla da çalışabiliyor. sigabsinput olanın girişi signed olabiliyor fakat girişin mutlak değerini alarak çalışıyor. Farklı bir amaçla tasarlandı yani. signedinput_pl olan ise biraz daha farklı bir tasarım. Özelliği ardışık (pipelined) tasarım kullanması. Ardışık tasarımların faydası üzerine de bir yazı yazmayı planlıyorum.