C18 Düşük Öncelikli Kesmelerde Yedek Almak

Başlatan Tagli, 06 Ocak 2011, 21:15:45

Tagli

C18'in rehberinde yüksek öncelikli kesmelerde WREG, BSR ve STATUS'un otomatik olarak yedeklendiği söyleniyor (aslında zaten bunu donanım yapıyor). Ama anladığım kadarıyla düşük öncelikli kesmelerde bunu kendimiz yapmamız gerekiyor, çünkü bu yedekleyicilerin derinliği 1, yani ikinci gelen birincinin üstüne biniyor. Benim düşük öncelikli kesmenin başında bu 3 register'ı kendi tanımladığım 3 değişkene atıp, çıkarken de geri yüklemem yeterli midir? Peki ya ben yedeklemeyi yapamadan yüksek öncelikli kesme gelirse? Acaba düşük öncelikli kesmenin başında yedekleme işlemini bitirene kadar yüksek öncelikli kesmeleri maskelesem mi? Bu tür bir kullanım sağlıklı olur mu?
Gökçe Tağlıoğlu

iyildirim

#1
Low level int' lar da WREG, BSR, STATUS registerleri software stack'e  atılarak kaydedilirken high level int' lar da  bu üç register  daha hızlı olması için shadow registerler kullanılarak kaydediliyor. Software stack'e kaydetmek shadow registerlere göre fazladan 10 word daha kullanmayı gerektiriyormuş.

Yani hem low hemde high level int'lar da bu üç register kaydediliyor. Özel bir şey yapmaya gerek yok.

Asıl sorun; shadow registerler tek seviyeli oldukları için,  high level int içerisinde iken ikinci bir high level int gelmesi. Bu yüzden high level int'lar single-instance olup nested çalıştırılamıyorlar. İkinci bir high level int gelirse ilkinin geri dönüş bilgileri kayboluyor.
Low level int'lar ise software stack boyutunun izin verdiği ölçüde nested olabiliyor. (kesme içerisinde iken başka bir kesme çağrılabiliyor.)

High level interruptlar çok hızlı tepki vermek gereken, örneğin komparatordan akım fazla oldu sinyali alıp PWM' i kesmek gibi durumlar için tasarlanmış sanırım.

Tagli

#2
Teşekkürler, yani düşük öncelikli kesme de kayıt işini kendisi hallediyor ancak donanım desteği olmadan. Peki yukarıda bahsetiğim kayıt işinin ortasında yüksek öncelikli kesme gelmesi olayını da hallediyor mu?

Ayrıca anladığım kadarıyla yedeği alınan başka şeyler de var. C18 Rehberi şöyle demiş:
Alıntı YapFor a high-priority interrupt, the location specified in the nosave= clause may be one of the following: FSR0, TBLPTR, TBLPTRU, TABLAT, PCLATH, PCLATU, PROD, section(".tmpdata"), or section("MATH_DATA").
Bunlardan gerekmeyenlerin yedeklenmesini nosave ile engelleyebiliyoruz. Peki normal işleyiş sırasında derleyici hangilerini kullanıyor, hangilerini kullanmıyor? Daha doğrusu bu register'ları ne amaçla kullanıyor? Aslında bunların ne işe yaradıklarını da az buçuk biliyorum ama derleyicinin bunları nasıl kullandığından emin değilim.

Anladığım kadarıyla düşük öncelikli kesmenin kullanmadığı değerleri belirlemem gerekiyor nosave'i kullanmak için. Benim kesme PORTB pinleri değişimi kesmesi. Bunu B4 ve B5 bacaklarından quadrature encoder okumak için kullanacağım. Kodu şu şekilde:
#pragma interruptlow updateEncoder
void updateEncoder(void){
	unsigned char encNew, sum;
	
	encNew = (PORTB >> 4) && 0b00000011;
	sum = encOld + encNew;
	
	if (sum == 1 || sum == 7 || sum == 14 || sum == 8){
		encCounter++;
	}
	else if (sum == 11 || sum == 13 || sum == 4 || sum == 2){
		encCounter--;
	}
	
	encOld = (encNew << 2) && 0b00001100;
	INTCONbits.RBIF = 0;
	return;	
}
FSR0 hakkında hiç bir fikrim yok ama bana sanki diğerleri kullanılmaz gibi geliyor. Belki de assembly koduna bakmalıyım ama üşendim açıkçası...
Gökçe Tağlıoğlu

iyildirim

Donanım desteği olmadan derken; aslında donanımsal pek bir farkı yok. Farkı yüksek öncelikli kesme de kayıt için yine registerler kullanılırken, düşük öncelikli kesme de kayıt, RAM üzerinde oluşturulan software stack'a yapılıyor.
Bu üç register (WREG, BSR, STATUS ) için iki kesme yönteminde de ayrı bir kayıt işlemi gerekmiyor.

