การจัดการขัดจังหวะในไมโครคอนโทรลเลอร์และตัวอย่าง FSM


9

คำถามเริ่มต้น

ฉันมีคำถามทั่วไปเกี่ยวกับการจัดการอินเทอร์รัปต์ในไมโครคอนโทรลเลอร์ ฉันใช้ MSP430 แต่ฉันคิดว่าคำถามอาจขยายไปยังยูซีเอสอื่น ๆ ฉันต้องการทราบว่าเป็นวิธีปฏิบัติที่ดีในการเปิด / ปิดการใช้งานอินเตอร์รัปต์ตามรหัสหรือไม่ ฉันหมายถึงถ้าฉันมีบางส่วนของรหัสที่ไม่ไวต่อการขัดจังหวะ (หรือแย่กว่านั้นคือต้องไม่ฟังการขัดจังหวะไม่ว่าด้วยเหตุผลใดก็ตาม) ดีกว่าไหมที่จะ:

  • ปิดการใช้งานอินเทอร์รัปต์ก่อนและเปิดใช้งานอีกครั้งหลังจากส่วนที่สำคัญ
  • วางธงไว้ใน ISR ที่เกี่ยวข้องและ (แทนที่จะปิดการใช้งานอินเตอร์รัปต์) ตั้งค่าสถานะเป็นเท็จก่อนส่วนสำคัญและรีเซ็ตเป็นจริงทันทีหลังจากนั้น เพื่อป้องกันรหัสของ ISR จากการถูกดำเนินการ
  • ไม่ใช่ทั้งสองข้อเสนอแนะจึงยินดีต้อนรับ!

อัปเดต: อินเตอร์รัปต์และแผนภูมิสถานะ

ฉันจะให้สถานการณ์ที่เฉพาะเจาะจง ให้เราสมมติว่าเราต้องการใช้แผนภูมิสถานะซึ่งประกอบด้วย 4 บล็อก:

  1. การเปลี่ยน / ผล
  2. เงื่อนไขการออก
  3. กิจกรรมเข้า
  4. ทำกิจกรรม

นี่คือสิ่งที่อาจารย์สอนเราที่มหาวิทยาลัย อาจไม่ใช่วิธีที่ดีที่สุดในการทำคือทำตามรูปแบบนี้:

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

ขอให้เราสมมติว่าจากSTATE_Aฉันต้องการที่จะอ่อนไหวต่อการขัดจังหวะที่มาจากชุดของปุ่ม (พร้อมระบบ debouce ฯลฯ ฯลฯ ) buttonPressedเมื่อกดคนหนึ่งของปุ่มเหล่านี้ขัดจังหวะถูกสร้างขึ้นและธงที่เกี่ยวข้องกับพอร์ตอินพุตจะถูกคัดลอกลงในตัวแปร หาก debounce ถูกตั้งค่าเป็น 200 ms ในบางกรณี (ตัวจับเวลาจ้องจับผิดตัวจับเวลาตัวนับเคาน์เตอร์ ... ) เรามั่นใจว่าbuttonPressedไม่สามารถอัปเดตด้วยค่าใหม่ก่อน 200 ms นี่คือสิ่งที่ฉันขอให้คุณ (และตัวฉันเอง :) แน่นอน

ฉันต้องเปิดใช้งานการขัดจังหวะในกิจกรรม DO STATE_Aและปิดการใช้งานก่อนออกหรือไม่

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

ในทางที่ผมแน่ใจว่าในครั้งต่อไปผม execxute บล็อก 1 (เปลี่ยนแปลง / ผลกระทบ) ที่ย้ำต่อไปผมแน่ใจว่าเงื่อนไขการตรวจสอบพร้อมเปลี่ยนไม่ได้มาจากการขัดจังหวะภายหลังที่มีการเขียนทับค่าก่อนหน้าของbuttonPressedผมว่า ต้องการ (แม้ว่ามันจะเป็นไปไม่ได้ที่สิ่งนี้เกิดขึ้นเพราะ 250 ms ต้องผ่านไป)


