Elektronik / Mikrodenetleyici Projeleri/

ARM: System Tick Timer

Sponsorlu Bağlantılar

Yazın sonunda karar verip başladığım ARM konusunda çalışmak için geçen ay bir ekip projesiyle birlikte yurtdışından üzerinde LPC1768 bulunan LPCExpresso kartı sipariş etmiştim. Kartın gelmesi uzasa da (ki bu süre zarfında ben başka bir ARM kitiyle çalışmalara başladım) en sonunda bugün çalışmalarıma başlayabildim.

Daha önce çeşitli sitelerde okuduğum kadarıyla LPC1768’de ilk dikkatimi çeken kısım System Tick Timer denen birim oldu.

Çoğu uygulamada gecikme sağlamak için çoğu kullanıcı yazılımsal olarak döngüler kullanır. Bu yöntem amatör uygulamalar için oldukça kolay ve işe yarar gibi görünse de iş karmaşıklaştıkça yazılımsal olarak sağlanan gecikmenin sağlıksız ve tutarsız (özellikle büyük gecikme zamanlarında) olduğu gözükür. Öte yandan iş gerçek zamanlı uygulamalara geldiğinde yazılımsal gecikme söz konusu dahi olmaz. İşte bu noktada System Timer Tick kullanım yönünden oldukça faydalı olabilmektedir.

Aşağıda blok şeması görülebilecek LPC1768 içerisinde bulunan System Tick Timer 100MHz saat kaynağında 10ms’de bir kesme oluşturacak şekilde ayarlanmıştır.

arm_System_Tick_Timer

Yukarıdaki şekilden de görüleceği üzere hem CCLK hem de STCLK pinine bağlanabilecek bir kristali saat kaynağı olarak kullanabilen System Tick Timer, içerisine yüklenen 24 bitlik sayı değeri sıfırlandığında kesmeye girer ve STRELOAD registerindeki değeri tekrar içerisine yükleyerek bir azaltarak tekrar sıfıra gelinceye kadar sayma işlemini yürütür. System Tick Timer’ı kullanmak için ise aşağıdaki adımlar sırasıyla uygulanmalıdır:

1) STRELOAD (24 bit) değeri ne kadar sürede bir kesmeye gitmesi gerekiyorsa ona göre STRELOAD=(CLK/100)-1 formülüne göre ayarlanmalıdır.

2) System Tick Timer Kesmesine izin verilmelidir ve

3) Son olarak System Tick Timer saat kaynağı seçilip, sayıcı aktif edilmelidir.

Örneğin 12MHz CCLK değerinde 1ms gecikme sağlamak için yüklenmesi gereken değerler;

STRELOAD=(12.000.000/100)-1=1D4BF
STCTRL=7;
olmalıdır.

Datasheet’te dikkatlice bakılırsa STRELOAD registerinin reset değerin de 0xF423F olduğu görülebilmektedir. Bu değer 100MHz CCLK değeri için 10ms gecikme sağlamaktadır.

Biz ise uygulamamızda 12MHz CCLK değeri için 1ms’de bir kesme oluşturup, LPCExpresso kartında bulunan tek ledin (P0.22) 500ms arayla yanıp sönmesini sağlayalım.

#include "LPC17xx.h"

volatile unsigned long STT_Value;

void SysTick_Handler(void)
{
	STT_Value++;
}

void DelayMs(unsigned int t)
{	
	unsigned long temp;

	temp = STT_Value;
	while ((STT_Value - temp) < t);
}
	
int main (void) 
{            
	SystemInit ();		// İlk ayarlar yapılıyor

	LPC_PINCON->PINSEL0=0;	// Pinler GPIO olarak ayarlanıyr
	LPC_PINCON->PINSEL1=0;

	LPC_GPIO0->FIODIR = 0xFFFFFFFF;	// GPIO'ların hepsi çıkış oluyor
	// 12MHz CCLK için 1ms kesme için yükleme değeri
	// (12MHz/1000)-1 olur.
	SysTick->LOAD=(12000000/1000)-1;  // 1ms de bir kesme oluşacak
	// System Tick timer kesmesi önceliği belirleniyor
	NVIC_SetPriority (SysTick_IRQn, 15);
	// System Tick Timer Value değeri sıfırlanıyor
	SysTick->VAL =(0x00);
	// System Tick Timer açılıyor, kesme aktif, kaynak CCLK
	SysTick->CTRL = 7; 

	for (;;)
	{
		LPC_GPIO0->FIOSET2 =  0x40;
		DelayMs(500);
		LPC_GPIO0->FIOCLR2 =  0x40;
		DelayMs(500);
	}
}

