มัลติทาสกิ้งบนไมโครคอนโทรลเลอร์ PIC


17

มัลติทาสกิ้งมีความสำคัญในทุกวันนี้ ฉันสงสัยว่าเราจะประสบความสำเร็จในไมโครคอนโทรลเลอร์และการเขียนโปรแกรมแบบฝังตัวได้อย่างไร ฉันกำลังออกแบบระบบซึ่งใช้ไมโครคอนโทรลเลอร์ PIC ฉันได้ออกแบบเฟิร์มแวร์ใน MplabX IDE โดยใช้ C แล้วออกแบบแอปพลิเคชันสำหรับมันใน Visual Studio โดยใช้ C #

เนื่องจากฉันคุ้นเคยกับการใช้เธรดในการเขียนโปรแกรม C # บนเดสก์ท็อปเพื่อใช้งานแบบขนานจึงมีวิธีทำเช่นเดียวกันในรหัสไมโครคอนโทรลเลอร์หรือไม่ MplabX IDE นำเสนอpthreads.hแต่มันเป็นเพียงส่วนที่ไม่มีการใช้งาน ฉันรู้ว่ามีการสนับสนุน FreeRTOS แต่การใช้ที่ทำให้โค้ดของคุณซับซ้อนยิ่งขึ้น บางฟอรัมบอกว่าการขัดจังหวะสามารถใช้เป็นการมัลติทาสกิ้งได้ แต่ฉันไม่คิดว่าการขัดจังหวะนั้นเทียบเท่ากับเธรด

ฉันกำลังออกแบบระบบที่ส่งข้อมูลบางอย่างไปยัง UART และในขณะเดียวกันก็จำเป็นต้องส่งข้อมูลไปยังเว็บไซต์ผ่านทางอีเธอร์เน็ต (แบบใช้สาย) ผู้ใช้สามารถควบคุมเอาต์พุตผ่านเว็บไซต์ แต่เอาต์พุตจะเปิด / ปิดด้วยความล่าช้า 2-3 วินาที นั่นคือปัญหาที่ฉันเผชิญ มีวิธีการแก้ปัญหาสำหรับมัลติทาสกิ้งในไมโครคอนโทรลเลอร์หรือไม่?


เธรดสามารถใช้กับโปรเซสเซอร์ที่รันระบบปฏิบัติการเท่านั้นเนื่องจากเธรดเป็นส่วนหนึ่งของกระบวนการและกระบวนการใช้ในระบบปฏิบัติการเท่านั้น
TicTacToe

@ Zola ใช่คุณพูดถูก แต่ในกรณีของคอนโทรลเลอร์
เครื่องบิน

2
สำเนาที่เป็นไปได้ของRTOS สำหรับระบบฝังตัว
Roger Rowland

1
คุณสามารถอธิบายได้ไหมว่าทำไมคุณถึงต้องทำงานมัลติทาสกิ้งอย่างแท้จริงและไม่สามารถนำซอฟต์แวร์ของคุณไปใช้ได้อย่างสมเหตุสมผลโดยใช้วิธีการปัดเศษแบบโรบินหรือวงเลือก () หรือคล้ายกัน?
whatsisname

2
อย่างที่ฉันได้กล่าวไปแล้วว่าฉันกำลังส่งและรับข้อมูลไปยัง uart และในเวลาเดียวกันการส่งและรับข้อมูลไปยังอีเธอร์เน็ต นอกจากนี้ฉันยังต้องบันทึกข้อมูลใน SD card พร้อมกับเวลาดังนั้นใช่ DS1307 RTC เกี่ยวข้องและ EEPROM เกี่ยวข้องด้วย จนถึงตอนนี้ฉันเพิ่งมี 1 UART แต่หลังจากนั้นไม่กี่วันฉันจะส่งและรับข้อมูลจาก 3 โมดูล UART เว็บไซต์จะรับข้อมูลจาก 5 ระบบที่ต่างกันที่ติดตั้งในที่ห่างไกล ทั้งหมดนี้จะต้องขนานกัน แต่ไม่ใช่ไม่ใช่ขนาน แต่มีความล่าช้าไม่กี่วินาที !
เครื่องบิน

คำตอบ:


20

ระบบปฏิบัติการมัลติทาสกิ้งมีสองประเภทหลักคือแบบ preemptive และแบบร่วมมือ ทั้งสองอนุญาตให้มีการกำหนดหลายภารกิจในระบบความแตกต่างคือวิธีการสลับงาน แน่นอนว่าด้วยหน่วยประมวลผลหลักเดียวมีเพียงงานเดียวเท่านั้นที่ทำงานในเวลาจริง

