ARM MCUlara başlarken

Başlatan MC_Skywalker, 11 Ekim 2012, 22:37:40

MC_Skywalker

Merhaba;

Elimde buluna ARM v7 çekirdekli ARM7 ailesinden NXP LPC2138 MCU lu deneme kartım bir kenarda boş boş durmayıp bir işe yaraması için atıldurduğu çekmeceden nihayet gün yüzüne çıkarttım

ilk ARM çekirdekli MCU lara başladığım zaman ki notlarımı kurcalayıp basit iki projeyi paylaşmak istedim. İlki proje Basit bir LED yak söndür projesi diğeri ise 0-9 sayıcı.


Proje 1.) LED yak söndür.

Bu projedeki amaç MCUnun P0.0 nolu pinine bağlı bir LED'i yakıp söndürmek. proje basit olduğu için PLL ayralarını KEIL içinde buluna ve proje olurşturma anında Keil tarafında projemize eklenen "Startup.s" dosyasın ki gibi kalacak. oyüzden ekstra bir SysInit fonksiyonu yazmadım.

KEIL uVision da "projecet" menüsünden "New uvision Projecet.." komutu ile projemizi oluşturmaya başlıyoruz. CPU seçim diyaloğundan üretici NXP(founded by Philips) LPC2138 seçip "OK" seçimizi onayladıktan sonra uVision un sorduğu "Startup.s" ekleyeyimis sorununu "YES" diyerek kabul edip

projemizi oluşturuyoruz. "File" menüsünden "New" komutu ile boş bir tex dosyayı oluşturup kodlarımızı yazmaya başlaya bilirizi.

#include <LPC21xx.H> 			//islemcinin kaynak dosyasi ekleniyor.
/* Gecikme Fonksiyonu */
void Gecikme (long int sure)
{
long int i,k;				// 16 bitlik i degiskeni tanimlaniyor.
for (i=1;i<=sure;i++) 			// girilen deger i degiskeninden büyük olana kadar for içerisinde dön
    {
        for (k=0;k<=12000;k++)
        {}
    }
}
/* Ana Fonksiyon */
int main (void)
{
IODIR0 = 0x00000001; 			//giris cikis ayarlama komutu 1 cikis 0 giris
                                        // 1. biti cikis digerleri giris
while (1)                               
    {					//sonsuz döngü
        IOSET0 = 0x00000001; 		//1. biti 1 yap
        Gecikme(500); 			//bekleme yap (yaklasik 500 msn - 12Mhz de)
        IOCLR0 = 0x00000001; 		//1. biti 0 yap
        Gecikme(500); 			//bekleme yap (yaklasik 500 msn - 12Mhz de)
    }
}


#include <LPC21xx.H>

#include komutu başlık dosyalarını veya diğer C, CPP, ASembler dosyalarını projemize dahil etmek için dir. PreProcesesor gibi tanımıda vardır.
<LPC21xx.H> başlık (Header) dosyası chip üreticisi (NXP) bu model için gerekli tanımlamaları bu dosya içinde sağlamıştır.

/* Gecikme Fonksiyonu */
void Gecikme (long int sure)
{
long int i,k;				// 16 bitlik i degiskeni tanimlaniyor.
for (i=1;i<=sure;i++) 			// girilen deger i degiskeninden büyük olana kadar for içerisinde dön
    {
        for (k=0;k<=12000;k++)
        {}
    }
}


Burada ilk fonksiyonumuzu yazıyoruz. Ana program main içinde kullanacağımız foknsiyonlar #include ve varsa #define komutlarından sonra ve mainden önce yazılır.

/*birden
fazla
satırlı
açıklama */

// Teksatır lık açıklma


Bu iki işaret program içindeki açılmalar için kulanılır.

{
}


Fonksiyon içinde ki yordamlar köşeli ayraçlar içine yazlılır.

;


komutlar ; işareti ile sonladırlımalıdır.

void Gecikme (long int sure)

