ฟังก์ชั่นการกำหนดตัวชี้อะตอมใน Arduino?


11

ตัวอย่างต่อไปนี้มาจากซอร์สโค้ดของ TimerOne :

// 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();                                                                                            
}

คำถาม: หากตัวจับเวลากำลังทำงานอยู่และการเรียกโปรแกรมหลักattachInterrupt()ตัวจับเวลาจะเกิดขึ้นที่นั่นระหว่างการกำหนดฟังก์ชั่นตัวชี้isrCallback = isr;? จากนั้นด้วยเวลาที่โชคดีTimer1.isrCallback();ตัวชี้ฟังก์ชั่นจะประกอบด้วยบางส่วนของที่อยู่เก่าและส่วนหนึ่งของที่อยู่ใหม่ทำให้ ISR กระโดดไปที่ตำแหน่งปลอมหรือไม่

ฉันคิดว่านี่อาจเป็นเพราะตัวชี้ฟังก์ชั่นนั้นกว้างกว่า 1 ไบต์และการเข้าถึงข้อมูล> 1 ไบต์ไม่ใช่อะตอม วิธีแก้ไขที่เป็นไปได้อาจเป็น:

  • โทรทุกครั้งdetachInterrupt()เพื่อให้แน่ใจว่าตัวจับเวลาไม่ทำงานก่อนที่จะโทรattachInterrupt()เช่นทำให้เอกสาร Timer1 ชัดเจน
  • หรือแก้ไข Timer1 ปิดการใช้งานตัวจับเวลา overflow ขัดจังหวะชั่วคราวก่อนหน้านี้ isrCallback = isr;

สิ่งนี้สมเหตุสมผลหรือมีบางสิ่งในTimer1แหล่งที่มาหรือการกำหนดตัวชี้ฟังก์ชันที่ฉันพลาดไปหรือไม่

คำตอบ:


7

ลองดูรหัสสำหรับ attachInterrupt () และ detachInterrupt () ใน /Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/cores/arduino/WInterrupts.c(นั่นคือที่ที่พวกเขาอยู่บน Mac อย่างไรก็ตามโครงสร้างไฟล์ Arduino บน OS อื่น ๆ อาจดูคล้ายกันในระดับล่างของเส้นทาง)

ปรากฏว่า attachInterrupt () ถือว่าการขัดจังหวะที่มีปัญหายังไม่ได้เปิดใช้งานเพราะมันเขียนตัวชี้ฟังก์ชั่นโดยไม่ต้องระมัดระวังใด ๆ โปรดทราบว่า detachInterrupts () ปิดใช้งานการขัดจังหวะเป้าหมายก่อนที่จะเขียนตัวชี้ NULL ไปยังเวกเตอร์ อย่างน้อยฉันก็ควรใช้detachInterrupt()/ attachInterrupt()คู่

ฉันต้องการเรียกใช้รหัสดังกล่าวในส่วนที่สำคัญด้วยตัวเอง ดูเหมือนว่าวิธีแรกของคุณ (แยกออกแล้วแนบ) จะใช้งานได้ แต่ฉันไม่สามารถแน่ใจได้ว่าจะไม่พลาดการขัดจังหวะที่กำหนดเวลาไว้อย่างน่าเสียดาย แผ่นข้อมูลสำหรับ MCU ของคุณอาจมีมากกว่าที่จะพูดเกี่ยวกับเรื่องนั้น แต่ฉันก็ไม่แน่ใจในจุดนี้ว่าโลกcli()/ sei()ไม่ควรพลาดเช่นกัน แผ่นข้อมูล ATMega2560 ส่วน 6.8 พูดว่า "เมื่อใช้คำสั่ง SEI เพื่อเปิดใช้งานการขัดจังหวะคำสั่งต่อไปนี้ SEI จะถูกดำเนินการก่อนการขัดจังหวะที่รอดำเนินการดังแสดงในตัวอย่างนี้" ดูเหมือนจะบ่งบอกว่ามันสามารถบัฟเฟอร์การขัดจังหวะในขณะที่การขัดจังหวะ ถูกปิด


มันมีประโยชน์จริง ๆ กับการดำน้ำไปยังแหล่งที่มา :) กลไกการขัดจังหวะแนบ / แยกแยกของ TimerOne ดูเหมือนว่าจะทำในทำนองเดียวกันกับมาตรฐาน (ของ WInterrupt) และมีคุณสมบัติ "แบบเดียวกัน"
Joonas Pulakka

0

ดูเหมือนว่าคุณมีประเด็น สิ่งที่ต้องทำคือต้องปิดการใช้งานอินเทอร์รัปต์ในลักษณะที่คุณไม่ได้เปิดใช้งานหากพวกเขาถูกปิดใช้งานในครั้งแรก ตัวอย่างเช่น:

  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

"เมื่อใช้คำสั่ง SEI เพื่อเปิดใช้งานการขัดจังหวะการเรียนการสอนต่อไปนี้ SEI จะถูกดำเนินการก่อนการขัดจังหวะที่รอดำเนินการดังที่แสดงในตัวอย่างนี้"

จุดประสงค์นี้คือเพื่อให้คุณเขียนโค้ดดังนี้:

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

หากไม่มีบทบัญญัตินั้นคุณอาจได้รับการขัดจังหวะระหว่างสองบรรทัดและทำให้การนอนหลับไม่มีกำหนด (เนื่องจากการขัดจังหวะที่กำลังจะปลุกคุณเกิดขึ้นก่อนที่คุณจะนอนหลับ) ข้อกำหนดในตัวประมวลผลที่คำสั่งถัดไปหลังจากการขัดจังหวะถูกเปิดใช้งานหากไม่ได้เปิดใช้งานมาก่อนจะถูกดำเนินการเสมอป้องกันสิ่งนี้


มันจะมีประสิทธิภาพมากกว่าTIMSK1=0; TIFR1=_BV(TOV1); isrCallback=isr; TIMSK1=_BV(TOIE1);หรือไม่ มันทำหน้าที่สำรอง CPU หนึ่งตัวและไม่ทำให้เกิดความล่าช้าในการตอบโต้
Edgar Bonet

แล้วบิตอื่น ๆ เช่น ICIE1, OCIE1B, OCIE1A ล่ะ ฉันเข้าใจเกี่ยวกับความล่าช้า แต่การขัดจังหวะควรจะสามารถรับมือกับวงจรนาฬิกาสองรอบที่ไม่สามารถใช้งานได้ อาจจะมีสภาพการแข่งขัน อินเทอร์รัปต์อาจถูกทริกเกอร์แล้ว (เช่นตั้งค่าสถานะใน CPU) และการปิด TIMSK1 อาจไม่สามารถหยุดการจัดการได้ คุณอาจต้องรีเซ็ต TOV1 (โดยเขียน 1 ลงใน TIFR1) เพื่อให้แน่ใจว่าจะไม่เกิดขึ้น ความไม่แน่นอนทำให้ฉันคิดว่าการปิดอินเตอร์รัปต์ไปทั่วโลกเป็นวิธีที่ปลอดภัยกว่า
Nick Gammon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.