ทางเลือกเธรดสำหรับระบบฝังตัว
ฉันกำลังศึกษาวิศวกรรมไฟฟ้า เนื่องจากการระบาดชั้นเรียนของฉันจึงถูกระงับและฉันใช้เวลานี้เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับอิเล็กทรอนิกส์และการเขียนโปรแกรม
ฉันกำลังพยายามใช้ 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);
สามารถทำได้ แต่ฉันจะขอบคุณถ้าฉันไม่ต้องเพิ่มความซับซ้อนให้กับโปรเจ็กต์อีกเช่นเพิ่มเครื่องสถานะอื่น ฉันจะทำอะไรได้บ้างในข้อกำหนดของซอฟต์แวร์เพื่อใช้ฟังก์ชันนี้
คำตอบ
เธรดเป็นแนวคิดระดับสูงกว่าการเขียนโปรแกรมไมโครคอนโทรลเลอร์ พูดง่ายๆคือใช้เธรดเป็นตัวกำหนดตารางเวลาที่ใช้ตัวจับเวลาการขัดจังหวะซึ่งจะบันทึกตัวนับโปรแกรม + ตัวชี้สแต็ก ฯลฯ และตั้งค่าเหล่านั้นไปยังตำแหน่งต่างๆ ดังนั้นจึงเป็นไปได้ค่อนข้างง่ายและง่ายที่จะใช้แนวคิดที่คล้ายกันโดยใช้อินเทอร์รัปต์ - ด้วยประโยชน์ที่คุณจะได้รับอินเทอร์รัปต์พิเศษแทนการใช้มัลติเธรดทั่วไป
นั่นเป็นวิธีเดียวที่สมเหตุสมผลในการทำกับมรดกที่ถูก จำกัด 8 ขมเช่น PIC ซึ่งมีข้อ จำกัด อย่างมากเมื่อต้องใช้สแต็ก อย่าลืมใช้เธรด libs แม้กระทั่งที่เขียนขึ้นสำหรับไมโครคอนโทรลเลอร์ นั่นจะเป็นการเพิ่มความขยายและความซับซ้อนมากเกินไปโดยไม่ได้รับอะไรเลย เป็นความคิดที่ไม่ดีโดยทั่วไปในการลากแนวคิดการเขียนโปรแกรมพีซีเข้าสู่โลกฝังตัว
สิ่งที่คุณควรทำคือวางการสแกนปุ่มของคุณไว้ในอินเทอร์รัปต์ตัวจับเวลาแบบวนรอบซึ่งดำเนินการหนึ่งครั้งต่อ 10ms หรือมากกว่านั้น จากภายในอินเทอร์รัปต์คุณสำรวจปุ่มและเปรียบเทียบปุ่มที่อ่านกับครั้งก่อนหน้าเพื่อจุดประสงค์ในการลบล้าง ผลลัพธ์ของสิ่งนี้จะถูกเก็บไว้ในตัวแปรที่ใช้ร่วมกับโปรแกรมหลักซึ่งประกาศเป็นvolatile
และได้รับการปกป้องจากสภาวะการแข่งขัน เนื่องจากคุณเขียนถึงตัวแปรจากภายในอินเทอร์รัปต์เท่านั้นจึงอาจเป็นการป้องกันที่เพียงพอเพื่อให้แน่ใจว่าการอ่านมีขนาด 8 บิต แต่คุณต้องแยกชิ้นส่วนออกเพื่อให้แน่ใจ ข้อมูลเพิ่มเติมเกี่ยวกับที่นี่: การใช้สารระเหยในฝัง C การพัฒนา
ใช้การขัดจังหวะ
คุณต้องการเรียกใช้รหัสเมื่อกดปุ่มหรือไม่? ใช้ pin-change-interrupt
คุณต้องการทำอะไรในช่วงเวลาที่กำหนดหรือไม่? ใช้ตัวจับเวลาขัดจังหวะ
ในทางหนึ่งฮาร์ดแวร์ของไมโครคอนโทรลเลอร์จะเรียกใช้ 'เธรด' ที่ตรวจสอบแหล่งที่มาของการขัดจังหวะและเรียกใช้ 'การโทรกลับ' หรือการขัดจังหวะรูทีนสำหรับแต่ละเหตุการณ์
โปรแกรมหลักหยุดชั่วคราวโดยอัตโนมัติในขณะที่ดำเนินการขัดจังหวะ
วิธีทั่วไปในการแบ่งปันข้อมูลระหว่างอินเทอร์รัปต์และโค้ดหลักคือผ่านvolatile
ตัวแปรส่วนกลางและปิดใช้งานอินเทอร์รัปต์ชั่วคราวเมื่ออ่านข้อมูลจาก globals เหล่านี้เมื่อมีมากกว่าขนาดคำของคอนโทรลเลอร์ (เกือบตลอดเวลาในคอนโทรลเลอร์ 8 บิต)
ฉันอาจจะแนะนำไลบรารีมัลติทาสก์แบบร่วมมือ สิ่งที่ฉันเคยใช้ในอดีตคือ Protothreads:http://www.dunkels.com/adam/pt/
ไลบรารีมัลติทาสก์แบบร่วมมือที่ดีใด ๆ จะช่วยแยกเครื่องสถานะโดยนัยที่จำเป็นในการติดตามสิ่งต่างๆ
โชคดี.
โดยทั่วไปมีวิธีการที่แตกต่างกันในการทำงานหลายอย่างพร้อมกันเมื่อพูดถึงระบบฝังตัว:
- การสำรวจความคิดเห็นหรือการทำงานหลายอย่างแบบร่วมมือกัน: ทุกอย่างเสร็จสิ้นในลูปเดียวที่ไม่มีที่สิ้นสุดและงานได้รับการออกแบบให้ใช้เวลาน้อยที่สุดเท่าที่จะเป็นไปได้และกลับไปที่การดำเนินการหลักโดยเร็วที่สุดเพื่อหลีกเลี่ยงความล่าช้า โปรดทราบว่างานที่เหมาะกับสถาปัตยกรรมนี้อาจไม่ใช่สิ่งที่คุณคิดในแง่ของแนวคิดระดับสูงเช่นในแอปพลิเคชันของคุณงานหนึ่งอาจเป็น
update_display
งานอื่นอาจเป็นได้check_button
และคุณจะสร้างลูปเช่น:
while(1){
check_buttons();
update_display();
sleep(0.1); //seconds
}
การขัดจังหวะ : อินพุตที่เป็นไปได้ทั้งหมดที่เชื่อมต่อกับการขัดจังหวะของฮาร์ดแวร์และการดำเนินการหลักจะถูกทิ้งไว้สำหรับสิ่งที่ไม่สามารถวางอินเตอร์รัปต์ได้ (อาจไม่มีอะไรเลยซึ่งในกรณีนี้โดยปกติไมโครคอนโทรลเลอร์จะอยู่ในโหมดสลีปเพื่อลดการสิ้นเปลืองพลังงานรายละเอียดของวิธีนี้ การทำมักจะขึ้นอยู่กับไมโครคอนโทรลเลอร์และคอมไพเลอร์ที่ใช้
RTOS : ขึ้นอยู่กับปริมาณพลังงานที่ไมโครคอนโทรลเลอร์ให้อาจเป็นไปได้ที่จะเรียกใช้ระบบปฏิบัติการแบบเรียลไทม์ (RTOS) ซึ่งอาจมี API เพื่อสร้างงานหรือแม้แต่เธรด ขึ้นอยู่กับการใช้งานและความสามารถของฮาร์ดแวร์และไม่จำเป็นต้องมีตัวอย่างเพื่อการศึกษา (หรือแนะนำให้ใช้ imo)
พิจารณาด้วยว่าส่วนสำคัญอีกประการหนึ่งในการตัดสินใจสถาปัตยกรรมโดยรวมของแอปพลิเคชันคือการแบ่งงานและวิธีการทำงานร่วมกัน หนึ่งในกระบวนทัศน์ที่ใช้คือState Machines (ลิงก์นี้ไปยังหน้าวิกิพีเดียทั่วไปที่อาจมีทรัพยากรที่ง่ายกว่านี้เฉพาะสำหรับการเขียนโปรแกรมแบบฝังอยู่ในหนังสือเรียนของคุณหรือใน Google)
ส่วนใหญ่ในอุปกรณ์ 8 บิตมีแหล่งที่มา จำกัด ฉันคิดว่าวิธีแก้ปัญหาที่ง่ายกว่านั้นเป็นทางออกที่ดีกว่าใน PIC 8 บิต
คุณสามารถสร้างตัวจับเวลาฮาร์ดแวร์สำหรับงาน 2 งานที่แตกต่างกัน ตั้งค่าสถานะและตรวจสอบค่าสถานะในลูปที่ไม่มีที่สิ้นสุดของคุณทำงานและรีเซ็ตค่าสถานะ อย่าใช้ความล่าช้า วิธีนี้รับประกันว่าจะทำงานของคุณในลูปที่ไม่มีที่สิ้นสุดของคุณหากคุณตั้งค่าสถานะไว้
แต่คุณต้องรู้ว่างานไม่ได้ดำเนินการตามเวลาที่แน่นอนเมื่อแฟล็กขึ้น หากมีการตั้งค่าสถานะสองรายการในเวลาเดียวกันคุณจะไม่สามารถทราบได้ว่าอันใดถูกเรียกใช้ก่อน เพราะคุณไม่รู้ว่าอยู่ที่ไหนในลูปที่ไม่มีที่สิ้นสุด แต่ส่วนใหญ่แล้วมันก็โอเคสำหรับการเชื่อมต่อระหว่างแอปพลิเคชันที่ไม่สำคัญ