บทความ ESPino32 ตอนที่ 12 การใช้งานมัลติคอร์บนบอร์ด ESPino32

สารบัญ บทความ ESPino32

บทความ ESPino32 ตอนที่ 12 การใช้งานมัลติคอร์บนบอร์ด ESPino32

          บทความ ESPino32 ตอนที่ 12 การใช้งานมัลติคอร์บนบอร์ด ESPino32 เป็นบทความต่อจากบทความ “การใช้งานมัลติทาสก์บนบอร์ด ESPino32” โดยในบทความก่อนหน้านี้จะเป็นการแบ่งหน่วยประมวลผล(CPU) ออกเป็นส่วนต่างๆ (ด้วยซอร์ฟแวร์) ซึ่งโปรแกรมต่างๆนั้นจะทำงานบน CPU Core ต่างๆ ดังรูป โปรแกรมที่ทำงานบน cpu ของบอร์ด ESPino32 แต่ในบทความนี้จะเพิ่มเติมในส่วนของการเขียนโปรแกรมโดยการกำหนดโปรแกรมต่างๆให้ไปทำงานใน CPU Core ที่ผู้ใช้งานต้องการ ทำให้บอร์ด ESPino32 มีประสิทธิภาพในการทำงานมากขึ้น

โปรแกรมที่ทำงานบน cpu ของบอร์ด ESPino32

        จากรูป โปรแกรมที่ทำงานบน CPU ของบอร์ด ESPino32 จะเห็นได้ว่าฟังก์ชันต่างๆ เนื่องจากที่โปรแกรมถูกแยกไปทำงานใน CPU Core ที่ต่างกัน เพราะในฟังก์ชัน main.cpp กำหนดว่า หากสร้างทาสก์ใหม่ขึ้นมาทาสก์นั้นจะถูกกำหนดให้ทำงานที่ CPU Core 0 และหากเป็นฟังก์ชันที่ทำงานในฟังก์ชัน setup หรือ ฟังก์ชัน loop โปรแกรมจะสร้าง LoopTask ขึ้นมา ซึ่ง LoopTask นั้นจะไปทำงานบน CPU Core 1 ดังรูป โปรแกรมในฟังก์ชัน main.cpp

โปรแกรมในฟังก์ชัน main.cpp

ขั้นตอนการตรวจสอบว่าโปรแกรมประมวลผลอยู่บน CPU Core ใด

            หากต้องการตรวจสอบว่าโปรแกรมประมวลผลอยู่ที่ CPU Core ใด ผู้ใช้งานสามารถตรวจสอบได้โดยเรียกใช้งานฟังก์ชัน

ซึ่งฟังก์ชันนี้ ค่าที่ตอบกลับมา คือ หมายเลข Core ของ CPU ที่ใช้ประมวลผลอยู่

ตัวอย่างโปรแกรมที่ 1 แสดงการทำงานของโปรแกรมบน CPU Core

        ตัวอย่างนี้เป็นตัวอย่างการพัฒนาโปรแกรมโดยการใช้คำสั่ง xPortGetCoreID() เพื่อแสดงผลว่าโปรแกรมในฟังก์ชัน setup() หรือโปรแกรมในฟังก์ชัน loop() ของบอร์ด ESPino32 ทำงานอยู่บน CPU Core ใด

ตัวอย่างโปรแกรมที่ใช้


บรรทัดที่ 4         แสดงคำว่า “Program run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor

ผลการทดลอง

        เมื่อบอร์ด ESPino32 เริ่มทำงาน โปรแกรมในฟังก์ชัน setup() จะเรียกใช้งานฟังก์ชัน Serial และแสดงคำว่า “Program run on core ” + String(xPortGetCoreID()) ซึ่งค่าที่แสดงนั้นคือตำแหน่งของ CPU Core ที่โปรแกรมทำงานอยู่ ดังรูป ตัวอย่างโปรแกรมที่ 1 แสดงการทำงานของโปรแกรมบน CPU Core

ตัวอย่างโปรแกรมที่ 1 แสดงการทำงานของโปรแกรมบน CPU Core

ตัวอย่างที่ 2 แสดงการทำงานของโปรแกรมบน CPU Core

        ตัวอย่างนี้เป็นตัวอย่างการพัฒนาโปรแกรมโดยสร้างฟังก์ชันขึ้นจำนวน 3 ฟังก์ชัน คือ FunctionX(), FunctionY() และ FunctionZ() โดยไม่ระบุให้ทำงานบนทาสก์ฟังก์ชัน เพื่อแสดงผลว่าโปรแกรมในฟังก์ชัน setup() หรือโปรแกรมในฟังก์ชัน loop() ของบอร์ด ESPino32 ทำงานอยู่บน CPU Core ใด

ตัวอย่างโปรแกรมที่ใช้

บรรทัดที่ 1-4      functionX() สำหรับแสดงคำว่า “FunctionX run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor
บรรทัดที่ 5-8      functionY() สำหรับแสดงคำว่า “FunctionY run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor
บรรทัดที่ 9-12     functionZ() สำหรับแสดงคำว่า “FunctionZ run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor
บรรทัดที่ 13-19   ฟังก์ชัน setup() เรียกใช้งาน functionX(), functionY() และ functionZ() ตามลำดับ

