ทางเลือกเธรดสำหรับระบบฝังตัว

Aug 16 2020

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

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

ดังนั้นรหัส C ของฉัน (ยังไม่ได้ใช้กับรูปภาพโดยเฉพาะ) จึงเป็นดังนี้:

void display_timer(){
  static struct current_time timer;
  static int is_time_set = 0;
  set_current_time(&timer, &is_time_set);

  while (is_time_set){
    system("clear");
    printf("########\n");
    printf("%d:%d:%d\n", timer.HOURS, timer.MINUTES, timer.SECONDS);
    printf("########\n");
    sleep(1);
    update_timer(&timer, &is_time_set);
  }
}
int main ()
{
  while (1){
    display_menu();

  }
}

ในระหว่างการนอนหลับ () คอนโทรลเลอร์จะต้องสามารถเฝ้าดูอินพุตใหม่และดำเนินการตามนั้น

ทางเลือกหนึ่งที่ฉันใช้คือการใช้เครื่องของรัฐเพื่อจัดเก็บการกดปุ่มโดยแบ่งฟังก์ชันการนอนหลับออกเป็น 4 หรือ 8 ช่วงเวลาดังนี้:

  while (is_time_set){
    system("clear");
    printf("########\n");
    printf("%d:%d:%d\n", timer.HOURS, timer.MINUTES, timer.SECONDS);
    printf("########\n");
    for (int i = 0; i<8; i++){
    if (state_machine_input == 1){state_machine_input = 0; break;}
    sleep(1/8);
    }
    update_timer(&timer, &is_time_set);

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

คำตอบ

28 Lundin Aug 17 2020 at 07:04

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

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

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

15 Pelle Aug 17 2020 at 13:23

ใช้การขัดจังหวะ

คุณต้องการเรียกใช้รหัสเมื่อกดปุ่มหรือไม่? ใช้ pin-change-interrupt

คุณต้องการทำอะไรในช่วงเวลาที่กำหนดหรือไม่? ใช้ตัวจับเวลาขัดจังหวะ

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

โปรแกรมหลักหยุดชั่วคราวโดยอัตโนมัติในขณะที่ดำเนินการขัดจังหวะ

วิธีทั่วไปในการแบ่งปันข้อมูลระหว่างอินเทอร์รัปต์และโค้ดหลักคือผ่านvolatileตัวแปรส่วนกลางและปิดใช้งานอินเทอร์รัปต์ชั่วคราวเมื่ออ่านข้อมูลจาก globals เหล่านี้เมื่อมีมากกว่าขนาดคำของคอนโทรลเลอร์ (เกือบตลอดเวลาในคอนโทรลเลอร์ 8 บิต)

11 AndrewLentvorski Aug 16 2020 at 23:38

ฉันอาจจะแนะนำไลบรารีมัลติทาสก์แบบร่วมมือ สิ่งที่ฉันเคยใช้ในอดีตคือ Protothreads:http://www.dunkels.com/adam/pt/

ไลบรารีมัลติทาสก์แบบร่วมมือที่ดีใด ๆ จะช่วยแยกเครื่องสถานะโดยนัยที่จำเป็นในการติดตามสิ่งต่างๆ

โชคดี.

6 bracco23 Aug 17 2020 at 12:53

โดยทั่วไปมีวิธีการที่แตกต่างกันในการทำงานหลายอย่างพร้อมกันเมื่อพูดถึงระบบฝังตัว:

  • การสำรวจความคิดเห็นหรือการทำงานหลายอย่างแบบร่วมมือกัน: ทุกอย่างเสร็จสิ้นในลูปเดียวที่ไม่มีที่สิ้นสุดและงานได้รับการออกแบบให้ใช้เวลาน้อยที่สุดเท่าที่จะเป็นไปได้และกลับไปที่การดำเนินการหลักโดยเร็วที่สุดเพื่อหลีกเลี่ยงความล่าช้า โปรดทราบว่างานที่เหมาะกับสถาปัตยกรรมนี้อาจไม่ใช่สิ่งที่คุณคิดในแง่ของแนวคิดระดับสูงเช่นในแอปพลิเคชันของคุณงานหนึ่งอาจเป็นupdate_displayงานอื่นอาจเป็นได้check_buttonและคุณจะสร้างลูปเช่น:
    while(1){
         check_buttons();
         update_display();
         sleep(0.1); //seconds
     }
  • การขัดจังหวะ : อินพุตที่เป็นไปได้ทั้งหมดที่เชื่อมต่อกับการขัดจังหวะของฮาร์ดแวร์และการดำเนินการหลักจะถูกทิ้งไว้สำหรับสิ่งที่ไม่สามารถวางอินเตอร์รัปต์ได้ (อาจไม่มีอะไรเลยซึ่งในกรณีนี้โดยปกติไมโครคอนโทรลเลอร์จะอยู่ในโหมดสลีปเพื่อลดการสิ้นเปลืองพลังงานรายละเอียดของวิธีนี้ การทำมักจะขึ้นอยู่กับไมโครคอนโทรลเลอร์และคอมไพเลอร์ที่ใช้

  • RTOS : ขึ้นอยู่กับปริมาณพลังงานที่ไมโครคอนโทรลเลอร์ให้อาจเป็นไปได้ที่จะเรียกใช้ระบบปฏิบัติการแบบเรียลไทม์ (RTOS) ซึ่งอาจมี API เพื่อสร้างงานหรือแม้แต่เธรด ขึ้นอยู่กับการใช้งานและความสามารถของฮาร์ดแวร์และไม่จำเป็นต้องมีตัวอย่างเพื่อการศึกษา (หรือแนะนำให้ใช้ imo)

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

5 ExpinElectronics Aug 17 2020 at 06:15

ส่วนใหญ่ในอุปกรณ์ 8 บิตมีแหล่งที่มา จำกัด ฉันคิดว่าวิธีแก้ปัญหาที่ง่ายกว่านั้นเป็นทางออกที่ดีกว่าใน PIC 8 บิต

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

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