ระบบมัลติทาสกิ้งทั้งสองประเภทต้องใช้สแต็กแยกต่างหากสำหรับแต่ละงาน ดังนั้นสิ่งนี้จึงมีความหมายสองอย่างคือประการแรกหน่วยประมวลผลกลางอนุญาตให้วางที่ใดก็ได้ใน RAM และจึงมีคำแนะนำในการย้ายตัวชี้สแต็ก (SP) ไปรอบ ๆ - นั่นคือไม่มีจุดประสงค์ฮาร์ดแวร์สแต็คพิเศษเหมือนอย่างต่ำ PIC ของ สิ่งนี้ทำให้ซีรี่ย์ PIC10, 12 และ 16 ออกไป

คุณสามารถเขียนระบบปฏิบัติการเกือบทั้งหมดใน C แต่ตัวสลับงานซึ่ง SP จะย้ายไปรอบ ๆ จะต้องอยู่ในชุดประกอบ ในหลาย ๆ ครั้งฉันได้เขียนตัวสลับงานสำหรับ PIC24, PIC32, 8051 และ 80x86 ความกล้าทั้งหมดแตกต่างกันมากขึ้นอยู่กับสถาปัตยกรรมของโปรเซสเซอร์

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

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

คุณไม่ได้พูดว่า PIC ประเภทใดที่คุณต้องการใช้ PIC24 และ PIC32 ส่วนใหญ่จะมีพื้นที่เหลือเฟือสำหรับเรียกใช้ระบบปฏิบัติการมัลติทาสก์ PIC18 (PIC 8 บิตเพียงตัวเดียวที่มีสแต็คใน RAM) มีขนาด RAM สูงสุด 4K นั่นเป็นเรื่องที่ค่อนข้างแน่นอน

ด้วยความร่วมมือในการทำงานหลายอย่างพร้อมกัน (ที่ง่ายกว่าของสองคน) การสลับงานจะทำได้ก็ต่อเมื่องาน "เลิก" การควบคุมกลับไปยังระบบปฏิบัติการ สิ่งนี้จะเกิดขึ้นเมื่อใดก็ตามที่งานต้องเรียกรูทีน OS เพื่อทำหน้าที่บางอย่างที่มันจะรอเช่นการร้องขอ I / O หรือการจับเวลา สิ่งนี้ทำให้ง่ายขึ้นสำหรับระบบปฏิบัติการที่จะสลับกองเนื่องจากไม่จำเป็นต้องบันทึกการลงทะเบียนและข้อมูลสถานะทั้งหมด SP สามารถสลับไปยังงานอื่นได้ (ถ้าไม่มีงานอื่นพร้อมที่จะทำงาน การควบคุมที่กำหนด) หากงานปัจจุบันไม่จำเป็นต้องทำการเรียกใช้ OS แต่ได้ทำงานมาระยะหนึ่งแล้วก็จำเป็นต้องยกเลิกการควบคุมโดยสมัครใจเพื่อให้ระบบตอบสนอง

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

Windows 3.1 และรุ่นก่อนหน้าเป็นระบบความร่วมมือซึ่งเป็นส่วนหนึ่งที่ทำให้ประสิทธิภาพไม่ได้ดีนัก

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

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

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


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

1
... ทรัพยากรเต็มรูปแบบเพื่อดำเนินการให้เสร็จสิ้น (ในระบบ pre-emptive) จะต้องมีการล็อกดังนั้นจึงทำให้วัตถุที่มีการป้องกันพร้อมใช้งานในภารกิจที่สอง
supercat

1
ในขณะที่สหกรณ์มัลติทาสกิ้งจำเป็นต้องมีระเบียบวินัยการทำให้มั่นใจว่าบางครั้งจะต้องปฏิบัติตามข้อกำหนดด้านเวลาสามารถทำได้ง่ายกว่าภายใต้มัลติทาสก์ร่วมมือกว่าภายใต้กฎเกณฑ์ เนื่องจากการล็อกน้อยมากจะต้องถูกเก็บข้าม task task ระบบ task-switch-round-robin task-task ห้าภารกิจที่ต้องทำงานไม่ให้เกิน 10ms โดยไม่ยอมรวมกับตรรกะเล็กน้อยที่ระบุว่า "ถ้า task X เร่งด่วน จำเป็นต้องเรียกใช้เรียกใช้งานถัดไป "จะทำให้แน่ใจว่าภารกิจ X ไม่ต้องรอนานกว่า 10ms เมื่อสัญญาณส่งสัญญาณก่อนที่จะทำงาน ในทางตรงกันข้ามหากงานต้องการล็อคซึ่งเป็นภารกิจ X ...
supercat