ผลการทดลอง

            เมื่อบอร์ด ESPino32 เริ่มทำงาน โปรแกรมในฟังก์ชัน setup() จะเรียกใช้งานฟังก์ชัน Serial และเรียกใช้งานฟังก์ชัน functionX(), functionY() และ functionZ() โดยในฟังก์ชันต่างๆจะแสดงคำว่า “Function… run on core ” + String(xPortGetCoreID()) ซึ่งค่าที่แสดงนั้นคือตำแหน่งของ CPU Core ที่โปรแกรมทำงานอยู่ ดังรูป ตัวอย่างโปรแกรมที่ 2 แสดงการทำงานของโปรแกรมบน CPU Core

ตัวอย่างโปรแกรมที่ 2 แสดงการทำงานของโปรแกรมบน CPU Core

ตัวอย่างโปรแกรมที่ 3 แสดงการทำงานของโปรแกรมบน CPU Core

        ตัวอย่างนี้เป็นตัวอย่างการพัฒนาโปรแกรมโดยสร้างทาสก์ฟังก์ชันขึ้นจำนวน 2 ฟังก์ชัน คือ taskOne() และ taskTwo() เพื่อแสดงผลว่าโปรแกรมในทาสก์ฟังก์ชัน และโปรแกรมในฟังก์ชัน setup() หรือโปรแกรมในฟังก์ชัน loop() ของบอร์ด ESPino32 ทำงานอยู่บน CPU Core ใด

ตัวอย่างโปรแกรมที่ใช้

บรรทัดที่ 1-5      taskOne() สำหรับแสดงคำว่า “TaskOne run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor
บรรทัดที่ 6-10     taskTwo() สำหรับแสดงคำว่า “TaskTwo run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor
บรรทัดที่ 11-19   ฟังก์ชัน setup() เรียกใช้งานทาสก์ฟังก์ชัน taskOne(), taskTwo(), และแสดงคำว่า “Setup Function run on core ” + String(xPortGetCoreID()) และการขึ้นบรรทัดใหม่ออกทาง Serial Monitor

ผลการทดลอง

        เมื่อบอร์ด ESPino32 เริ่มทำงาน โปรแกรมในฟังก์ชัน setup() จะใช้งานฟังก์ชัน Serial และเรียกใช้งานทาสก์ฟังก์ชัน taskOne(), taskTwo() โดยในทาสก์ฟังก์ชันต่างๆจะแสดงคำว่า “Task… run on core ” + String(xPortGetCoreID()) และในฟังก์ชัน setup() ได้มีการแสดงคำว่า “Setup Function run on core ” + String(xPortGetCoreID()) ซึ่งค่าที่แสดงต่างๆนั้นคือตำแหน่งของ CPU Core ที่โปรแกรมทำงานอยู่ ดังรูป ตัวอย่างโปรแกรมที่ 3 แสดงการทำงานของโปรแกรมบน CPU Core

ตัวอย่างโปรแกรมที่ 3 แสดงการทำงานของโปรแกรมบน CPU Core

ขั้นตอนการพัฒนาโปรแกรมแบบมัลติคอร์โดยผู้ใช้สามารถกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ

        สำหรับการพัฒนาโปรแกรมแบบมัลติคอร์นี้ จะเห็นว่าการเขียนโปรแกรมแบบมัลติทาสก์นั้น CPU ทั้ง 2 Core ของบอร์ด ESPino32 จะถูก freeRTOS แบ่งให้โปรแกรมทำงานใน CPU Core ต่างๆ คือ

– ฟังก์ชันที่ถูกสร้างด้วยทาสก์ฟังก์ชันจะทำงานบน CPU Core 0 เท่านั้น

– ฟังก์ชันที่ถูกสร้างในฟังก์ชัน setup() หรือฟังก์ชัน loop() จะทำงานใน CPU Core 1

ในขั้นตอนนี้จะเป็นการพัฒนาโปรแกรมโดยผู้ใช้งานสามารถกำหนดให้ทาสก์ฟังก์ชันที่ถูกสร้างขึ้นมาทำงานใน CPU Core 0 หรือ CPU Core 1 ตามต้องการ

ส่วนประกอบหลักในการเขียนโปรแกรมแบบมัลติคอร์โดยผู้ใช้สามารถกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ มีอยู่ 2 ส่วนคือ

1. ฟังก์ชัน setup

2. ฟังก์ชันที่ถูกเรียกใช้งาน

ตัวอย่างที่ 1 การใช้งานมัลติคอร์โดยกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ

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

ตัวอย่างโปรแกรมที่ใช้

บรรทัดที่ 7-18     ฟังก์ชัน setup() เรียกใช้งาน xTaskCreatePinnedToCore() เพื่อสร้างทาสก์ฟังก์ชันบน CPU Core1 โดยมีพารามิเตอร์คือ coreTask, “coreTask”, 10000, NULL, 1,NULL และ taskCore

