สภาพการแข่งขัน Sleep Microcontroller


11

รับไมโครคอนโทรลเลอร์ที่ใช้รหัสต่อไปนี้:

volatile bool has_flag = false;

void interrupt(void) //called when an interrupt is received
{
    clear_interrupt_flag(); //clear interrupt flag
    has_flag = true; //signal that we have an interrupt to process
}

int main()
{
    while(1)
    {
        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

สมมติว่าif(has_flag)สภาพประเมินเป็นเท็จและเรากำลังจะดำเนินการคำสั่งการนอนหลับ ขวาก่อนที่เราจะดำเนินการเรียนการสอนการนอนหลับที่เราได้รับการขัดจังหวะ หลังจากเราออกจากการขัดจังหวะเราดำเนินการสอนการนอนหลับ

ลำดับการดำเนินการนี้ไม่เป็นที่ต้องการเนื่องจาก:

  • process()ไมโครคอนโทรลเลอร์ไปนอนแทนการตื่นขึ้นมาและเรียก
  • ไมโครคอนโทรลเลอร์อาจไม่เคยตื่นขึ้นมาหากไม่ได้รับการขัดจังหวะหลังจากนั้น
  • การโทรไปยังprocess()ถูกเลื่อนออกไปจนกว่าจะมีการขัดจังหวะครั้งต่อไป

รหัสจะถูกเขียนเพื่อป้องกันไม่ให้เกิดสภาพการแข่งขันนี้ได้อย่างไร

แก้ไข

ไมโครคอนโทรลเลอร์บางตัวที่ ATMega มีบิตเปิดใช้งานสลีปซึ่งป้องกันไม่ให้เกิดสภาวะเช่นนี้ (ขอบคุณ Kvegaoro สำหรับการชี้ให้เห็นสิ่งนี้) JRoberts เสนอตัวอย่างการใช้งานที่เป็นตัวอย่างของพฤติกรรมนี้

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

int main()
{
    while(1)
    {
        //clear global interrupt enable bit.
        //if the flag tested below is not set, then we enter
        //sleep with the global interrupt bit cleared, which is
        //the intended behavior.
        disable_global_interrupts();

        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            enable_global_interrupts();  //set global interrupt enable bit.

            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

นี่เป็นคำถามเชิงปฏิบัติหรือเชิงทฤษฎีหรือไม่?
AndrejaKo

ในทางทฤษฎีคุณใช้ตัวจับเวลาที่ปลุกคุณขึ้นมาทุกครั้ง (ป้อนค่าที่ยอมรับได้) ms แล้วกลับเข้าสู่โหมดสลีปหากไม่มีความต้องการอะไร
ผู้เล่นเกรดี้เล่น

1
ฉันจะทำinterrupt_flagตามintและเพิ่มทุกครั้งที่มีการขัดจังหวะ จากนั้นเปลี่ยนif(has_flag)ไปเป็นwhile (interrupts_count)แล้วนอน อย่างไรก็ตามการขัดจังหวะอาจเกิดขึ้นหลังจากที่คุณออกจากวงขณะที่ หากนี่เป็นปัญหาการประมวลผลในอินเทอร์รัปต์จะทำเองหรือไม่?
angelatlarge

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

1
@TRISAbits: ใน PIC 18x วิธีที่ฉันอธิบายไว้ในคำตอบของฉันใช้ได้ดี (มันเป็นแบบปกติของฉันเมื่อใช้ส่วนนั้น)
supercat

คำตอบ:


9

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

forever,
   interrupts off;
   if has_flag,
      interrupts on;
      process interrupt;
   else,
      interrupts-on-and-sleep;    # won't be interrupted
   end
end

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


คำตอบที่ดี! อัลกอริทึมที่คุณให้ใช้งานได้ดีกับ AVR ขอบคุณสำหรับคำแนะนำ
TRISAbits

3

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

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

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

บางครั้ง Sleep-on-exit ก็เป็นรุ่นที่ใช้งานได้ดี ตัวอย่างเช่นอุปกรณ์ที่มีจอแอลซีดีประหยัดพลังงานอาจมีการตั้งโปรแกรมพร้อมโค้ดที่ดูเหมือน:

void select_view_user(int default_user)
{
  int current_user;
  int ch;
  current_user = default_user;
  do
  {
    lcd_printf(0, "User %d");
    lcd_printf(1, ...whatever... );
    get_key();
    if (key_hit(KEY_UP)) {current_user = (current_user + 1) % MAX_USERS};
    if (key_hit(KEY_DOWN)) {current_user = (current_user + MAX_USERS-1) % MAX_USERS};
    if (key_hit(KEY_ENTER)) view_user(current_user);
  } while(!key_hit(KEY_EXIT | KEY_TIMEOUT));
}

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


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

1

โปรแกรมไมโครเพื่อปลุกเมื่อขัดจังหวะ

รายละเอียดเฉพาะจะแตกต่างกันไปขึ้นอยู่กับไมโครที่คุณใช้

จากนั้นแก้ไขรูทีน main ():

int main()
{
    while(1)
    {
        sleep();
        process(); //process the interrupt
    }
}

1
มีการสันนิษฐานว่ามีสถาปัตยกรรม Wake-on-interrupt ฉันไม่คิดว่าคำตอบของคุณแก้ปัญหา / ปัญหา
angelatlarge

@angelat Large Point ได้รับการยอมรับ ฉันได้เพิ่มตัวอย่างรหัสที่ฉันคิดว่าช่วย
jwygralak67

@ jwygralak67: ขอบคุณสำหรับคำแนะนำ แต่รหัสที่คุณให้เพียงแค่ย้ายปัญหาไปที่รูทีนกระบวนการ () ซึ่งตอนนี้ต้องทดสอบว่ามีการขัดจังหวะเกิดขึ้นก่อนที่จะดำเนินการเนื้อความของกระบวนการ () หรือไม่
TRISAbits

2
หากการขัดจังหวะไม่เกิดขึ้นเราจะตื่นทำไม
JRobert

1
@JRobert: เราอาจตื่นจากการขัดจังหวะก่อนหน้าให้ทำรูทีนกระบวนการ () และเมื่อเราเสร็จสิ้นการทดสอบ if (has_flag) และถูกต้องก่อนการนอนหลับเราจะได้รับการขัดจังหวะอีกครั้งซึ่งทำให้เกิดปัญหาที่ฉันได้อธิบายไว้ใน คำถาม.
TRISAbits
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.