Merhabalar, bir önceki yazımda ARM asselmbly diline giriş niteliğinde olan LED yakıp söndürme uygulamasından bahsetmiştim. Bu seferki yazımda ise yine temel bir uygulama niteliğinde olan buton okuma uygulaması yapıp Cortex-M0 işlemcisinin iç yapısı hakkında bilgi vereceğim.

Yine söylenmeyeni söylemeye, yazılmayanı yazmaya devam ediyoruz…

Bir önceki uygulama kodlarını hatırlarsanız kodlarda R0,R6 gibi dahili değişkenleri kullandık. Peki nedir bu dahili değişkenler? Niye dahilidir? Hangisini kullanacağız gibi sorular aklınıza gelebilir. Şimdi bu değişkenleri ele alalım…

ARM® Cortex®-M0 Kaydedicileri

İşlemciler ile assembly seviyesinde kod yazanların hatırlayacağı üzere her işlemcinin kendine ait özel veya genel amaçlı dahili kaydedicileri vardır. Dahili kaydedicilerin ayırt edici özelliği ise işlemci tarafından komutlar ile direk olarak işlem görebilmesidir. Örneğin harici bellekteki(Ör:RAM bellek ) 2 değişkeni toplamak için işlemcinin komut kümesine göre bu değişkenlerden en az birinin dahili değişkenlere aktarılması gerekir. Yani her durumda dahili değişkenler işlemcinin komutları ile birlikte kullanılmaktadır. Bu nedenle özellikle assembly ile kod yazarken işlemcinin dahili kaydedicilerini iyi tanıyıp ona göre kodlarımızı yazmamız gerekir. C gibi yüksek seviyeli dillerde, derleyiciler zaten arka planda bu değişkenleri verimli bir şekilde kullanmaktadır.

Şekil-2

Şekil-2

Şekil-2’de Cortex-M0 işlemci çekirdeğine ait dahili çekirdek kaydedicileri görülmektedir. Değişkenleri kısım kısım inceleyecek olursak;

R0-R7 Düşük Kaydediciler(Low Registers)

R0-R7 kaydedicileri low registers olarak anılırlar. Genel amaçlı kaydedicilerdir. İşlemcinin tüm komutları ile birlikte kullanılabilirler. Örneğin programınızda sürekli eriştiğiniz bir sayaç değişkeniniz varsa, bu kaydedicilerden herhangi birini kullanıp işlemlerinizi hızlı bir şekilde yapabilirsiniz.

R8-R12 Yüksek Kaydediciler(High Registers)

R8-R12 kaydedicileri high registers olarak anılır. Bu kaydedicilerde genel amaçlı kaydedicilerdir. Fakat mimari gereği tüm komutlar ile birlikte kullanılamazlar. Kullanmadan önce komut setini incelemenizde fayda var.

R13, Yığın İşaretçisi (Stack Pointer, SP)

R13 kaydedicisi aynı zamanda Stack Pointer olarak anılır. Adından da anlaşılacağı üzere R13 kaydedicisi Stack hafızasının o anki kullanılan adresini tutar. PUSH ve POP komutları ile Stack alanına okuma/yazma yapıldığında R13 kaydedicisinde ki adres değeri de değişir. Başlangıç seviyesindeki kullanıcıların R13 kaydedicisine fazla kafa yormalarına gerek yoktur.

Diğer bir nokta ise şekil-2’de görüldüğü gibi Cortex-M0 işlemcisinde Ana Yığın İşaretçisi (MSP, Main Stack Pointer) ve İşlem Yığın İşaretçisi (PSP, Process Stack pointer) olmak üzere iki adet yığın işaretçisi bulunmaktadır. İşlemci tarafından her iki işaretçi kullanılabilir. Varsayılan olarak MSP seçilmiştir. Bizde uygulamalarımızda MSP’yi kullanacağız. İki adet işaretçi bulunması Cortex-M0 işlemcisinin gelişmiş mimarisinden dolayıdır. İşletim sistemi, RTOS vb. görevler halinde programların çalıştırıldığı uygulamalarda PSP işlemleri kolaylaştırır performansı arttırır, bir çok fayda sağlar.

R14, Bağlantı Kaydedicisi (Link Register, LR)