Yukarıdaki kodun çalışır halini ise aşağıda verdiğim videoda görebilirsiniz. Bu noktada belirtmek isterim ki yukarıdaki kod System Tick Timer’ın nasıl çalıştırılabileceğine örnek olduğu için içerisinde (DelayMs’den dolayı) ölü zamanlar bulunmaktadır.
Eğer siz ölü zamansız bir işlem yaptırmak istiyorsanız aşağıda verebileceğim kod yapısını ya da bir benzerini kullanabilirsiniz.

#include "LPC17xx.h"

volatile unsigned long STT_Value;
volatile unsigned char led_flag;

void SysTick_Handler(void)
{
	STT_Value++;
	if(STT_Value<500) led_flag=1;
	else		     led_flag=0;
	if(STT_Value==1000)  STT_Value=0;
}
	
int main (void) 
{            
	SystemInit ();		// İlk ayarlar yapılıyor

	LPC_PINCON->PINSEL0=0;	// Pinler GPIO olarak ayarlanıyr
	LPC_PINCON->PINSEL1=0;

	LPC_GPIO0->FIODIR = 0xFFFFFFFF;	// GPIO'ların hepsi çıkış oluyor
	// 12MHz CCLK için 1ms kesme için yükleme değeri
	// (12MHz/1000)-1 olur.
	SysTick->LOAD=(12000000/1000)-1;  // 1ms de bir kesme oluşacak
	// System Tick timer kesmesi önceliği belirleniyor
	NVIC_SetPriority (SysTick_IRQn, 15);
	// System Tick Timer Value değeri sıfırlanıyor
	SysTick->VAL =(0x00);
	// System Tick Timer açılıyor, kesme aktif, kaynak CCLK
	SysTick->CTRL = 7; 

	for (;;)
	{
		if(led_flag) LPC_GPIO0->FIOSET2 |=  0x40;
		else	     LPC_GPIO0->FIOCLR2 |=  0x40;
		// Herhangi başka bir görev
	}
}

System Tick Timer’ı kapamadan önce son bir şey söylemek istiyorum. Bildiğiniz üzere LPCExpresso kartına yalnızca kendisiyle birlikte gelen, benim için oldukça karışık bir IDE ile programlanıp, debug yapılabiliyor. Bunu gidermek için ise kartı ortadan ikiye ayırıp, mikrodenetleyici kısmına jtag çıkışı almak gerekiyor fakat LPCExpresso’ya sadece program atacaksanız, debug ile ilgilenmeyecekseniz.

Ayrıca ileriki zamanlarda C# ile güzel bir ARM hesaplayıcı yapmayı düşünüyorum. Bu konuda önerilere açığım..
Herkese bol ARM’lı günler, iyi çalışmalar..

  • Metin

    Güzel bir anlatım olmuş. Bu nadir bulunan konularda siz anlatanlar da haklı olarak herkesin anlayabilmesi için en basit şekilde anlatmaya çalışıyorsunuz. Fakat o zaman arada kopukluklar olduğunu düşünüyorum. Mesela System Tick Timer kullanarak belli aralıklarla bir ledi yakıp söndürmüşsünüz. Peki yapılan iş şu olsaydı; Diyelim ki program içerisinde birçok hesap yapıyoruz hesapların sonunda istediğimiz değerde ise x pininden çıkış veriyoruz daha sonra adc kanallarından gelen bilgileri okumak için bir miktar bekliyoruz daha sonra okuyoruz ve okuduğumuz bu değerlerle tekrar hesap, karşılaştırma vs işlemleri yapıyoruz. Burada System Tick Timer kullanmakla basit bir döngü ile aynı gecikmeyi sağlamak aynı şey değil mi?

    Dediğim gibi System Tick Timer’ın ne olduğunu nasıl kullanıldığını anlatmak istemişsiniz ve başarmışsınız da. Acaba birde asıl kullanım amacı ile küçük bir örnek alabilir miyiz?