1
... กำลังต้องการ แต่ถูกสลับโดย pre-emptive switcher ก่อนที่จะปล่อยมัน X อาจไม่ได้ทำอะไรที่มีประโยชน์จนกว่าตัวกำหนดตารางเวลาของ CPU จะเริ่มทำงานก่อน เว้นแต่ว่าตัวกำหนดตารางเวลาจะมีตรรกะในการรับรู้และจัดการกับลำดับความสำคัญของการผกผันมันอาจใช้เวลาสักครู่ก่อนที่มันจะเริ่มทำงานเพื่อให้งานแรกทำธุรกิจให้เสร็จสิ้นและปลดล็อค ปัญหาดังกล่าวไม่สามารถแก้ไขได้ แต่การแก้ปัญหาต้องใช้ความซับซ้อนจำนวนมากซึ่งสามารถหลีกเลี่ยงได้ในระบบสหกรณ์ ระบบสหกรณ์ทำงานได้ดีมากยกเว้นหนึ่ง gotcha: ...
supercat

3
คุณไม่จำเป็นต้องร่วมมือกันหลายกองหากคุณใช้รหัสในการดำเนินการต่อ ในสาระสำคัญรหัสของคุณถูกแบ่งออกในฟังก์ชั่นvoid foo(void* context)ควบคุมตรรกะ (เคอร์เนล) ดึงตัวชี้หนึ่งและฟังก์ชั่นคู่ตัวชี้ของคิวและเรียกมันทีละครั้ง ฟังก์ชันนั้นใช้บริบทเพื่อเก็บตัวแปรของมันและจากนั้นสามารถเพิ่มการส่งความต่อเนื่องในคิว ฟังก์ชั่นเหล่านั้นจะต้องกลับมาอย่างรวดเร็วเพื่อให้งานอื่น ๆ ของพวกเขาใน CPU นี่เป็นวิธีการตามเหตุการณ์ที่ต้องการสแต็กเดียวเท่านั้น
วงล้อประหลาด

16

เธรดมีให้โดยระบบปฏิบัติการ ในโลกที่ฝังตัวเรามักไม่มีระบบปฏิบัติการ ("โลหะเปลือย") ดังนั้นนี่คือตัวเลือกต่อไปนี้:

  • ห่วงการสำรวจหลักแบบคลาสสิก หน้าที่หลักของคุณมีระยะเวลา (1) ซึ่งทำงาน 1 จากนั้นทำงาน 2 ...
  • การวนรอบหลัก + ISR ค่าสถานะ: คุณมี ISR ซึ่งทำหน้าที่ time-critical แล้วแจ้งเตือนการวนรอบหลักผ่านตัวแปรค่าสถานะที่งานต้องการบริการ บางที ISR อาจสร้างตัวละครใหม่ในบัฟเฟอร์วงกลมแล้วบอกลูปหลักเพื่อจัดการข้อมูลเมื่อพร้อมที่จะทำเช่นนั้น
  • ISR ทั้งหมด: ตรรกะส่วนใหญ่ที่นี่ดำเนินการจาก ISR บนคอนโทรลเลอร์ที่ทันสมัยเช่น ARM ซึ่งมีหลายระดับความสำคัญ สิ่งนี้สามารถจัดให้มีรูปแบบ "คล้ายเธรด" ที่ทรงพลัง แต่อาจสับสนในการดีบักดังนั้นจึงควรสงวนไว้เฉพาะสำหรับข้อ จำกัด ด้านเวลาที่สำคัญเท่านั้น
  • RTOS: เคอร์เนล RTOS (อำนวยความสะดวกโดย ISR ตัวจับเวลา) สามารถอนุญาตให้สลับระหว่างหลายเธรดของการดำเนินการ คุณพูดถึง FreeRTOS

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


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

8

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

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

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

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

วิธีเดียวที่แท้จริงในการทำงานหลายอย่างในเวลาเดียวกันบน MCU แบบ single-core คือการใช้ DMA และอุปกรณ์ต่อพ่วงเนื่องจากทำงานเป็นอิสระจากแกนหลัก (DMA และ MCU ใช้บัสเดียวกันดังนั้นจึงทำงานช้าลงเล็กน้อยเมื่อ ทั้งคู่เปิดใช้งานอยู่) ดังนั้นในขณะที่ DMA กำลังสับไบต์ไปยัง UART แกนของคุณมีอิสระที่จะส่งข้อมูลไปยังอีเธอร์เน็ต