R14, Link register olarak anılır. R14, programda alt program yada fonksiyon çağrıldığında geri dönüş adresini tutar. Alt program yada fonksiyon tamamlandığında LR değişkenin tuttuğu adrese geri dönülerek program kaldığı yerden devam eder.

R15, Program Sayacı (Program Counter, PC)

R15 kaydedicisi adından da anlaşılacağı üzere işlemcinin icra ettiği o anki komutların adresini tutar. Okunup yazılabilen bir kaydedicidir. Okunması durumunda o anki komutun değeri +4 (mimari gererği)değerini döndürür.

Program Durum Kaydedicisi (PSR, Program Status Register)

PSR, işlemci durum kaydedicisidir. APSR(Application PSR, Uygulama), IPSR(Interrupt PSR, Kesme) ve EPSR(Execution PSR, Çalıştırma) durum kaydedicilerinin birleşiminden oluşur.

IPSR, o anki çalıştırılan kesme servisinin numarasını tutar.

EPSR, içerisinde sadece T biti bulunur. Bu bit işlemcinin Thumb komut seti ile çalıştığını belirtir. Cortex-M0 sadece Thumb komutlarını desteklediği için bu bitin sıfırlanması hard fault exception(Donanım hatası kesmesi) oluşmasına neden olacaktır.

APSR ise işlemcinin ALU(Aritmetik Lojik Birimi) biriminin işlem sonuçlarına dair durumlarını tutar. İçerisinde N(Negatif bayrağı), Z(Sıfır bayrağı), C(Elde veya borç bayrağı) ve V(Taşma bayrağı) bayrak bitleri bulunmaktadır. Örneğin işlemci yaptığı çıkarma işlemi sonucunda sonuç sıfır olursa Z bayrağı set edilir. Aynı şekilde eğer sonuç negatif olursa negatif bayrağı ve borç bayrağı set edilir. Bu bayraklar çoğu işlemcilerde bulunup, işlemcilerin vazgeçilmez bayrak bitleridir. Uygulamalarda bayrak sonuçlarına programın akışı kontrol edilir.

Kullandığınız işlemciye efektif kodlar yazabilmek için işlemcinizin mimarisini iyi tanımanız çok önemlidir. Cortex-M0 serinin en küçük işlemcisi de olsa bir çok detayı mevcuttur. Detaylı tanımak için kullanma kılavuzunu iyi incelemek gerekir. Cortex-M0 ile ilgili detaylı bilgiye buradan ulaşabilirsiniz.

Buton Uygulaması

Bu kadar anlatımdan sonra uygulamamıza geçebiliriz. Buton uygulaması yine aynı kart üzerinde gerçekleştirilmiştir. Kart üzerinde bulunan GPIOA.0 pinine bağlı buton kullanılmıştır. Program temel olarak butona basılması tutulması durumunda GPIOC.8 ve GPIOC.9 pinlerine bağlı LED’leri yakar, bırakılması durumunda söndürür. Uygulamaya ait kodlar aşağıdadır.

;****************************************************************************
; Uygulama : Button Test	                	       		    *
; Yazan	   : Erhan YILMAZ			                	    *
; E-posta  : erhanyilmaz.ytu@gmail.com		                            *
; Tarih	   : 14-06-2015				                            *
; Sürüm	   : 1.0					                    *
; Açıklama : Cortex M0 işlemcisine sahip STM32F051R8 denetleyicisi	    *
; 	     için assembly dilinde yazılmış Buton okuma uygulamasıdır.      *
; 	     Geliştirme kartı olarak STm32F0 Discovery kullanılmıştır.	    *
;****************************************************************************
 
Stack_Size      EQU     0x00000400	;Stack boyutu tanımlanıyor.(1kb)
 