Bunun dışında sizin de bahsettiğiniz gibi diğer registerler, yada belli bir bellek bölümü, yada programınızda tanımladığınız herhangi bir değişkenin de yedeğinin alınması sağlanabiliyor. Nelerin yedeğinin alınacağı da tamamen sizin yazılımınızı nasıl oluşturduğunuza, algoritmanıza bağlı. Kesme içerisinde de kullanılan ancak kesme çağrıldığı anda ki değerleri kesme den dönüldüğünde tekrar istenen değişkenler, registerlerin kaydedilmesi uygun olur. Emin olmak ve minimum saat çevrimi kaybı için disassembler list'e bakıp C kodunuzun ASM karşılığında, kesme içerisinde hangi registerler kullanılıyoru incelemek gerekir.

Kesme çağrıldığı anda yapılanlar la ilgili kısım da çok detaylı (kesme çağrılma aşamasında iken bir başka kesme araya girebilirmi ) anlatılmamış. Kullanılan mcu nun kesmeyi nasıl çağırdığı ile ilgili kısımlara da bakmak lazım.  Ancak sanırım bir kesme çağrılma aşamasında iken bir başka kesmenin aktif olması engelleniyor. Bu C18 de veya donanımsal olarak mcu içerisinde hallediliyor. Stack'e nelerin atılacağını belirlediğimize göre C18 içerisinde hallediliyor olmalı. Aksi durumda bol kesmeli bir yazılımın sağlıklı çalışması garanti edilemezdi. Bunu garanti etmeyen bir C derleyicisi de çok anlamlı olmazdı.


camby

Alıntı yapılan: Tagli - 06 Ocak 2011, 21:15:45
C18'in rehberinde yüksek öncelikli kesmelerde WREG, BSR ve STATUS'un otomatik olarak yedeklendiği söyleniyor (aslında zaten bunu donanım yapıyor)

Alıntı yapılan: iyildirim - 07 Ocak 2011, 04:15:22
Low level int' lar da WREG, BSR, STATUS registerleri software stack'e  atılarak kaydedilirken high level int' lar da  bu üç register  daha hızlı olması için shadow registerler kullanılarak kaydediliyor. Software stack'e kaydetmek shadow registerlere göre fazladan 10 word daha kullanmayı gerektiriyormuş.

Bu shadow register nasıl çalışıyor ? Ben hiç farketmedim şimdiye kadar böyle bir konuyu , Yüksek kesmeye girişte sürekli olarak WREG , BSR , STATUS yedekliyorum.

ORG 		0x00			
		GOTO 		AYAR
		ORG		0x08
		GOTO		YUKSEK_KESME
		ORG 		0x18			
		GOTO 		DUSUK_KESME

;=============== Yüksek Kesme ============================================
;{
YUKSEK_KESME	movwf 		W_TEMP_1 		
		movff 		STATUS, STATUS_TEMP_1 	
		movff 		BSR, BSR_TEMP_1	
.....
..


gerek yok mu bunu yapmaya ?

Tagli

Evet, gerek yok. Bu registar'ların saklanması kesme girişinde otomatik olarak yapılıyor. Ancak geri dönüşte eski yerlerine geri yüklenebilmeleri için "RETFIE 1" şeklinde komutun yanına "1" argümanını da vererek kullanmalısın. Benzer kullanım CALL ve RETURN için de geçerli, ama aşağıda bahsedeceğim sebepten dolayı tavsiye etmiyorum.

Dikkat edilmesi gereken nokta şu ki, bu yedekleyici register'lar tek seviyeli. Yani son gelen değer eskisini siliyor. Bu sebeple, düşük öncelikli bir kesme sırasında yüksek öncelikli bir kesme gelirse, yedek regisler'ları değişeceği için, düşük öncelikli kesmeye girmeden kaydedilen değerler kaybedilir. CALL ile kullanılmasını tavsiye etmememin nedeni de bu. Kesme her an gelebilir ve CALL'un aldığı yedeği bozabilir. Elbette uygun şekilde kesmeleri kapatmak gibi değişik yollar da izlenebilir, bu tasarıma bağlı.

Genel olarak, yüksek öncelikli kesmelerde bu 3 register için otomatik yedekleme işlemine güvenilebilir ama düşük öncelikli kesmelerde yedeklemeyi eski usül elle yapmak gerekiyor.

Bu arada, Microchip'in bu yedekleme işini neden 2 seviyeli yapmadığını da merak etmişimdir hep. İşi yapmışken tam yapsalardı keşke.
Gökçe Tağlıoğlu