3
เป็นการยากที่จะให้คำแนะนำโดยไม่ทราบข้อมูลเพิ่มเติมเกี่ยวกับสถานการณ์ของคุณ แต่บางครั้งก็จำเป็นต้องปิดการใช้งานอินเตอร์รัปต์ในระบบฝังตัว ควรปิดการใช้งานอินเตอร์รัปต์ในช่วงเวลาสั้น ๆ เท่านั้นเพื่อไม่ให้มีการขัดจังหวะ อาจเป็นไปได้ที่จะปิดใช้งานเฉพาะการขัดจังหวะโดยเฉพาะมากกว่าการขัดจังหวะทั้งหมด ฉันจำไม่ได้ว่าเคยใช้เทคนิค flag-inside-ISR เหมือนที่คุณอธิบายดังนั้นฉันจึงสงสัยว่านั่นเป็นทางออกที่ดีที่สุดของคุณหรือไม่
kkrambo

คำตอบ:


17

กลยุทธ์แรกคือการสร้างเฟิร์มแวร์โดยรวมเพื่อให้การขัดจังหวะเกิดขึ้นได้ตลอดเวลา ต้องปิดการขัดจังหวะเพื่อให้รหัสเบื้องหน้าสามารถดำเนินการลำดับอะตอมควรทำเท่าที่จำเป็น มักจะมีวิธีสถาปัตยกรรมรอบ ๆ มัน

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

โปรดทราบว่าหากคุณไม่มั่นใจในวงจรหรือตำแหน่งหน่วยความจำ (อาจเกิดขึ้นได้) ไม่เช่นนั้นคุณต้องการเพิ่มประสิทธิภาพเพื่อความชัดเจนและการบำรุงรักษาโค้ด ตัวอย่างเช่นหากคุณมีเครื่อง 16 บิตที่อัปเดตตัวนับ 32 บิตในสัญญาณนาฬิกาขีดถูกขัดจังหวะคุณจะต้องตรวจสอบให้แน่ใจว่าเมื่อรหัสเบื้องหน้าอ่านตัวนับว่าครึ่งทั้งสองนั้นสอดคล้องกัน วิธีหนึ่งคือปิดการขัดจังหวะอ่านคำทั้งสองแล้วเปิดการขัดจังหวะอีกครั้ง หากเวลาแฝงการอินเตอร์รัปต์ไม่สำคัญสิ่งนั้นก็เป็นที่ยอมรับได้อย่างสมบูรณ์

ในกรณีที่คุณต้องมี latentcy ขัดจังหวะต่ำคุณสามารถอ่านคำสูงอ่านคำต่ำอ่านคำสูงอีกครั้งและทำซ้ำหากมีการเปลี่ยนแปลง สิ่งนี้จะทำให้โค้ดพื้นหน้าช้าลงเล็กน้อย แต่จะไม่มีการแฝงอินเทอร์รัปต์เลยแม้แต่น้อย มีลูกเล่นเล็ก ๆ น้อย ๆ มากมาย อีกอย่างหนึ่งอาจเป็นการตั้งค่าสถานะในรูทีนการขัดจังหวะที่บ่งชี้ว่าตัวนับต้องเพิ่มขึ้นจากนั้นให้ทำในเหตุการณ์ลูปหลักในโค้ดเบื้องหน้า ใช้งานได้ดีหากอัตราการขัดจังหวะของตัวนับช้าพอที่การวนซ้ำเหตุการณ์จะเพิ่มขึ้นก่อนที่จะตั้งค่าสถานะอีกครั้ง

หรือแทนที่จะใช้แฟลกใช้ตัวนับคำเดียว รหัสเบื้องหน้าจะเก็บตัวแปรแยกต่างหากที่มีตัวนับล่าสุดที่ได้อัปเดตระบบเป็น มันจะทำการลบค่าลบที่ไม่ได้ลงนามของตัวนับสดลบค่าที่บันทึกไว้เพื่อกำหนดจำนวนครั้งในการจัดการเห็บ สิ่งนี้ทำให้โค้ดเบื้องหน้าพลาดเหตุการณ์สูงสุดครั้งละ 2 N -1 โดยที่ N คือจำนวนบิตในคำดั้งเดิมที่ ALU สามารถจัดการได้แบบอะตอม