; Genel amaçlı bit değerleri için tanımlamalar yapılıyor.
BIT0	EQU	0x00000001
BIT1	EQU	0x00000002
BIT2	EQU	0x00000004
BIT3	EQU	0x00000008
BIT4	EQU	0x00000010
BIT5	EQU	0x00000020
BIT6	EQU	0x00000040
BIT7	EQU	0x00000080
BIT8	EQU	0x00000100
BIT9	EQU	0x00000200
BIT10	EQU	0x00000400
BIT11	EQU	0x00000800
BIT12	EQU	0x00001000
BIT13	EQU	0x00002000
BIT14	EQU	0x00004000
BIT15	EQU	0x00008000
BIT16	EQU	0x00010000
BIT17	EQU	0x00020000
BIT18	EQU	0x00040000
BIT19	EQU	0x00080000
BIT20	EQU	0x00100000
BIT21	EQU	0x00200000
BIT22	EQU	0x00400000
BIT23	EQU	0x00800000
BIT24	EQU	0x01000000
BIT25	EQU	0x02000000
BIT26	EQU	0x04000000
BIT27	EQU	0x08000000
BIT28	EQU	0x10000000
BIT29	EQU	0x20000000
BIT30	EQU	0x40000000
BIT31	EQU	0x80000000
 
; Kullanılan kaydedicilerin adresleri tanımlanıyor.
RCC_BASE	EQU	0x40021000
RCC_AHBENR	EQU	RCC_BASE + 0x14
 
GPIOA_BASE	EQU	0x48000000
GPIOA_MODER	EQU	GPIOA_BASE + 0x0
GPIOA_OTYPER	EQU	GPIOA_BASE + 0x4
GPIOA_OSPEEDR	EQU	GPIOA_BASE + 0x8
GPIOA_PUPDR	EQU	GPIOA_BASE + 0xc
GPIOA_IDR	EQU	GPIOA_BASE + 0x10
GPIOA_ODR	EQU	GPIOA_BASE + 0x14
GPIOA_BSRR	EQU	GPIOA_BASE + 0x18
GPIOA_LCKR	EQU	GPIOA_BASE + 0x1C
GPIOA_AFRL	EQU	GPIOA_BASE + 0x20
GPIOA_AFRH	EQU	GPIOA_BASE + 0x24
GPIOA_BRR	EQU	GPIOA_BASE + 0x28
 
GPIOC_BASE	EQU	0x48000800
GPIOC_MODER	EQU	GPIOC_BASE + 0x0
GPIOC_OTYPER	EQU	GPIOC_BASE + 0x4
GPIOC_OSPEEDR	EQU	GPIOC_BASE + 0x8
GPIOC_PUPDR	EQU	GPIOC_BASE + 0xc
GPIOC_IDR	EQU	GPIOC_BASE + 0x10
GPIOC_ODR	EQU	GPIOC_BASE + 0x14
GPIOC_BSRR	EQU	GPIOC_BASE + 0x18
GPIOC_LCKR	EQU	GPIOC_BASE + 0x1C
GPIOC_AFRL	EQU	GPIOC_BASE + 0x20
GPIOC_AFRH	EQU	GPIOC_BASE + 0x24
GPIOC_BRR	EQU	GPIOC_BASE + 0x28
 
; Stack için yer ayrılıyor.
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
 
                PRESERVE8	; Linker bildirimi
                THUMB		; Thumb komut seti kullanılıyor.
 
; Vector Table Mapped to Address 0 at Reset
; Vektör tablosu tanımlanıyor.
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors	 ; Linker bildirimi
; İşlemci tarafından kullanılan kesmeler(exceptions) handler'lar ve stack top adresi tanımlanıyor.
__Vectors       DCD     __initial_sp                   ; Top of Stack
                DCD     Reset_Handler                  ; Reset Handler
                DCD     NMI_Handler                    ; NMI Handler
                DCD     HardFault_Handler              ; Hard Fault Handler
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     SVC_Handler                    ; SVCall Handler
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     PendSV_Handler                 ; PendSV Handler
                DCD     SysTick_Handler                ; SysTick Handler