Gecikme adlı fonksiyomumuzun içindeki bir değişken başka bir fonksiyondan değer alacağı için void ile tanımlanır. Fonksiyon sonundaki() değikenin türü ve adı içindir

i++ burada i değikenimizin içeriği 1 atrrılıp yeni değer yine i değişkenine atanıyor.


/* Ana Fonksiyon */
int main (void)
{
IODIR0 = 0x00000001; 			//giris cikis ayarlama komutu 1 cikis 0 giris
                                        // 1. biti cikis digerleri giris
while (1)                               
    {					//sonsuz döngü
        IOSET0 = 0x00000001; 		//1. biti 1 yap
        Gecikme(500); 			//bekleme yap (yaklasik 500 msn - 12Mhz de)
        IOCLR0 = 0x00000001; 		//1. biti 0 yap
        Gecikme(500); 			//bekleme yap (yaklasik 500 msn - 12Mhz de)
    }
}


while (1)


Ana programımız bir sonsuz döngü içersinde çalışır.

IODIR0 = 0x00000001;


IODIRx pinleri durumları ile ilgili olan kaydedici(register) ayarlamlarını bu komut vasıtası ile yapmaktayız her bir pin için 2 bitlik kaydedici alanı vardır. ARM işlemciler 32bit olduğu için çıkış pinleri ikiye bölünmüştür
0-15 bitler bir kadedici 16-31 bitler ayrı bir kaydedicidedir. işlemcinin kullanıcı kılavuzunza bakıp ayarlanmalı. üreticiden üreticiye kaydedici adı ve bit yapısı farklılık göstermektedir.

IOSET0 = 0x00000001;


IOSETx pinleri durumlarını 1 (hi) seviyesine ayarlar

Gecikme(500);


Gecikme(xxx) bir değer gönderip onu çalıştırıyoruz.

IOCLR0 = 0x00000001;


IOCLRx pinleri durumlarını temizler varsayılan değeri olan 0 (low) seviyesine ayarlar

Proje 2.) 7 segment led display ile 0-9 sayıcı.





#include <LPC21xx.H>
					
unsigned char segment[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};

/* Gecikme Fonksiyonu */
void Gecikme (long int sure)
{
  long int i,k; 				// 16 bitlik i degiskeni tanimlaniyor.
  for (i=1;i<=sure;i++) 			// girilen deger i degiskeninden büyük olana kadar for içerisinde dön
    {
		for (k=0;k<=12000;k++)
				{
				}
	}
}

int main(void)
{
	IODIR0 = 0x000000FF;   			// ilk 8. biti cikis digerleri giris
	while (1)
		{
		unsigned char s;
		for	(s=0; s<=10; s++)
			{
			Gecikme(250); 		//bekle
			IOPIN0 = segment[s];  	//7 segment display de değeri göster.
			}
		}
}





dilim döndüğünce anlatmaya çalıştım. Hatalarım olduysa affola. düzeltilmesi için ikazlarını esirgemeyin.

zamzam23

Eline sağlık. Derslerin devamı gelecek mi?

MC_Skywalker

#2
çok dağınık biri olduğum için çalışırken aldığım notlarımı toparladığım müddetçe paylaşacağım. 

Şimdi biraz programın başına yazılan SysInt fonksiyonunu açıklamak için birkaç satır aktaracağım. Daha önce forumda yazmıştım bir konuda toparlamak amacı ile buraya aynen kopyalıyorum


SystemInt fonksiyonu MCU içindeki işlemcimizin ilk çalıştırma paremetrtelerini yüklemek için. Normal şartlarda biz kendi main.c içine SystemInt ile ilgili herhangi bir tanımlama yapmadığımızada bu tanımlamalar MCU ya özel proje anında eklenen startupxxxxx.s dosyasından gelir.