แต่ละวิธีมีข้อดีและข้อเสียของตัวเอง ไม่มีคำตอบที่ถูกต้องเดียว เข้าใจอีกครั้งว่าเครื่องทำงานอย่างไรจากนั้นคุณไม่จำเป็นต้องมีกฎง่ายๆ


7

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

ดังนั้นการปิดการใช้งานอินเตอร์รัปต์ซึ่งส่วนใหญ่จัดการโดยคำสั่งโปรเซสเซอร์เดียว (และเรียกว่าการใช้ฟังก์ชั่นคอมไพเลอร์ภายใน) เป็นหนึ่งในการเดิมพันที่ปลอดภัยที่สุดที่คุณสามารถทำได้

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

หากส่วนที่สำคัญของคุณต้องการการขัดจังหวะเพียงครั้งเดียวจะไม่ถูกดำเนินการ แต่ควรดำเนินการส่วนอื่น ๆ วิธีอื่นน่าจะเป็นไปได้

ฉันพบว่าตัวเองเขียนโปรแกรมรูทีนการขัดจังหวะบริการสั้นที่สุด ดังนั้นพวกเขาเพียงแค่ตั้งค่าสถานะซึ่งจะถูกตรวจสอบในช่วงปกติของโปรแกรม แต่ถ้าคุณทำเช่นนั้นระวังสภาพการแข่งขันในขณะที่รอธงนั้นไว้

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


5

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

วางธงไว้ใน ISR ที่เกี่ยวข้องและ (แทนที่จะปิดการใช้งานอินเตอร์รัปต์) ตั้งค่าสถานะเป็นเท็จก่อนส่วนสำคัญและรีเซ็ตเป็นจริงทันทีหลังจากนั้น เพื่อป้องกันรหัสของ ISR จากการถูกดำเนินการ

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

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

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

  • หากไม่มีการขัดจังหวะระหว่างส่วนที่สำคัญจะไม่มีการดำเนินการใด ๆ
  • หากมีสิ่งใดสิ่งหนึ่งขัดจังหวะในระหว่างส่วนที่สำคัญจะมีการดำเนินการอย่างใดอย่างหนึ่ง
  • หากมีการขัดจังหวะหลายครั้งระหว่างส่วนที่สำคัญจะมีการดำเนินการเพียงครั้งเดียวหลังจากนั้น

หากระบบของคุณซับซ้อนและต้องติดตามการขัดจังหวะโดยสมบูรณ์ยิ่งขึ้นคุณจะต้องออกแบบระบบที่ซับซ้อนมากขึ้นเพื่อติดตามการขัดจังหวะและดำเนินการตามนั้น

อย่างไรก็ตามหากคุณออกแบบอินเทอร์รัปต์ให้ทำงานขั้นต่ำตามที่จำเป็นเพื่อให้เกิดการทำงานตามปกติและหน่วงเวลาทุกอย่างให้กับการประมวลผลปกติอินเทอร์รัปต์จะไม่ค่อยส่งผลกระทบต่อโค้ดอื่น ๆ ของคุณ มีการดักจับหรือปล่อยข้อมูลถ้าจำเป็นหรือตั้งค่า / รีเซ็ตเอาต์พุต ฯลฯ ตามต้องการจากนั้นให้พา ธ รหัสหลักให้ความสนใจกับแฟล็กบัฟเฟอร์และตัวแปรที่อินเตอร์รัปต์ส่งผลกระทบดังนั้นการประมวลผลที่มีความยาวสามารถทำได้ในลูปหลัก แทนที่จะขัดจังหวะ

สิ่งนี้ควรกำจัดทั้งหมดยกเว้นสถานการณ์น้อยมากซึ่งคุณอาจต้องการส่วนของโค้ดที่ไม่ขาดตอน