; External Interrupts
; Denetleyici tarfından kullanılan kesmeler(exceptions) için handler'lar tanımlanıyor.
                DCD     WWDG_IRQHandler                ; Window Watchdog
                DCD     PVD_IRQHandler                 ; PVD through EXTI Line detect
                DCD     RTC_IRQHandler                 ; RTC through EXTI Line
                DCD     FLASH_IRQHandler               ; FLASH
                DCD     RCC_IRQHandler                 ; RCC
                DCD     EXTI0_1_IRQHandler             ; EXTI Line 0 and 1
                DCD     EXTI2_3_IRQHandler             ; EXTI Line 2 and 3
                DCD     EXTI4_15_IRQHandler            ; EXTI Line 4 to 15
                DCD     TS_IRQHandler                  ; TS
                DCD     DMA1_Channel1_IRQHandler       ; DMA1 Channel 1
                DCD     DMA1_Channel2_3_IRQHandler     ; DMA1 Channel 2 and Channel 3
                DCD     DMA1_Channel4_5_IRQHandler     ; DMA1 Channel 4 and Channel 5
                DCD     ADC1_COMP_IRQHandler           ; ADC1, COMP1 and COMP2 
                DCD     TIM1_BRK_UP_TRG_COM_IRQHandler ; TIM1 Break, Update, Trigger and Commutation
                DCD     TIM1_CC_IRQHandler             ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler                ; TIM2
                DCD     TIM3_IRQHandler                ; TIM3
                DCD     TIM6_DAC_IRQHandler            ; TIM6 and DAC
                DCD     0                              ; Reserved
                DCD     TIM14_IRQHandler               ; TIM14
                DCD     TIM15_IRQHandler               ; TIM15
                DCD     TIM16_IRQHandler               ; TIM16
                DCD     TIM17_IRQHandler               ; TIM17
                DCD     I2C1_IRQHandler                ; I2C1
                DCD     I2C2_IRQHandler                ; I2C2
                DCD     SPI1_IRQHandler                ; SPI1
                DCD     SPI2_IRQHandler                ; SPI2
                DCD     USART1_IRQHandler              ; USART1
                DCD     USART2_IRQHandler              ; USART2
                DCD     0                              ; Reserved
                DCD     CEC_IRQHandler                 ; CEC
                DCD     0                              ; Reserved
 
                AREA    MAIN, CODE, READONLY
; Reset handler routine
; Reset sonrası başlangıç adresi
Reset_Handler    
                EXPORT  Reset_Handler                
start			
; GPIOA, GPIOC saatleri aktif
	LDR R0, =RCC_AHBENR	; RCC_AHBENR değerini R0'a yükle
	LDR R1, =BIT19+BIT17	; BIT19+BIT17 değerini R1'e yükle
	STR R1, [R0]	        ; RCC_AHBENR harici değişkenine BIT19+BIT17 değerini yükle
; GPIOC.8, GPIOC.9 çıkış				
	LDR R0, =GPIOC_MODER	; GPIOC_MODER değerini R0'a yükle
	LDR R1, =BIT16+BIT18	; BIT16+BIT18 değerini R1'e yükle
	STR R1, [R0]		; GPIOC_MODER harici değişkenine BIT16+BIT18 değerini yükle
LOOP				
	LDR  R0, =GPIOA_IDR	; GPIOA_IDR değerini R0'a yükle
	LDR  R1, [R0]		; GPIOA_IDR harici değişkenini R1 dahili değişkenine yükle
	MOVS R0, #BIT0		; R0=BIT0
	TST  R0, R1		; R0 & R1 (bit0 test ediliyor)
	BNE	 L1		; Sonuç 0 değilse L1'e git
; GPIOC.8, GPIOC.9 çıkışlarını sıfırla				
	LDR R0, =GPIOC_BRR	; GPIOC_BRR değerini R0'a yükle
	LDR R1, =BIT8+BIT9	; BIT8+BIT9 değerini R1'e yükle
	STR R1, [R0]		; GPIOC_BRR harici değişkenine BIT8+BIT9 değerini yükle
	B LOOP			; LOOP adresine dallan
L1				
; GPIOC.8, GPIOC.9 çıkışlarını set et
	LDR R0, =GPIOC_BSRR	; GPIOC_BSRR değerini R0'a yükle
	LDR R1, =BIT8+BIT9	; BIT8+BIT9 değerini R1'e yükle
	STR R1, [R0]		; GPIOC_BSRR harici değişkenine BIT8+BIT9 değerini yükle
	B LOOP			; LOOP adresine dallan
 
; Dummy Exception Handlers (infinite loops which can be modified)
; Kesme(exceptions) vektörleri için tanımlanmış iç boş sonsuz döngüye giren handler'lar
NMI_Handler     
        B .