ARM MCU lar ilk çalışmaya başladığında dahili içi RC osilatör ile çalışmaya başlar. Daha sonra ya startupxxxxx.s tanımlamalardan yada bizim main.c dosyamınız ilk kısmına yazdığımız kodlar vasıtası ile ve main fonksiyonu içinde bunu aktif ettiğimizde RC/Xtal/RTC ile çalışır ve iç PLL beslenip hangi frekansta koşacağı ayarlanır.

startupxxxxx.s dosyası genellikle mcu fizksel olark üreten firma tarafından hazırlanır ve OSC, NVIC vb. tanımlamalar bu dosyadadır. KEIL bunları uVison içine ekler.


Forumuzda ki örneklerde bulunan SystemInt kodlarına biraz daha yakından bakalım.

Bizim örneklerimizde sadece MCU çalışma frekansı ile ilgili tanımlamalar var. bunları belirli bir sırada işletilmesi gerekmektedir.

ilk adım PLL besleyecek saat darbesi üretcini seçmek RC/XTAL/RTC

RC = Chip üreticisi tarafından MCU içine kuonulan Direnç ve Kondasatörden oluşan saat darbesi üreteci
XTAL = XTAL pinlerine bağlanan harici XTAL tabanlı saat darbesi üreteci
RTC = MCU içindeki Gerçek zaman saati devresi besleyen RTCXTAL pinlerine bağlanan harici XTAL tabanlı saat darbesi üreteci

ikinci adım seçilen kaynağın PLL e bağlanması
üçüncü adım PLL çarpanının belirlenmesi
dördüncü adım PLL çıkışının Cortex-M çekirdeğine yönlendirilmesi.

Aşağıda NXP firmasının ürettiği LPC1768 işlemcisi için kullandığım OSC ayar rutinlerini içiren kod parçası bulunmakta. her satıra tek tek açıklama yazmaya çalıştım. ARMv7 işlemcileri içinde bu kodlar kulanır fakat şeçtiğimiz chip'in kullancı klavuzunu okuyarak XTAL  seçimi, PLL besleme, PLL aktif vb. etme detaylarını öğrenmekte fayda var.

void SyatemInt(void)
{
    LPC_SC->SCS=0x20;				                //System Control and Status R/W 0 0x400F C1A0 5. bit 1 yapılıp xtal osc (main) aktif ediliyor
						                                //OSCRANGE 4.bit ---> 0 ise 1Mhz ile 2Mhz arası 1 ise 15Mhz ile 25Mhz arası    
						                                //OSCEN 5.bit --->	0 ise devredışı 1 ise xtal uçlarına bağlı kristali kullanır
							                        //OSCSTAT 6.bit --->	0 ise osilatör hazır değil 1 ise osilatör hazır (sadece okunur)
    LPC_SC->CLKSRCSEL=0x01;				//select main oscilator	CLKSRCSEL 2 bit uzunluğunda
							                        //00 iç RC kaynağı osilatör olarak seçilmiş
							                        //01 ana osilatörü PLL0 için kaynak seç							  
							                        //10 iç RTC (gerçek zaman saati) osilatör olarak seç
							                        //11 bu durum kullanımaz rezerve edilmiş bir değerdir.
    LPC_SC->CCLKCFG=0x03;				        //pllclk/4
    LPC_SC->PLL0CON=0x01;				        //pll activeted
    LPC_SC->PLL0CFG=0x20031; 			        //msel=49,nsel=2;
    LPC_SC->PLL0FEED=0xAA;				        //pll i beslemek için yazılmalı datasheet te belirtiliyor
    LPC_SC->PLL0FEED=0x55;				        //önce 0xAA sonra 0x55 yüklenir
    while(!(LPC_SC->PLL0STAT&(1<<26)));
    LPC_SC->PLL0CON=0x07;
    LPC_SC->PLL0FEED=0xAA;			               //pll i beslemek için yazılmalı datasheet te belirtiliyor
    LPC_SC->PLL0FEED=0x55;				      //önce 0xAA sonra 0x55 yüklenir
    LPC_SC->PCLKSEL0=0;
}