ผลต่างระหว่างรุ่นของ "มัลติทาสกิ้งบนไมโครคอนโทรลเลอร์"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
แถว 65: แถว 65:
 
กลไกการทำงานของงาน (หรือเครื่องจักร) ข้างต้นเป็นดังนี้
 
กลไกการทำงานของงาน (หรือเครื่องจักร) ข้างต้นเป็นดังนี้
  
# เริ่มทำงานโดยเข้าสู่สถานะ ON ซึ่งมีการสั่งให้ LED สีเขียวติด และบันทึกเวลาปัจจุบันเป็นมิลลิวินาทีจากฟังก์ชัน <code>millis()</code> ไว้ในตัวแปร <code>ts</code>
+
# เริ่มทำงานโดยเข้าสู่สถานะ ON ซึ่งมีการสั่งให้ LED สีเขียวติด และบันทึกเวลาปัจจุบันเป็นมิลลิวินาทีจากฟังก์ชัน <code>millis()</code> ไว้ในตัวแปร <code>ts</code> (ย่อมาจาก timestamp)
 
#: ''(หมายเหตุ: ฟังก์ชัน millis() เป็นฟังก์ชันที่เฟรมเวิร์ก Arduino มีให้ใช้สำหรับตรวจสอบว่าไมโครคอนโทรลเลอร์ได้ทำงานมาเป็นระยะเวลานานกี่มิลลิวินาที)''
 
#: ''(หมายเหตุ: ฟังก์ชัน millis() เป็นฟังก์ชันที่เฟรมเวิร์ก Arduino มีให้ใช้สำหรับตรวจสอบว่าไมโครคอนโทรลเลอร์ได้ทำงานมาเป็นระยะเวลานานกี่มิลลิวินาที)''
 
# ตรวจสอบเวลาที่อยู่ในสถานะนี้โดยคำนวณค่า <code>millis()-ts</code> เกินค่า 1000 มิลลิวินาที
 
# ตรวจสอบเวลาที่อยู่ในสถานะนี้โดยคำนวณค่า <code>millis()-ts</code> เกินค่า 1000 มิลลิวินาที
 +
#* หากยังไม่เกินให้อยู่ในสถานะเดิม
 +
#* หากเกินแล้ว ให้เข้าสู่สถานะ OFF โดยบันทึกค่า <code>ts</code> เป็นเวลาที่เข้าสู่สถานะใหม่ใน และดับ LED สีเขียว
 +
# ดำเนินการในลักษณะเดียวกันเมื่ออยู่ในสถานะใหม่
  
 
= ไลบรารี Protothreads =
 
= ไลบรารี Protothreads =

รุ่นแก้ไขเมื่อ 08:46, 19 ตุลาคม 2557

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

ตัวอย่าง
เขียนเฟิร์มแวร์ที่ทำให้ LED สีเขียวบนบอร์ดพ่วงติด 1 วินาทีและดับ 0.5 วินาทีสลับกันไป ในขณะเดียวกันทำให้ LED สีแดงติด 0.7 วินาทีและดับ 0.3 วินาทีสลับกันไป

จะเห็นว่างานทั้งหมดประกอบด้วยงานย่อยสองงาน ที่ผ่านมานั้นหากใช้เฟรมเวิร์กของ Arduino การทำให้เพียง LED สีเขียวกระพริบตามที่กำหนดทำได้โดยการเขียนโค้ดในฟังก์ชัน loop ลักษณะนี้

// LED สีเขียวติด 1 วินาที ดับ 0.5 วินาที
void loop()
{
  digitalWrite(PIN_PC2, HIGH);
  delay(1000);
  digitalWrite(PIN_PC2, LOW);
  delay(500);
}

ในขณะที่การทำให้ LED สีแดงกระพริบจะใช้โค้ดดังนี้

// LED สีแดงติด 0.7 วินาที ดับ 0.3 วินาที
void loop()
{
  digitalWrite(PIN_PC0, HIGH);
  delay(700);
  digitalWrite(PIN_PC0, LOW);
  delay(300);
}

อย่างไรก็ตาม การให้ LED ทั้งสองดวงกระพริบตามจังหวะของตัวเองขนานกันไปนั้นไม่อาจทำได้โดยการรวมงานทั้งคู่เข้าด้วยกันอย่างตรงไปตรงมาเช่นนี้ได้

// โค้ดด้านล่างไม่ได้ทำงานตามพึงประสงค์

void taskGreen()
{
  digitalWrite(PIN_PC0, HIGH);
  delay(700);
  digitalWrite(PIN_PC0, LOW);
  delay(300);
}

void taskRed()
{
  digitalWrite(PIN_PC0, HIGH);
  delay(700);
  digitalWrite(PIN_PC0, LOW);
  delay(300);
}

void loop()
{
  taskGreen();
  taskRed();
}

ทั้งนี้เนื่องจากว่าฟังก์ชัน taskRed() ไม่มีการถูกเรียกจนกว่าฟังก์ชัน taskGreen() จะทำงานเสร็จในหนึ่งรอบซึ่งใช้เวลาทั้งสิ้น 1.5 วินาที ในทำนองเดียวกัน ระหว่างที่ฟังก์ชัน taskRed() ทำงานอยู่นั้นฟังก์ชัน taskGreen() ก็ไม่มีโอกาสได้ทำงานเช่นกัน

เครื่องจักรสถานะ

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

ผังภาพเครื่องจักรสถานะของการทำไฟสีเขียวติด 1 วินาที ดับครึ่งวินาที

กลไกการทำงานของงาน (หรือเครื่องจักร) ข้างต้นเป็นดังนี้

  1. เริ่มทำงานโดยเข้าสู่สถานะ ON ซึ่งมีการสั่งให้ LED สีเขียวติด และบันทึกเวลาปัจจุบันเป็นมิลลิวินาทีจากฟังก์ชัน millis() ไว้ในตัวแปร ts (ย่อมาจาก timestamp)
    (หมายเหตุ: ฟังก์ชัน millis() เป็นฟังก์ชันที่เฟรมเวิร์ก Arduino มีให้ใช้สำหรับตรวจสอบว่าไมโครคอนโทรลเลอร์ได้ทำงานมาเป็นระยะเวลานานกี่มิลลิวินาที)
  2. ตรวจสอบเวลาที่อยู่ในสถานะนี้โดยคำนวณค่า millis()-ts เกินค่า 1000 มิลลิวินาที
    • หากยังไม่เกินให้อยู่ในสถานะเดิม
    • หากเกินแล้ว ให้เข้าสู่สถานะ OFF โดยบันทึกค่า ts เป็นเวลาที่เข้าสู่สถานะใหม่ใน และดับ LED สีเขียว
  3. ดำเนินการในลักษณะเดียวกันเมื่ออยู่ในสถานะใหม่

ไลบรารี Protothreads