2
ขอบคุณ DMA ฟังดูน่าสนใจ ฉันจะค้นหามันแน่นอน!
เครื่องบิน

PICs บางรุ่นเท่านั้นที่มี DMA
Matt Young

1
ฉันใช้ PIC32;)
เครื่องบิน

6

คำตอบอื่น ๆ ได้อธิบายถึงตัวเลือกที่ใช้บ่อยที่สุด (main loop, ISR, RTOS) นี่เป็นอีกทางเลือกหนึ่งประนีประนอม: Protothreads มันเป็น lib ที่เบามากสำหรับเธรดที่ใช้ลูปหลักและมาโคร C บางตัวเพื่อ "เลียนแบบ" RTOS แน่นอนว่ามันไม่มีระบบปฏิบัติการเต็มรูปแบบ แต่สำหรับเธรด "แบบง่าย" อาจมีประโยชน์


ฉันสามารถดาวน์โหลดซอร์สโค้ดสำหรับ windows ได้จากที่ใด ฉันคิดว่ามันใช้งานได้กับ linux เท่านั้น!
เครื่องบิน

@CZAbhinav มันควรจะเป็นระบบปฏิบัติการที่เป็นอิสระและคุณจะได้รับการดาวน์โหลดล่าสุดที่นี่
erebos

ตอนนี้ฉันอยู่ใน windows และใช้ MplabX ฉันไม่คิดว่ามันมีประโยชน์ที่นี่ อย่างไรก็ตามขอขอบคุณ!
เครื่องบิน

ยังไม่เคยได้ยินเกี่ยวกับ protothreads ดูเหมือนจะเป็นเทคนิคที่น่าสนใจ
Arsenal

@CZAbhinav คุณกำลังพูดถึงอะไร เป็นรหัส C และไม่เกี่ยวข้องกับระบบปฏิบัติการของคุณ
Matt Young

3

การออกแบบขั้นพื้นฐานของฉันสำหรับ RTOS ที่มีการแบ่งเวลาน้อยที่สุดไม่ได้เปลี่ยนแปลงอะไรมากมายในตระกูลไมโครหลายตระกูล โดยทั่วไปแล้วตัวจับเวลาจะขัดจังหวะการขับเครื่องรัฐ รูทีนการบริการขัดจังหวะคือเคอร์เนลระบบปฏิบัติการในขณะที่คำสั่งสวิตช์ในลูปหลักคืองานของผู้ใช้ ไดรเวอร์อุปกรณ์คือรูทีนบริการขัดจังหวะสำหรับการขัดจังหวะ I / O

โครงสร้างพื้นฐานมีดังนี้:

unsigned char tick;

void interrupt HANDLER(void) {
    device_driver_A();
    device_driver_B();
    if(T0IF)
    {
        TMR0 = TICK_1MS;
        T0IF = 0;   // reset timer interrupt
        tick ++;
    }
}

void main(void)
{
    init();

    while (1) {
        // periodic tasks:
        if (tick % 10 == 0) { // roughly every 10 ms
            task_A();
            task_B();    
        }
        if (tick % 55 == 0) { // roughly every 55 ms
            task_C();
            task_D();    
        }

        // tasks that need to run every loop:
        task_E();
        task_F();
    }
}

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

คุณสามารถดูตัวอย่างของรูปแบบสถาปัตยกรรมนี้ในซอฟต์แวร์เครื่องส่งสัญญาณ RC ของฉัน (ใช่ฉันใช้เพื่อบินเครื่องบิน RC ดังนั้นจึงค่อนข้างปลอดภัยที่สำคัญเพื่อป้องกันไม่ให้เครื่องบินของฉันพังและอาจฆ่าผู้คน): https://github.com / โดยทั่วไปมี 3 งาน - 2 งานแบบเรียลไทม์ที่ใช้งานเป็นไดรเวอร์อุปกรณ์ stateful (ดูสิ่ง ppmio) และงานพื้นหลัง 1 งานที่ใช้ตรรกะการผสม ดังนั้นโดยทั่วไปมันคล้ายกับเว็บเซิร์ฟเวอร์ของคุณโดยที่มี 2 เธรด I / O


1
ฉันจะไม่เรียกมันว่า 'การทำงานร่วมกันแบบหลายส่วน' เพราะมันไม่ได้แตกต่างไปจากโปรแกรมไมโครคอนโทรลเลอร์อื่น ๆ ที่ต้องทำหลายอย่างมาก
whatsisname

2