HardFault_Handler
        B .
SVC_Handler     
        B .
PendSV_Handler  
        B .
SysTick_Handler 
        B .
WWDG_IRQHandler
PVD_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_1_IRQHandler
EXTI2_3_IRQHandler
EXTI4_15_IRQHandler
TS_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_3_IRQHandler
DMA1_Channel4_5_IRQHandler
ADC1_COMP_IRQHandler 
TIM1_BRK_UP_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM6_DAC_IRQHandler
TIM14_IRQHandler
TIM15_IRQHandler
TIM16_IRQHandler
TIM17_IRQHandler
I2C1_IRQHandler
I2C2_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
CEC_IRQHandler   
 
        B .
	ALIGN	; Adres hizalama Assembler bildirimi
	END	; Program sonu assmbler bildirimi

Görüldüğü üzere uygulama kodları yapı itibari ile bir önceki LED uygulaması kodlarına büyük oranda benzemektedir. Diğer uygulamadan öenmli farkı ise buton okuma işlemidir. Buton okuması için harici hafızada bulunan GPIOA_IDR değişkenin okunması gerekmektedir.

LDR  R0, =GPIOA_IDR	; GPIOA_IDR değerini R0'a yükle
LDR  R1, [R0]		; GPIOA_IDR harici değişkenini R1 dahili değişkenine yükle
MOVS R0, #BIT0		; R0=BIT0
TST  R0, R1		; R0 & R1 (bit0 test ediliyor)
BNE	 L1		; Sonuç 0 değilse L1'e git

Yukarıda kod parçasını inceleyecek olursak; İlk iki satırda GPIOA portunun değerini tutan harici GPIOA_IDR değişkeni R1 dahili değişkenine aktarılmaktadır. Bu sayede A portunun o anki değerlerini dahili değişkene aktarılmıştır. Bu yapı genel bir yapı olmakla birlikte herhangi bir harici adresden veri okunup dahili değişkenlere yazmak için kullanılabilir.

A portunun tüm değerlerini okuduktan sonra butonun bağlı olduğu 0. bitin değerini öğrenilmesi gerekir. Bunun için öncelikle R0 değişkenine BIT0(0x01) değeri yüklenir ve TST komutu ile R0 ve R1 değişkenleri AND işlemine tabi tutulur. Bu işlem sonucu eğer sıfır ise butona basılmış(Pull-Up kullanıldı.) demektedir. İşlem sonucu sıfır değilse butona basılmamış demektir. Önceden bahsettiğim gibi sonucun sıfır olması Z bayrağını set eder. BNE komutu ise Z bayrağına bakar. Eğer sonuç sıfır değilse belirtilen adrese gider. Benzer şekilde BEQ komutu ise sonuç sıfır ise belirtilen adrese gider. Bu temel yapıyı kullanarak istediğiniz herhangi bir değişkenin bitlerinin durumlarını test edip programınızı yönlendirebilirsiniz.

BNE, BEQ vs. gibi komutlar koşullu dallanma komutlarıdır. Programa yön vermek açısından önemli komutlardır. Cortex-M0 işlemcisinde bir çok koşullu dallanma komutu mevcuttur. Detaylı bilgi için işlemcinin kullanma kılavuzuna bakılmalıdır.

Diğer belirtmek istediğim bir nokta ise görüldüğü gibi komutlarda büyük küçük harf duyarlılığı yoktur. Benzer şekilde R13-R15 gibi kaydediciler özel isimleri (SP, LR, PC) ile kullanılabilir. Derleyici bu isimleri otomatik olarak algılar.

Şekil-3

Şekil-3’te uygulamanın çalışma görüntüsü görülmektedir. Böylelikle ARM assembly dilinde 2. temel uygulamamız olan buton okuma uygulamasını gerçekleştirmiş olduk. Teorik olarak bu aşamadan sonra ARM assembly dili ile istediğiniz kodu yazabilirsiniz. 🙂 Uygulamaya ait kodlara proje halinde buradan ulaşabilirsiniz. Yazı ile ilgili eksik yerleri, görüş ve önerilerinizi bildirirseniz sevinirim. Bir sonraki yazıda görüşmek üzere inşaallah…