Arduino'da fonksiyon işaretçisi atamaları atomik midir?


11

Aşağıdaki snippet'ler TimerOne kitaplığı kaynak kodundandır :

// TimerOne.h:
void (*isrCallback)();

// TimerOne.cpp:
ISR(TIMER1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  Timer1.isrCallback();
}

// TimerOne.cpp:
void TimerOne::attachInterrupt(void (*isr)(), long microseconds)
{
  if(microseconds > 0) setPeriod(microseconds);
  isrCallback = isr; // register the user's callback with the real ISR
  TIMSK1 = _BV(TOIE1); // sets the timer overflow interrupt enable bit
  resume();                                                                                            
}

Soru: Zamanlayıcı zaten çalışıyorsa ve ana program çağırıyorsa attachInterrupt(), zamanlayıcı kesintisi fonksiyon işaretçisi ataması sırasında orada olabilir isrCallback = isr;mi? Sonra, şanslı zamanlama ile, Timer1.isrCallback();fonksiyon işaretçisi kısmen eski ve kısmen yeni adreslerden oluşarak ISR'nin sahte bir yere atlamasına neden olur mu?

İşlev işaretçileri kesinlikle 1 bayttan daha geniş olduğundan ve> 1 bayt verilerine erişmek atomik olmadığından, durum böyle olabilir. Olası geçici çözümler şunlar olabilir:

  • Aramadan detachInterrupt()önce zamanlayıcının çalışmadığından emin olmak için her zaman arayın attachInterrupt(), yani Zamanlayıcı1 belgelerini netleştirin.
  • Veya Zamanlayıcı1'i değiştirin, zamanlayıcı taşması devre dışı bırakıldığında, isrCallback = isr;

Bu mantıklı mı, yoksa Timer1kaynaklarda veya işlev işaretçisi atamalarında kaçırdığım bir şey var mı?

Yanıtlar:


7

İçinde attachInterrupt () ve detachInterrupt () koduna bir göz atın ( /Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/cores/arduino/WInterrupts.czaten Mac'te oldukları yer budur. Diğer işletim sistemlerindeki Arduino dosya yapısı muhtemelen yolun alt düzeylerinde benzer görünür).

AttachInterrupt () işlevinin, herhangi bir önlem almadan işlev işaretçisini yazdığı için söz konusu kesmenin henüz etkin olmadığını varsayar. DetachInterrupts () işlevinin vektörüne bir NULL-pointer yazmadan önce hedef kesmeyi devre dışı bıraktığını unutmayın. En azından bir detachInterrupt()/ attachInterrupt()pair kullanırım

Kritik bir bölümde böyle bir kodu çalıştırmak istiyorum, kendim. Maalesef zamanlanmış bir kesmeyi kaçıramayacağından emin olamıyorum, ancak ilk yolun (ayrılma, sonra ekleme) işe yarayacağı görünüyor. MCU'nuzun veri sayfasında bunun hakkında söylenecek daha fazla bilgi olabilir. Ama bu noktada da emin değilim, bir küresel cli()/ sei()onu da kaçırmaz. ATMega2560 veri sayfası, bölüm 6.8, "Kesintileri etkinleştirmek için SEI komutunu kullanırken, bu örnekte gösterildiği gibi SEI'yi takip eden talimatlar, beklemede olan herhangi bir kesintiden önce yürütülecektir", kesmeler sırasında bir kesintiyi arabelleğe alabileceğini ima ediyor gibi görünüyor kapalı.


Kaynaklara dalmak gerçekten yararlıdır :) TimerOne'un takma / ayırma kesme mekanizması standart (WInterrupt's) ile aynı şekilde yapılmış gibi görünüyor ve bu yüzden aynı "özelliklere" sahip.
Joonas Pulakka

0

Bir fikrin var gibi görünüyor. Yapılacak mantıklı şey, kesintileri ilk etapta devre dışı bırakıldıklarında yeniden etkinleştirmeyeceğiniz şekilde devre dışı bırakmak olacaktır. Örneğin:

  uint8_t oldSREG = SREG;  // remember if interrupts are on
  cli();                   // make the next line interruptible
  isrCallback = isr;       // register the user's callback with the real ISR
  SREG = oldSREG;          // turn interrupts back on, if they were on before

"Kesintileri etkinleştirmek için SEI komutunu kullanırken, bu örnekte gösterildiği gibi, SEI'yi izleyen talimatlar beklemedeki herhangi bir kesintiden önce yürütülecektir"

Bunun amacı şu şekilde kod yazmanıza izin vermektir:

  sei ();         // enable interrupts
  sleep_cpu ();   // sleep

Bu hüküm olmadan, bu iki çizgi arasında bir kesinti yaşayabilir ve böylece süresiz olarak uyuyabilirsiniz (çünkü sizi uyandıracak olan kesinti, uyumadan önce meydana gelmiştir ). İşlemcide, bir sonraki talimatın, daha önce etkinleştirilmemişlerse kesmelerden sonra etkin hale getirilmesi şartı her zaman yürütülür, buna karşı korur.


Daha verimli olmaz mıydı TIMSK1=0; TIFR1=_BV(TOV1); isrCallback=isr; TIMSK1=_BV(TOIE1);? Bir CPU kaydından tasarruf sağlar ve kesme gecikmesine katkıda bulunmaz.
Edgar Bonet

ICIE1, OCIE1B, OCIE1A gibi diğer bitler ne olacak? Gecikmeyi anlıyorum, ancak kesintiler, mevcut olmayan birkaç saat döngüsüyle başa çıkabilmelidir. Bir yarış durumu olabilir. Kesme zaten tetiklenmiş olabilir (yani CPU'daki bayrak ayarlanmıştır) ve TIMSK1'in kapatılması işlemin durmasına neden olmayabilir. Bunun gerçekleşmediğinden emin olmak için TOV1'i (TIFR1'de 1 yazarak) da sıfırlamanız gerekebilir. Oradaki belirsizlik beni dünya çapında kesintileri durdurmanın daha güvenli bir yol olduğunu düşündürüyor.
Nick Gammon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.