ในขณะที่ฉันชื่นชมว่าคำถามนี้ถามเฉพาะเกี่ยวกับการใช้ RTOS แบบฝังตัว แต่สำหรับฉันแล้วคำถามที่กว้างขึ้นที่ถูกถามคือ "วิธีการทำมัลติทาสก์บนแพลตฟอร์มแบบฝังตัว"

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

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

ภาพประกอบหยาบ:

for(;;)
{
    main_lcd_ui_tick();
    networking_tick();
}


...

// In your LCD UI module:
void main_lcd_ui_tick(void)
{
    check_for_key_presses();
    update_lcd();
}

...

// In your networking module:
void networking_tick(void)
{
    //'Tick' the TCP/IP library. In this example, I'm periodically
    //calling the main function for Keil's TCP/IP library.
    main_TcpNet();
}

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

ฉันทำงานกับอุปกรณ์ฝังตัวที่มีอินเตอร์เฟส LCD ของฮาร์ดแวร์เว็บเซิร์ฟเวอร์ภายในไคลเอนต์อีเมลไคลเอนต์ DDNS VOIP และคุณสมบัติอื่น ๆ อีกมากมาย แม้ว่าเราจะใช้ RTOS (Keil RTX) แต่จำนวนเธรด (งาน) ที่ใช้นั้นมีขนาดเล็กมากและส่วนใหญ่ของ 'การทำงานหลายภารกิจ' จะสำเร็จตามที่อธิบายไว้ข้างต้น

ในการให้ตัวอย่างห้องสมุดที่แสดงแนวคิดนี้:

  1. ไลบรารีเครือข่าย Keil สแต็ก TCP / IP ทั้งหมดสามารถทำงานแบบเธรดเดียว คุณเรียก main_TcpNet () เป็นระยะซึ่งวนซ้ำ TCP / IP และตัวเลือกเครือข่ายอื่น ๆ ที่คุณรวบรวมจากไลบรารี (เช่นเว็บเซิร์ฟเวอร์) ดูhttp://www.keil.com/support/man/docs/rlarm/rlarm_main_tcpnet.htm เป็นที่ยอมรับในบางสถานการณ์ (อาจอยู่นอกขอบเขตของคำตอบนี้) คุณถึงจุดที่เริ่มเป็นประโยชน์หรือจำเป็นต้องใช้เธรด (โดยเฉพาะถ้าใช้บล็อกซ็อกเก็ต BSD) (หมายเหตุเพิ่มเติม: V5 MDK-ARM ใหม่วางไข่เธรดอีเทอร์เน็ตโดยเฉพาะ - แต่ฉันแค่พยายามแสดงภาพประกอบ)

  2. ห้องสมุด Linphone VOIP ไลบรารี linphone นั้นมีเธรดเดียว คุณเรียกใช้iterate()ฟังก์ชันในช่วงเวลาที่เพียงพอ ดูhttp://www.linphone.org/docs/liblinphone-javadoc/org/linphone/core/LinphoneCore.html#iterate () (บิตของตัวอย่างที่ไม่ดีเพราะฉันใช้สิ่งนี้บนแพลตฟอร์มลินุกซ์ในตัวและลินุกซ์พึ่งพาห้องสมุดของวางไข่เธรดอย่างไม่ต้องสงสัย แต่อีกครั้งมันแสดงให้เห็นถึงจุด)

กลับไปที่ปัญหาที่ระบุโดย OP ปัญหาดูเหมือนว่าเป็นความจริงที่ว่าการสื่อสาร UART จะต้องเกิดขึ้นพร้อมกันกับเครือข่ายบางอย่าง (การส่งแพ็กเก็ตผ่าน TCP / IP) ฉันไม่รู้ว่าคุณใช้ห้องสมุดเครือข่ายแบบใด แต่ฉันคิดว่ามันมีฟังก์ชั่นหลักที่จำเป็นต้องเรียกใช้บ่อยครั้ง คุณจะต้องเขียนรหัสของคุณที่เกี่ยวข้องกับการส่ง / รับข้อมูล UART เพื่อจัดโครงสร้างในลักษณะที่คล้ายกันเนื่องจากเป็นเครื่องสถานะที่สามารถทำซ้ำได้โดยการโทรเป็นระยะไปยังฟังก์ชันหลัก


2
ขอบคุณสำหรับคำอธิบายที่ดีนี้ฉันใช้ห้องสมุด TCP / IP ที่จัดทำโดยไมโครชิปและมันเป็นรหัสที่ซับซ้อนมาก ฉันสามารถแบ่งมันออกเป็นส่วน ๆ และทำให้สามารถใช้งานได้ตามความต้องการของฉัน ฉันจะลองแนวทางของคุณอย่างแน่นอน!
เครื่องบิน

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

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