Kesme işleyicileri yazma¶
Uygun donanım üzerinde MicroPython, kesme işleyicilerini Python ile yazma olanağı sunar. Kesme işleyicileri - kesme hizmet rutinleri (ISR’ler) olarak da bilinir - geri çağırma (callback) fonksiyonları olarak tanımlanır. Bunlar, bir zamanlayıcı tetiklenmesi veya bir pin üzerindeki gerilim değişikliği gibi bir olaya yanıt olarak yürütülür. Bu tür olaylar, program kodunun yürütülmesinin herhangi bir noktasında meydana gelebilir. Bu durum, bazıları MicroPython diline özgü olan önemli sonuçlar doğurur. Diğerleri ise gerçek zamanlı olaylara yanıt verebilen tüm sistemlerde ortaktır. Bu belge önce dile özgü konuları, ardından konuya yeni başlayanlar için gerçek zamanlı programlamaya kısa bir giriş sunmaktadır.
Bu giriş, “yavaş” veya “mümkün olduğunca hızlı” gibi belirsiz terimler kullanmaktadır. Bu, hızlar uygulamaya bağlı olduğu için bilinçli bir tercihtir. Bir ISR için kabul edilebilir süreler, kesmelerin oluşma hızına, ana programın doğasına ve diğer eşzamanlı olayların varlığına bağlıdır.
İpuçları ve önerilen uygulamalar¶
Bu bölüm aşağıda ayrıntılı olarak açıklanan noktaları özetler ve kesme işleyici kodu için temel önerileri listeler.
Kodu mümkün olduğunca kısa ve basit tutun.
Bellek ayırmaktan kaçının: listelere ekleme veya sözlüklere yerleştirme yapmayın, kayan nokta kullanmayın.
Yukarıdaki kısıtlamayı aşmak için
micropython.schedulekullanmayı düşünün.Bir ISR’nin birden fazla bayt döndürdüğü durumlarda, önceden ayrılmış bir
bytearraykullanın. Bir ISR ile ana program arasında birden fazla tam sayı paylaşılacaksa, bir dizi (array.array) kullanmayı düşünün.Veriler ana program ile bir ISR arasında paylaşıldığında, ana programda verilere erişmeden önce kesmeleri devre dışı bırakmayı ve hemen ardından yeniden etkinleştirmeyi düşünün (Kritik Bölümler kısmına bakın).
Bir acil durum istisna arabelleği ayırın (aşağıya bakın).
MicroPython sorunları¶
Acil durum istisna arabelleği¶
Bir ISR’de hata oluşursa, bu amaçla özel bir arabellek oluşturulmadığı sürece MicroPython bir hata raporu üretemez. Kesme kullanan herhangi bir programa aşağıdaki kod dahil edilirse hata ayıklama kolaylaşır.
import micropython
micropython.alloc_emergency_exception_buf(100)
Acil durum istisna arabelleği yalnızca bir istisna yığın izini tutabilir. Bu, yığın (heap) kilitliyken bir istisnanın işlenmesi sırasında ikinci bir istisna fırlatılırsa, ikinci istisna temiz bir şekilde işlense bile, ikinci istisnanın yığın izinin orijinalinin yerini alacağı anlamına gelir. Arabellek daha sonra yazdırılırsa bu durum kafa karıştırıcı istisna mesajlarına yol açabilir.
Basitlik¶
Çeşitli nedenlerle ISR kodunu mümkün olduğunca kısa ve basit tutmak önemlidir. Yalnızca onu tetikleyen olaydan hemen sonra yapılması gereken işlemleri yapmalıdır: ertelenebilecek işlemler ana program döngüsüne devredilmelidir. Tipik olarak bir ISR, kesmeye neden olan donanım aygıtıyla ilgilenir ve onu bir sonraki kesmenin oluşmasına hazır hale getirir. Kesmenin meydana geldiğini belirtmek üzere paylaşılan verileri güncelleyerek ana döngüyle iletişim kurar ve geri döner. Bir ISR, denetimi mümkün olduğunca hızlı bir şekilde ana döngüye geri vermelidir. Bu, MicroPython’a özgü bir sorun olmadığından aşağıda daha ayrıntılı olarak ele alınmıştır.
Bir ISR ile ana program arasında iletişim¶
Normalde bir ISR’nin ana programla iletişim kurması gerekir. Bunu yapmanın en basit yolu, global olarak bildirilen veya bir sınıf aracılığıyla paylaşılan bir veya daha fazla paylaşılan veri nesnesidir (aşağıya bakın). Bunu yapmanın çeşitli kısıtlamaları ve tehlikeleri vardır ve bunlar aşağıda daha ayrıntılı olarak ele alınmaktadır. Çeşitli veri türlerini saklayabilen diziler (array modülünden) ile birlikte tam sayılar, bytes ve bytearray nesneleri bu amaçla yaygın olarak kullanılır.
Nesne metotlarının geri çağırma olarak kullanımı¶
MicroPython, bir ISR’nin altta yatan kodla örnek değişkenlerini paylaşmasına olanak tanıyan bu güçlü tekniği destekler. Ayrıca, bir aygıt sürücüsü uygulayan bir sınıfın birden fazla aygıt örneğini desteklemesini de sağlar. Aşağıdaki örnek, iki LED’in farklı hızlarda yanıp sönmesine neden olur.
import machine
import micropython
micropython.alloc_emergency_exception_buf(100)
class Foo(object):
def __init__(self, freq, led):
self.led = led
self.timer = machine.Timer(-1, freq=freq, callback=self.cb, hard=True)
def cb(self, tim):
self.led.toggle()
red = Foo(1, machine.LED("LED_RED"))
green = Foo(0.8, machine.LED("LED_GREEN"))
Bu örnekte red örneği, 1 Hz’lik bir sanal zamanlayıcıdan kırmızı LED’i çalıştırır: zamanlayıcı her tetiklendiğinde red.cb() çağrılır ve kırmızı LED durumunu değiştirir. green örneği benzer şekilde 0.8 Hz’lik bir zamanlayıcıyla yeşil LED’in durumunu değiştirir. Örnek metotların kullanımı iki fayda sağlar. İlk olarak, tek bir sınıf, kodun birden fazla donanım örneği arasında paylaşılmasını sağlar. İkinci olarak, bağlı bir metot olarak geri çağırma fonksiyonunun ilk argümanı self‘tir. Bu, geri çağırmanın örnek verilerine erişmesini ve ardışık çağrılar arasında durum bilgisini saklamasını sağlar. Örneğin, yukarıdaki sınıfın yapıcıda sıfıra ayarlanmış bir self.count değişkeni olsaydı, cb() sayacı artırabilirdi. red ve green örnekleri böylece her LED’in durumunu kaç kez değiştirdiğine dair bağımsız sayımlar tutardı.
Python nesnelerinin oluşturulması¶
ISR’ler Python nesnelerinin örneklerini oluşturamaz. Bunun nedeni, MicroPython’un nesne için heap adı verilen bir boş bellek bloğu deposundan bellek ayırması gerektiğidir. Yığın ayırma işlemi yeniden girişli (re-entrant) olmadığından bir kesme işleyicisinde buna izin verilmez. Başka bir deyişle, kesme ana program bir ayırma işleminin ortasındayken meydana gelebilir - yığının bütünlüğünü korumak için yorumlayıcı ISR kodunda bellek ayırma işlemlerine izin vermez.
Bunun bir sonucu olarak ISR’ler kayan nokta aritmetiği kullanamaz; bunun nedeni kayan noktaların Python nesneleri olmasıdır. Benzer şekilde bir ISR bir listeye öğe ekleyemez. Uygulamada, hangi kod yapılarının bellek ayırma girişiminde bulunup bir hata mesajına yol açacağını tam olarak belirlemek zor olabilir: ISR kodunu kısa ve basit tutmanın bir başka nedeni de budur.
Bu sorunu önlemenin bir yolu, ISR’nin önceden ayrılmış arabellekler kullanmasıdır. Örneğin bir sınıf yapıcısı, bir bytearray örneği ve bir boolean bayrağı oluşturur. ISR metodu, arabellekteki konumlara veri atar ve bayrağı ayarlar. Bellek ayırma işlemi ISR’de değil, nesne örneklendiğinde ana program kodunda gerçekleşir.
MicroPython kütüphanesi G/Ç metotları genellikle önceden ayrılmış bir arabellek kullanma seçeneği sunar. Örneğin machine.I2C.readfrom_into(), çağıran tarafından sağlanan değiştirilebilir bir arabelleğe okuma yapar: bu, onun bir ISR’de kullanılmasını sağlar.
Bir sınıf veya global değişken kullanmadan nesne oluşturmanın bir yolu aşağıdaki gibidir:
def set_volume(t, buf=bytearray(3)):
buf[0] = 0xa5
buf[1] = t >> 4
buf[2] = 0x5a
return buf
Derleyici, fonksiyon ilk kez yüklendiğinde (genellikle içinde bulunduğu modül içe aktarıldığında) varsayılan buf argümanını örnekler.
Bağlı bir metoda bir referans oluşturulduğunda bir nesne oluşturma örneği meydana gelir. Bu, bir ISR’nin bir fonksiyona bağlı bir metot iletemeyeceği anlamına gelir. Bir çözüm, sınıf yapıcısında bağlı metoda bir referans oluşturmak ve bu referansı ISR’de iletmektir. Örneğin:
class Foo():
def __init__(self):
self.bar_ref = self.bar # Allocation occurs here
self.x = 0.1
self.tim = machine.Timer(-1, freq=2, callback=self.cb, hard=True)
def bar(self, _):
self.x *= 1.2
print(self.x)
def cb(self, t):
# Passing self.bar would cause allocation.
micropython.schedule(self.bar_ref, 0)
Diğer teknikler, metodu yapıcıda tanımlayıp örneklemek veya Foo.bar() metodunu self argümanıyla iletmektir.
Python nesnelerinin kullanımı¶
Python’un çalışma şekli nedeniyle nesneler üzerinde ek bir kısıtlama ortaya çıkar. Bir import ifadesi yürütüldüğünde Python kodu bytecode‘a derlenir ve genellikle bir satır kod birden fazla bytecode’a karşılık gelir. Kod çalıştığında yorumlayıcı her bytecode’u okur ve onu bir dizi makine kodu komutu olarak yürütür. Bir kesmenin makine kodu komutları arasında herhangi bir anda meydana gelebileceği göz önüne alındığında, orijinal Python kodu satırı yalnızca kısmen yürütülmüş olabilir. Sonuç olarak, ana döngüde değiştirilen bir küme, liste veya sözlük gibi bir Python nesnesi, kesmenin meydana geldiği anda dahili tutarlılıktan yoksun olabilir.
Tipik bir sonuç şöyledir. Nadir durumlarda ISR, nesne kısmen güncellenmişken tam o anda çalışır. ISR nesneyi okumaya çalıştığında bir çökme meydana gelir. Bu tür sorunlar tipik olarak nadir, rastgele anlarda meydana geldiğinden teşhis edilmeleri zor olabilir. Bu sorunu aşmanın yolları aşağıda Kritik Bölümler kısmında açıklanmıştır.
Bir nesnenin değiştirilmesinin neyi oluşturduğu konusunda net olmak önemlidir. Bir dizinin veya bytearray’in içeriğini değiştirmek güvenlidir. Bunun nedeni, baytların veya sözcüklerin kesilemeyen tek bir makine kodu komutu olarak yazılmasıdır: gerçek zamanlı programlama terminolojisinde bu yazma işlemi atomiktir. Aynı durum bir sözlük öğesini güncellemek için de geçerlidir, çünkü öğeler tam sayılar veya nesnelere işaretçiler olan makine sözcükleridir. Kullanıcı tanımlı bir nesne, bir dizi veya bytearray örnekleyebilir. Hem ana döngünün hem de ISR’nin bunların içeriğini değiştirmesi geçerlidir.
Tehlike, bir nesnenin yapısı değiştirildiğinde, özellikle sözlükler söz konusu olduğunda ortaya çıkar. Anahtar eklemek veya silmek bir yeniden hashleme işlemini tetikleyebilir. Bir yeniden hashleme devam ederken bir donanım ISR’si çalışır ve bir öğeye erişmeye çalışırsa bir çökme meydana gelebilir. Global değişkenler dahili olarak bir sözlük olarak uygulanır. Sonuç olarak ana program, donanım kesmeleri üreten bir işlem başlatmadan önce gerekli tüm global değişkenleri oluşturmalıdır. Uygulama kodu ayrıca global değişkenleri silmekten kaçınmalıdır.
MicroPython, keyfi hassasiyetteki tam sayıları destekler. 230 -1 ile -230 arasındaki değerler tek bir makine sözcüğünde saklanır. Daha büyük değerler Python nesneleri olarak saklanır. Sonuç olarak uzun tam sayılardaki değişiklikler atomik kabul edilemez. Değişkenin değeri değiştikçe bellek ayırma girişiminde bulunulabileceğinden, ISR’lerde uzun tam sayıların kullanımı güvenli değildir.
Kayan nokta sınırlamasının üstesinden gelme¶
Genel olarak ISR kodunda kayan noktalar kullanmaktan kaçınmak en iyisidir: donanım aygıtları normalde tam sayılarla çalışır ve kayan noktaya dönüştürme normalde ana döngüde yapılır. Ancak kayan nokta gerektiren birkaç DSP algoritması vardır. Donanımsal kayan noktaya sahip platformlarda (STM32 tabanlı OpenMV Cam’ler gibi) bu sınırlamayı aşmak için satır içi ARM Thumb assembler kullanılabilir. Bunun nedeni, işlemcinin kayan nokta değerlerini bir makine sözcüğünde saklamasıdır; değerler bir kayan nokta dizisi aracılığıyla ISR ile ana program kodu arasında paylaşılabilir.
micropython.schedule kullanımı¶
Bu fonksiyon, bir ISR’nin “çok yakında” yürütülmek üzere bir geri çağırma zamanlamasını sağlar. Geri çağırma, yığının kilitli olmadığı bir zamanda yürütülmek üzere kuyruğa alınır. Bu nedenle Python nesneleri oluşturabilir ve kayan noktalar kullanabilir. Geri çağırmanın ayrıca ana programın Python nesneleri üzerindeki herhangi bir güncellemeyi tamamladığı bir zamanda çalışacağı garanti edilir, böylece geri çağırma kısmen güncellenmiş nesnelerle karşılaşmaz.
Tipik kullanım, sensör donanımıyla ilgilenmektir. ISR, donanımdan veri alır ve onun başka bir kesme yayınlamasını sağlar. Ardından verileri işlemek için bir geri çağırma zamanlar.
Zamanlanmış geri çağırmalar, aşağıda özetlenen kesme işleyici tasarımı ilkelerine uymalıdır. Bu, ana program döngüsünü kesintiye uğratan herhangi bir kodda ortaya çıkabilecek G/Ç etkinliği ve paylaşılan verilerin değiştirilmesinden kaynaklanan sorunları önlemek içindir.
Yürütme süresi, kesmelerin oluşabileceği sıklıkla ilişkili olarak değerlendirilmelidir. Önceki geri çağırma yürütülürken bir kesme meydana gelirse, geri çağırmanın başka bir örneği yürütülmek üzere kuyruğa alınır; bu, mevcut örnek tamamlandıktan sonra çalışır. Bu nedenle sürekli yüksek bir kesme tekrarlama hızı, sınırsız kuyruk büyümesi ve sonunda bir RuntimeError ile başarısızlık riski taşır.
schedule() fonksiyonuna iletilecek geri çağırma bağlı bir metotsa, “Python nesnelerinin oluşturulması” bölümündeki notu göz önünde bulundurun.
İstisnalar¶
Bir ISR bir istisna fırlatırsa, bu istisna ana döngüye yayılmaz. İstisna ISR kodu tarafından işlenmediği sürece kesme devre dışı bırakılır.
asyncio ile arabirim oluşturma¶
Bir ISR çalıştığında asyncio zamanlayıcısını kesintiye uğratabilir. ISR bir asyncio işlemi gerçekleştirirse zamanlayıcının çalışması bozulabilir. Bu, kesmenin donanımsal mı yoksa yazılımsal mı olduğuna bakılmaksızın geçerlidir ve ISR’nin yürütmeyi micropython.schedule aracılığıyla başka bir fonksiyona aktarması durumunda da geçerlidir. Özellikle görev oluşturmak veya iptal etmek bir ISR bağlamında geçersizdir. asyncio ile etkileşimin güvenli yolu, senkronizasyonun asyncio.ThreadSafeFlag ile gerçekleştirildiği bir eş yordam (coroutine) uygulamaktır. Aşağıdaki parça, bir kesmeye yanıt olarak bir görevin oluşturulmasını göstermektedir:
tsf = asyncio.ThreadSafeFlag()
def isr(_): # Interrupt handler
tsf.set()
async def foo():
while True:
await tsf.wait()
asyncio.create_task(bar())
Bu örnekte, ISR’nin yürütülmesi ile foo()‘nun yürütülmesi arasında değişken miktarda gecikme olacaktır. Bu, işbirlikçi zamanlamanın doğasında vardır. Maksimum gecikme uygulamaya ve platforma bağlıdır ancak tipik olarak onlarca ms ile ölçülebilir.
Genel konular¶
Bu yalnızca gerçek zamanlı programlama konusuna kısa bir giriştir. Yeni başlayanlar, gerçek zamanlı programlardaki tasarım hatalarının teşhis edilmesi özellikle zor olan arızalara yol açabileceğini unutmamalıdır. Bunun nedeni, bunların nadiren ve esasen rastgele aralıklarla meydana gelebilmesidir. İlk tasarımı doğru yapmak ve sorunlar ortaya çıkmadan önce onları öngörmek çok önemlidir. Hem kesme işleyicilerinin hem de ana programın aşağıdaki konuları göz önünde bulundurarak tasarlanması gerekir.
Kesme işleyici tasarımı¶
Yukarıda belirtildiği gibi, ISR’ler mümkün olduğunca basit olacak şekilde tasarlanmalıdır. Her zaman kısa, öngörülebilir bir süre içinde geri dönmelidirler. Bu önemlidir çünkü ISR çalışırken ana döngü çalışmaz: ana döngü kaçınılmaz olarak kodun rastgele noktalarında yürütmesinde duraklamalar yaşar. Bu tür duraklamalar, özellikle süreleri uzun veya değişkense, teşhis edilmesi zor hataların kaynağı olabilir. ISR çalışma süresinin etkilerini anlamak için kesme önceliklerine ilişkin temel bir kavrayış gereklidir.
Kesmeler bir öncelik şemasına göre düzenlenir. ISR kodunun kendisi daha yüksek öncelikli bir kesme tarafından kesintiye uğratılabilir. İki kesme veri paylaşırsa bunun etkileri vardır (aşağıdaki Kritik Bölümler kısmına bakın). Böyle bir kesme meydana gelirse, ISR koduna bir gecikme ekler. ISR çalışırken daha düşük öncelikli bir kesme meydana gelirse, ISR tamamlanana kadar geciktirilir: gecikme çok uzunsa, daha düşük öncelikli kesme başarısız olabilir. Yavaş ISR’lerle ilgili bir başka sorun, yürütülmesi sırasında aynı türde ikinci bir kesmenin meydana gelmesi durumudur. İkinci kesme, birincinin sonlanmasıyla işlenir. Ancak gelen kesmelerin hızı, ISR’nin onları işleme kapasitesini sürekli olarak aşarsa sonuç pek hoş olmayacaktır.
Sonuç olarak döngü yapılarından kaçınılmalı veya en aza indirilmelidir. Kesmeye neden olan aygıt dışındaki aygıtlara G/Ç normalde önlenmelidir: disk erişimi, print ifadeleri ve UART erişimi gibi G/Ç işlemleri görece yavaştır ve süreleri değişebilir. Buradaki bir başka sorun, dosya sistemi fonksiyonlarının yeniden girişli olmamasıdır: bir ISR’de ve ana programda dosya sistemi G/Ç’sini kullanmak tehlikeli olur. En önemlisi, ISR kodu bir olayı beklememelidir. Kod, öngörülebilir bir süre içinde dönmesi garanti edilebiliyorsa G/Ç kabul edilebilir, örneğin bir pini veya LED’i değiştirmek gibi. Kesmeye neden olan aygıta I2C veya SPI aracılığıyla erişmek gerekli olabilir ancak bu tür erişimlerin aldığı süre hesaplanmalı veya ölçülmeli ve uygulama üzerindeki etkisi değerlendirilmelidir.
Genellikle ISR ile ana döngü arasında veri paylaşma ihtiyacı vardır. Bu, global değişkenler aracılığıyla veya sınıf ya da örnek değişkenleri aracılığıyla yapılabilir. Değişkenler tipik olarak tam sayı veya boolean türleri ya da tam sayı veya bayt dizileridir (önceden ayrılmış bir tam sayı dizisi, bir listeden daha hızlı erişim sunar). ISR tarafından birden fazla değerin değiştirildiği durumlarda, kesmenin ana programın değerlerin bazılarına eriştiği ama tümüne erişmediği bir anda meydana geldiği durumu göz önünde bulundurmak gerekir. Bu, tutarsızlıklara yol açabilir.
Aşağıdaki tasarımı düşünün. Bir ISR, gelen verileri bir bytearray’de saklar, ardından alınan bayt sayısını işleme için hazır toplam baytları temsil eden bir tam sayıya ekler. Ana program bayt sayısını okur, baytları işler, ardından hazır bayt sayısını sıfırlar. Bu, ana program bayt sayısını okuduktan hemen sonra bir kesme meydana gelene kadar çalışır. ISR, eklenen verileri arabelleğe koyar ve alınan sayıyı günceller, ancak ana program sayıyı zaten okumuş olduğundan başlangıçta alınan verileri işler. Yeni gelen baytlar kaybolur.
Bu tehlikeyi önlemenin çeşitli yolları vardır; en basiti dairesel bir arabellek kullanmaktır. Doğal olarak iş parçacığı güvenliğine sahip bir yapı kullanmak mümkün değilse, diğer yollar aşağıda açıklanmıştır.
Yeniden girişlilik¶
Bir fonksiyon veya metot, ana program ile bir veya daha fazla ISR arasında ya da birden fazla ISR arasında paylaşılırsa olası bir tehlike meydana gelebilir. Buradaki sorun, fonksiyonun kendisinin kesintiye uğrayabileceği ve bu fonksiyonun başka bir örneğinin çalışabileceğidir. Bunun meydana gelmesi durumunda, fonksiyon yeniden girişli olacak şekilde tasarlanmalıdır. Bunun nasıl yapılacağı, bu öğreticinin kapsamı dışında ileri düzey bir konudur.
Kritik bölümler¶
Kritik bir kod bölümünün bir örneği, bir ISR’den etkilenebilecek birden fazla değişkene erişen bir bölümdür. Kesme, tek tek değişkenlere erişimler arasında meydana gelirse, değerleri tutarsız olur. Bu, yarış durumu (race condition) olarak bilinen bir tehlikenin bir örneğidir: ISR ve ana program döngüsü değişkenleri değiştirmek için yarışır. Tutarsızlığı önlemek için, ISR’nin kritik bölümün süresi boyunca değerleri değiştirmemesini sağlamak amacıyla bir yöntem kullanılmalıdır. Bunu başarmanın bir yolu, bölümün başlangıcından önce machine.disable_irq() ve sonunda machine.enable_irq() çağrısı yapmaktır. İşte bu yaklaşımın bir örneği:
import machine
import micropython
import array
import random
import time
micropython.alloc_emergency_exception_buf(100)
class BoundsException(Exception):
pass
ARRAYSIZE = const(20)
index = 0
data = array.array('i', [0] * ARRAYSIZE)
def callback1(t):
global data, index
for x in range(5):
data[index] = random.getrandbits(30) # simulate input
index += 1
if index >= ARRAYSIZE:
raise BoundsException('Array bounds exceeded')
tim = machine.Timer(-1, freq=100, callback=callback1, hard=True)
for loop in range(1000):
if index > 0:
irq_state = machine.disable_irq() # Start of critical section
for x in range(index):
print(data[x])
index = 0
machine.enable_irq(irq_state) # End of critical section
print('loop {}'.format(loop))
time.sleep_ms(1)
tim.deinit()
Bir kritik bölüm, tek bir kod satırından ve tek bir değişkenden oluşabilir. Aşağıdaki kod parçasını düşünün.
count = 0
def cb(): # An interrupt callback
count += 1
def main():
# Code to set up the interrupt callback omitted
while True:
count += 1
Bu örnek, ince bir hata kaynağını göstermektedir. Ana döngüdeki count += 1 satırı, oku-değiştir-yaz olarak bilinen belirli bir yarış durumu tehlikesi taşır. Bu, gerçek zamanlı sistemlerdeki hataların klasik bir nedenidir. Ana döngüde MicroPython, count değerini okur, ona 1 ekler ve geri yazar. Nadir durumlarda kesme, okumadan sonra ve yazmadan önce meydana gelir. Kesme count‘u değiştirir ancak ISR geri döndüğünde değişikliği ana döngü tarafından üzerine yazılır. Gerçek bir sistemde bu, nadir, öngörülemeyen arızalara yol açabilir.
Yukarıda belirtildiği gibi, ana kodda bir Python yerleşik türünün bir örneği değiştirilirse ve bu örneğe bir ISR’de erişilirse dikkatli olunmalıdır. Değişikliği gerçekleştiren kod, ISR çalıştığında örneğin geçerli bir durumda olmasını sağlamak için kritik bir bölüm olarak değerlendirilmelidir.
Bir veri kümesi farklı ISR’ler arasında paylaşılıyorsa özellikle dikkatli olunması gerekir. Buradaki tehlike, daha düşük öncelikli kesme paylaşılan verileri kısmen güncellediğinde daha yüksek öncelikli kesmenin meydana gelebilmesidir. Bu durumla başa çıkmak, aşağıda açıklanan mutex nesnelerinin bazen kullanılabileceğini belirtmek dışında, bu girişin kapsamı dışında ileri düzey bir konudur.
Kritik bir bölümün süresi boyunca kesmeleri devre dışı bırakmak olağan ve en basit yoldur, ancak bu yalnızca soruna neden olma potansiyeli olan kesmeyi değil tüm kesmeleri devre dışı bırakır. Bir kesmeyi uzun süre devre dışı bırakmak genellikle istenmeyen bir durumdur. Zamanlayıcı kesmeleri söz konusu olduğunda, bir geri çağırmanın meydana geldiği zamana değişkenlik katar. Aygıt kesmeleri söz konusu olduğunda, aygıtın çok geç hizmet görmesine ve aygıt donanımında olası veri kaybı veya taşma hatalarına yol açabilir. ISR’ler gibi, ana koddaki bir kritik bölüm de kısa, öngörülebilir bir süreye sahip olmalıdır.
Kesmelerin devre dışı bırakıldığı süreyi kökten azaltan, kritik bölümlerle başa çıkma yaklaşımlarından biri, mutex (adı karşılıklı dışlama -mutual exclusion- kavramından türetilmiştir) olarak adlandırılan bir nesne kullanmaktır. Ana program, kritik bölümü çalıştırmadan önce mutex’i kilitler ve sonunda kilidini açar. ISR, mutex’in kilitli olup olmadığını test eder. Kilitliyse, kritik bölümden kaçınır ve geri döner. Tasarım zorluğu, kritik değişkenlere erişimin reddedilmesi durumunda ISR’nin ne yapması gerektiğini tanımlamaktır. Basit bir mutex örneği burada bulunabilir. Mutex kodunun kesmeleri devre dışı bıraktığını, ancak yalnızca sekiz makine komutu süresince yaptığını unutmayın: bu yaklaşımın faydası, diğer kesmelerin neredeyse hiç etkilenmemesidir.
Kesmeler ve REPL¶
Zamanlayıcılarla ilişkili olanlar gibi kesme işleyicileri, bir program sonlandıktan sonra çalışmaya devam edebilir. Bu, geri çağırmayı yükselten nesnenin kapsam dışına çıkmış olmasını beklediğiniz durumlarda beklenmedik sonuçlar üretebilir. Örneğin bir OpenMV Cam üzerinde:
def bar():
foo = machine.Timer(-1, freq=4, callback=lambda t: print('.', end=''), hard=True)
bar()
Bu, zamanlayıcı açıkça devre dışı bırakılana veya kart Ctrl-D ile sıfırlanana kadar çalışmaya devam eder.