ผลการทดลอง

        เมื่อบอร์ด ESPino32 เริ่มทำงาน โปรแกรมในฟังก์ชัน setup() จะใช้งานฟังก์ชัน Serial และ เรียกใช้งาน xTaskCreatePinnedToCore() เพื่อสร้างทาสก์ฟังก์ชันขึ้นมา ซึ่งทาสก์ฟังก์ชันนั้นมีพารามิเตอร์ taskCore ที่กำหนดให้โปรแกรมทำงานที่ CPU Core1 ดูผลลัพธ์ของโปรแกรมได้จาก ตัวอย่างโปรแกรมที่ 1 การใช้งานมัลติคอร์โดยกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ (ซ้าย กำหนดให้โปรแกรมทำงานที่ CPU Core1)

        หากทำการทดสอบตัวอย่างการทำงานเสร็จแล้วลองเปลี่ยนตัวอย่างโปรแกรมโดยกำหนดให้ taskCore = 0เพื่อเปลี่ยนจากนั้นทดสอบเปลี่ยนการทำงานมาที่ CPU Core0 แทน ดูผลลัพธ์ของโปรแกรมได้จาก ตัวอย่างโปรแกรมที่ 1 การใช้งานมัลติคอร์โดยกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ

ตัวอย่างโปรแกรมที่ 1 การใช้งานมัลติคอร์โดยกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ
(บน กำหนดให้โปรแกรมทำงานที่ CPU Core1)
(ล่าง กำหนดให้โปรแกรมทำงานที่ CPU Core0)

ตัวอย่างที่ 2 ตัวอย่างการใช้งานมัลติคอร์โดยกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ

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

– สลับการทำงานของ LED ทุกๆ 1 วินาที

– อ่านค่าจากสวิทต์กดติด-ปล่อยดับ

– ซิงค์เวลาจาก NTP เซิร์ฟเวอร์ แล้วให้บอร์ด ESPino32 ทำงานเป็นนาฬิกา

ตัวอย่างโปรแกรมที่ใช้

บรรทัดที่ 1-11     เรียกใช้งานไลบรารี่ และกำหนดค่าพารามิเตอร์ต่างๆ
บรรทัดที่ 12-22   ฟังก์ชัน coreTask0() สำหรับสลับการทำงานของ LED ทุกๆ 1 วินาที และแสดงผลการทำงานว่าโปรแกรมทำงานอยู่ที่ CPU Core ใด
บรรทัดที่ 23-36   ฟังก์ชัน coreTask1() สำหรับอ่านค่าจากการกดสวิตท์กดติดปล่อยดับ และแสดงผลการทำงานว่าโปรแกรมทำงานอยู่ที่ CPU Core ใด
บรรทัดที่ 37-43   ฟังก์ชัน printLocalTime() สำหรับแสดงค่าเวลาออกทาง Serial Monitor
บรรทัดที่ 46-62   ฟังก์ชัน setup() สำหรับเชื่อมต่อ WiFi และเรียกใช้งาน xTaskCreatePinnedToCore() เพื่อสร้างทาสก์ฟังก์ชันบน coreTask0() ทำงานบน CPU Core0 และ coreTask1() ทำงานบน CPU Core1
บรรทัดที่ 63-68   ฟังก์ชัน loop() สำหรับแสดงผลเวลาที่ซิงค์มาจากอินเทอร์เน็ต และแสดงผลการทำงานว่าโปรแกรมทำงานอยู่ที่ CPU Core ใด

ผลการทดลอง

        เมื่อบอร์ด ESPino32 เริ่มทำงาน โปรแกรมในฟังก์ชัน setup() จะใช้งานฟังก์ชัน Serial และเรียกใช้งาน xTaskCreatePinnedToCore() เพื่อสร้างทาสก์ฟังก์ชันบน coreTask0() ทำงานบน CPU Core0 และ coreTask1() ทำงานบน CPU Core1   สามารถอธิบายการทำงานแยกได้เป็น 3 ส่วนดังนี้คือ

1. ฟังก์ชัน coreTask0() สำหรับสลับการทำงานของ LED ทุกๆ 1 วินาที และแสดงผลการทำงานว่าโปรแกรมทำงานอยู่ที่ CPU Core ใด
2. ฟังก์ชัน coreTask1() สำหรับอ่านค่าจากการกดสวิตท์กดติดปล่อยดับ และแสดงผลการทำงานว่าโปรแกรมทำงานอยู่ที่ CPU Core ใด
3. ฟังก์ชัน loop() สำหรับแสดงผลเวลาที่ซิงค์มาจากอินเทอร์เน็ต และแสดงผลการทำงานว่าโปรแกรมทำงานอยู่ที่ CPU Core ใด

ตัวอย่างที่ 2 ตัวอย่างการใช้งานมัลติคอร์โดยกำหนดให้โปรแกรมทำงานบน CPU Core ใดๆ

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

ขอขอบคุณแหล่งข้อมูลอ้างอิงจาก

– https://techtutorialsx.com/2017/05/09/esp32-running-code-on-a-specific-core/

สารบัญ บทความ ESPino32