ฉันได้อัปเดตโพสต์เพื่ออธิบายสถานการณ์ที่ฉันทำงานได้ดีขึ้น :)
Umberto D.

1
ในโค้ดตัวอย่างของคุณฉันจะพิจารณาปิดใช้งานการขัดจังหวะปุ่มเฉพาะเมื่อไม่ต้องการและเปิดใช้เมื่อจำเป็น การทำสิ่งนี้บ่อยครั้งไม่ใช่ปัญหาจากการออกแบบ เปิดการอินเตอร์รัปต์โกลบอลไว้เพื่อให้คุณสามารถเพิ่มอินเทอร์รัปต์อื่น ๆ ลงในรหัสได้ในภายหลังหากจำเป็น หรืออีกวิธีหนึ่งคือรีเซ็ตค่าสถานะเมื่อคุณไปที่สถานะ A และไม่สนใจ หากกดปุ่มและตั้งค่าสถานะใครจะสนใจ? ไม่ต้องสนใจจนกว่าคุณจะกลับสู่สถานะ A.
Adam Davis

ใช่ นั่นอาจเป็นทางออกเพราะในการออกแบบที่เกิดขึ้นจริงฉันมักจะไปที่ LPM3 (MSP430) ด้วยการขัดจังหวะการเปิดใช้งานทั่วโลกและฉันออกจาก LPM3 กลับมาทำงานอีกครั้งทันทีที่ตรวจพบการขัดจังหวะ ดังนั้นทางออกคือสิ่งที่คุณนำเสนอซึ่งฉันคิดว่ามีการรายงานในส่วนที่สองของรหัส: เปิดใช้งานการขัดจังหวะทันทีที่ฉันเริ่มที่จะทำกิจกรรมของรัฐที่ต้องการและปิดการใช้งานก่อนที่จะบล็อกการเปลี่ยน อาจมีวิธีอื่นที่เป็นไปได้คือปิดการใช้งานอินเทอร์รัปต์ก่อนออกจาก "บล็อกกิจกรรม" และเปิดใช้งานอีกครั้งในภายหลัง (เมื่อ?)
Umberto D.

1

การวางธงไว้ใน ISR ในขณะที่คุณอธิบายอาจไม่ทำงานเนื่องจากคุณไม่สนใจเหตุการณ์ที่ทำให้เกิดการขัดจังหวะ การปิดใช้งานอินเตอร์รัปต์ทั่วโลกมักเป็นทางเลือกที่ดีกว่า อย่างที่คนอื่นพูดคุณไม่ควรทำบ่อยนัก โปรดทราบว่าการอ่านหรือการเขียนใด ๆ ที่ทำผ่านคำสั่งเดียวไม่ควรได้รับการปกป้องเนื่องจากคำสั่งนั้นรันหรือไม่

มากขึ้นอยู่กับชนิดของทรัพยากรที่คุณพยายามแบ่งปัน หากคุณกำลังป้อนข้อมูลจาก ISR ไปยังโปรแกรมหลัก (หรือกลับกัน) คุณสามารถใช้บางอย่างเช่นบัฟเฟอร์ FIFO การดำเนินการปรมาณูเพียงอย่างเดียวคือการอัปเดตพอยน์เตอร์การอ่านและการเขียนซึ่งจะลดเวลาที่คุณใช้กับการขัดจังหวะการใช้งานให้น้อยที่สุด


0

มีความแตกต่างเล็กน้อยที่คุณต้องคำนึงถึงคือ คุณอาจเลือกที่จะ "ล่าช้า" การจัดการขัดจังหวะหรือ "ไม่สนใจ" และขัดจังหวะ

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

บางครั้งเราต้องการที่จะไม่สนใจการขัดจังหวะ วิธีที่ดีที่สุดอาจเป็นการขัดขวางการขัดจังหวะในระดับฮาร์ดแวร์ มักจะมีตัวควบคุมขัดจังหวะหรือคล้ายกันซึ่งเราอาจพูดได้ว่าอินพุตใดควรสร้างการขัดจังหวะ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.