- การลบงานใน FreeRTOS Arduino
- คิวใน FreeRTOS คืออะไร?
- การสร้างคิวใน FreeRTOS
- แผนภูมิวงจรรวม
- การใช้ FreeRTOS Queue ใน Arduino IDE
ในบทช่วยสอนก่อนหน้านี้เราได้แนะนำ FreeRTOS ใน Arduino Uno และสร้างงานสำหรับ LED ที่กะพริบ ตอนนี้ในบทช่วยสอนนี้เราจะเจาะลึกแนวคิดขั้นสูงของ RTOS API และเรียนรู้เกี่ยวกับการสื่อสารระหว่างงานต่างๆ ที่นี่เรายังได้เรียนรู้เกี่ยวกับคิวที่จะถ่ายโอนข้อมูลจากงานหนึ่งไปยังอีกและแสดงให้เห็นถึงการทำงานของ APIs คิวโดยการเชื่อมต่อจอแอลซีดี 16x2 และ LDR กับ Arduino Uno
ก่อนที่จะพูดคุยเกี่ยวกับ Queues เรามาดู FreeRTOS API อีกหนึ่งรายการซึ่งมีประโยชน์ในการลบงานเมื่อเสร็จสิ้นกับงานที่ได้รับมอบหมาย บางครั้งจำเป็นต้องลบงานเพื่อเพิ่มหน่วยความจำที่กำหนด ในความต่อเนื่องของบทช่วยสอนก่อนหน้านี้เราจะใช้ ฟังก์ชัน vTaskDelete () API ในรหัสเดียวกันเพื่อลบงานอย่างใดอย่างหนึ่ง งานสามารถใช้ ฟังก์ชัน vTaskDelete () API เพื่อลบตัวเองหรืองานอื่น ๆ
ในการใช้ API นี้คุณต้องกำหนดค่าไฟล์ FreeRTOSConfig.h ไฟล์นี้ใช้เพื่อปรับแต่ง FreeRTOS ตามแอปพลิเคชัน ใช้เพื่อเปลี่ยนอัลกอริทึมการตั้งเวลาและพารามิเตอร์อื่น ๆ อีกมากมาย ไฟล์สามารถพบได้ใน Arduino Directory ซึ่งโดยทั่วไปมีอยู่ในโฟลเดอร์ Documents ของพีซีของคุณ ในกรณีของฉันมีอยู่ใน \ Documents \ Arduino \ libraries \ FreeRTOS \ src ดังที่แสดงด้านล่าง
ตอนนี้เปิดไฟล์นี้โดยใช้โปรแกรมแก้ไขข้อความใด ๆ และค้นหา #define INCLUDE_vTaskDelete และให้แน่ใจว่าค่าที่เป็น '1' (1 วิธีเปิดใช้งานและ 0 หมายถึงการปิดการใช้งาน) เป็น 1 โดยค่าเริ่มต้น แต่ตรวจสอบ
เราจะใช้ไฟล์กำหนดค่านี้บ่อยๆในบทช่วยสอนถัดไปสำหรับการตั้งค่าพารามิเตอร์
ตอนนี้เรามาดูวิธีการลบงาน
การลบงานใน FreeRTOS Arduino
ในการลบงานเราต้องใช้ฟังก์ชัน vTaskDelete () API ใช้เวลาเพียงหนึ่งอาร์กิวเมนต์
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete:เป็นหมายเลขอ้างอิงของงานที่จะลบ มันเป็นเช่นเดียวกับ 6 THข้อโต้แย้งของ xTaskCreate () API ในบทช่วยสอนก่อนหน้าอาร์กิวเมนต์นี้ถูกตั้งค่าเป็น NULL แต่คุณสามารถส่งที่อยู่ของเนื้อหาของงานโดยใช้ชื่อใดก็ได้ สมมติว่าคุณต้องการตั้งค่าตัวจัดการงานสำหรับ Task2 ซึ่งประกาศเป็น
TaskHandle_t any_name; ตัวอย่าง: TaskHandle_t xTask2Handle;
ขณะนี้ใน vTaskCreate () API ตั้ง 6 THอาร์กิวเมนต์เป็น
xTaskCreate (TaskBlink2, "task2", 128, NULL, 1, & xTask2Handle);
ขณะนี้เนื้อหาของงานนี้สามารถเข้าถึงได้โดยใช้หมายเลขอ้างอิงที่คุณกำหนด
นอกจากนี้งานสามารถลบตัวเองได้โดยการส่ง NULL แทนตัวจัดการงานที่ถูกต้อง
ถ้าเราต้องการลบ Task 3 ออกจากงาน 3 เองคุณต้องเขียน vTaskDelete (NULL); ภายในฟังก์ชัน Task3 แต่ถ้าคุณต้องการลบงาน 3 จากงาน 2 ให้เขียน vTaskDelete (xTask3Handle); ภายในฟังก์ชัน task2
ในรหัสการสอนก่อนหน้านี้หากต้องการลบ Task2 ออกจาก task2 เพียงเพิ่ม vTaskDelete (NULL) ใน โมฆะ TaskBlink2 (void * pvParameters) ฟังก์ชั่น จากนั้นฟังก์ชันด้านบนจะเป็นดังนี้
โมฆะ TaskBlink2 (โมฆะ * pvParameters) { Serial.println (“ Task2 กำลังทำงานและกำลังจะลบ”); vTaskDelete (NULL); PinMode (7, เอาท์พุท); ในขณะที่ (1) { digitalWrite (7, HIGH); vTaskDelay (300 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (300 / portTICK_PERIOD_MS); } }
ตอนนี้อัปโหลดรหัสและสังเกตไฟ LED และจอภาพอนุกรม คุณจะเห็นว่า LED ที่สองไม่กะพริบในขณะนี้และ task2 จะถูกลบหลังจากพบกับลบ API
ดังนั้นจึงสามารถใช้ API นี้เพื่อหยุดการทำงานของงานนั้น ๆ
ตอนนี้เรามาเริ่มต้นด้วยคิว
คิวใน FreeRTOS คืออะไร?
คิวคือโครงสร้างข้อมูลที่สามารถเก็บองค์ประกอบขนาดคงที่จำนวน จำกัด และดำเนินการในรูปแบบ FIFO (เข้าก่อนออกก่อน) คิวจัดเตรียมกลไกการสื่อสารระหว่างภารกิจงานต่องานขัดจังหวะและขัดจังหวะงาน
จำนวนคิวองค์ประกอบสูงสุดที่สามารถรองรับได้เรียกว่า "ความยาว" ทั้งความยาวและขนาดของแต่ละองค์ประกอบจะถูกกำหนดเมื่อสร้างคิว
ตัวอย่างวิธีใช้คิวสำหรับการถ่ายโอนข้อมูลแสดงได้ดีในเอกสาร FreeRTOS ซึ่งสามารถพบได้ที่นี่ คุณสามารถเข้าใจตัวอย่างที่กำหนดได้อย่างง่ายดาย
หลังจากทำความเข้าใจกับ Queues แล้วเรามาลองทำความเข้าใจกระบวนการสร้างคิวและลองใช้งานในโค้ด FreeRTOS ของเรา
การสร้างคิวใน FreeRTOS
ขั้นแรกอธิบายคำชี้แจงปัญหาที่จะดำเนินการด้วยความช่วยเหลือของคิว FreeRTOS และ Arduino Uno
เราต้องการที่จะพิมพ์ค่าของเซ็นเซอร์ของ LDR เมื่อวันที่ 16 * 2 จอแอลซีดี ตอนนี้มีสองงาน
- Task1 ได้รับค่าแอนะล็อกของ LDR
- Task2 กำลังพิมพ์ค่าอนาล็อกบน LCD
ดังนั้นคิวที่นี่จึงมีบทบาทเพราะส่งข้อมูลที่สร้างโดย task1 ไปยัง task2 ใน task1 เราจะส่งค่าอนาล็อกไปยังคิวและใน task2 เราจะได้รับจากคิว
มีสามฟังก์ชันในการทำงานกับคิว
- การสร้างคิว
- การส่งข้อมูลไปยัง Queue
- การรับข้อมูลจากคิว
สำหรับการสร้างคิวให้ใช้xQueueCreate () function API ต้องใช้สองอาร์กิวเมนต์
xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength:จำนวนรายการสูงสุดที่คิวที่สร้างขึ้นสามารถเก็บได้ในคราวเดียว
uxItemSize:ขนาดเป็นไบต์ของแต่ละรายการข้อมูลที่สามารถเก็บไว้ในคิว
ถ้าฟังก์ชันนี้คืนค่า NULL แสดงว่าคิวจะไม่ถูกสร้างขึ้นเนื่องจากหน่วยความจำไม่เพียงพอและหากส่งคืนค่าที่ไม่ใช่ NULL คิวจะถูกสร้างสำเร็จ เก็บค่าที่ส่งคืนนี้ให้กับตัวแปรเพื่อใช้เป็นจุดจับในการเข้าถึงคิวดังที่แสดงด้านล่าง
QueueHandle_t Queue1; คิว 1 = xQueueCreate (4, sizeof (int));
สิ่งนี้จะสร้างคิวองค์ประกอบ 4 ในหน่วยความจำฮีปที่มีขนาด int (2 ไบต์ของแต่ละบล็อก) และเก็บค่าที่ส่งคืนไปยังตัวแปรที่จับ que1
2. การส่งข้อมูลไปยังคิวใน FreeRTOS
ในการส่งค่าไปยังคิว FreeRTOS มี API 2 รูปแบบสำหรับวัตถุประสงค์นี้
- xQueueSendToBack (): ใช้เพื่อส่งข้อมูลไปยังด้านหลัง (tail) ของคิว
- xQueueSendToFront (): ใช้เพื่อส่งข้อมูลไปยังด้านหน้า (ส่วนหัว) ของคิว
ตอนนี้, xQueueSend () เทียบเท่ากับและเหมือนกับ, xQueueSendToBack ()
API ทั้งหมดนี้ใช้ 3 อาร์กิวเมนต์
xQueueSendToBack (QueueHandle_t xQueue, const เป็นโมฆะ * pvItemToQueue, TickType_t xTicksToWait);
xQueue:หมายเลขอ้างอิงของคิวที่จะส่งข้อมูล (เขียน) ตัวแปรนี้เหมือนกับที่ใช้ในการจัดเก็บค่าที่ส่งคืนของ xQueueCreate API
pvItemToQueue:ตัวชี้ไปยังข้อมูลที่จะคัดลอกลงในคิว
xTicksToWait:ระยะเวลาสูงสุดที่งานควรอยู่ในสถานะถูกปิดกั้นเพื่อรอให้มีพื้นที่ว่างในคิว
การตั้งค่า xTicksToWait เป็น portMAX_DELAY จะทำให้งานรอไปเรื่อย ๆ (โดยไม่หมดเวลา) โดยให้ INCLUDE_vTaskSuspend ตั้งค่าเป็น 1 ใน FreeRTOSConfig มิฉะนั้นคุณสามารถใช้มาโคร pdMS_TO_TICKS () เพื่อแปลงเวลาที่ระบุเป็นมิลลิวินาทีเป็นเวลาที่ระบุในเห็บ
3. การรับข้อมูลจากคิวใน FreeRTOS
ในการรับ (อ่าน) รายการจากคิวจะใช้xQueueReceive () รายการที่ได้รับจะถูกลบออกจากคิว
API นี้ยังรับสามอาร์กิวเมนต์
xQueueReceive (QueueHandle_t xQueue, โมฆะ * const pvBuffer, TickType_t xTicksToWait);
อาร์กิวเมนต์แรกและที่สามเหมือนกับการส่ง API เฉพาะอาร์กิวเมนต์ที่สองเท่านั้นที่แตกต่างกัน
const pvBuffer:ตัวชี้ไปยังหน่วยความจำที่ข้อมูลที่ได้รับจะถูกคัดลอก
หวังว่าคุณจะเข้าใจ API ทั้งสาม ตอนนี้เราจะใช้ API เหล่านี้ใน Arduino IDE และพยายามแก้ปัญหาคำชี้แจงที่เราได้อธิบายไว้ข้างต้น
แผนภูมิวงจรรวม
นี่คือลักษณะที่ปรากฏบนเขียงหั่นขนม:
การใช้ FreeRTOS Queue ใน Arduino IDE
มาเริ่มเขียนโค้ดสำหรับแอปพลิเคชันของเรา
1. ขั้นแรกให้เปิด Arduino IDE และรวมไฟล์ส่วนหัว Arduino_FreeRTOS.h ตอนนี้หากใช้วัตถุเคอร์เนลเช่นคิวให้รวมไฟล์ส่วนหัวของมัน เนื่องจากเราใช้ LCD 16 * 2 ดังนั้นให้รวมไลบรารีไว้ด้วย
# รวม # รวม
2. เริ่มต้นที่จับคิวเพื่อจัดเก็บเนื้อหาของคิว เริ่มต้นหมายเลขพิน LCD ด้วย
QueueHandle_t que_1; LiquidCrystal LCD (7, 8, 9, 10, 11, 12);
3. ใน การตั้งค่าเป็นโมฆะ () ให้ เริ่มต้น LCD และมอนิเตอร์แบบอนุกรมด้วยอัตราบอด 9600 สร้างคิวและสองงานโดยใช้ API ที่เกี่ยวข้อง ที่นี่เราจะสร้างคิวขนาด 4 ที่มีประเภทจำนวนเต็ม สร้างงานที่มีลำดับความสำคัญเท่ากันแล้วลองเล่นกับหมายเลขนี้ในภายหลัง สุดท้ายเริ่มตัวกำหนดตารางเวลาตามที่แสดงด้านล่าง
การตั้งค่าเป็นโมฆะ () { Serial.begin (9600); lcd.begin (16, 2); คิว_1 = xQueueCreate (4, sizeof (int)); ถ้า (Queue_1 == NULL) { Serial.println ("ไม่สามารถสร้างคิวได้"); } xTaskCreate (TaskDisplay, "Display_task", 128, NULL, 1, NULL); xTaskCreate (TaskLDR, "LDR_task", 128, NULL, 1, NULL); vTaskStartScheduler (); }
4. ตอนนี้ทำให้ทั้งสองฟังก์ชั่น TaskDisplay และTaskLDR ในฟังก์ชัน TaskLDR อ่านอะนาล็อกพิน A0 ในตัวแปรเมื่อเราเชื่อมต่อ LDR กับพิน A0 ของ Arduino UNO ตอนนี้ส่งค่าที่เก็บไว้ในตัวแปรโดยส่งผ่านใน xQueueSend API และส่งงานเพื่อบล็อกสถานะหลังจากผ่านไป 1 วินาทีโดยใช้ vTaskDelay () API ดังที่แสดงด้านล่าง
โมฆะ TaskLDR (โมฆะ * pvParameters) { int current_intensity; ในขณะที่ (1) { Serial.println ("Task1"); current_intensity = analogRead (A0); Serial.println (current_intensity); xQueueSend (que_1, & current_intensity, portMAX_DELAY); vTaskDelay (1000 / portTICK_PERIOD_MS); } }
5. ในทำนองเดียวกันให้สร้างฟังก์ชันสำหรับ TaskDisplay และรับค่าในตัวแปรที่ส่งผ่านไปยังฟังก์ชัน xQueueReceive นอกจากนี้ xQueueReceive () จะส่งคืน pdPASS หากสามารถรับข้อมูลจากคิวได้สำเร็จและส่งกลับค่า errQUEUE_EMPTY หากคิวว่างเปล่า
ตอนนี้แสดงค่าที่จอแอลซีดีโดยใช้ lcd.print () ฟังก์ชั่น
โมฆะ TaskDisplay (โมฆะ * pvParameters) { int ความเข้ม = 0; ในขณะที่ (1) { Serial.println ("Task2"); ถ้า (xQueueReceive (que_1, & ความเข้ม, portMAX_DELAY) == pdPASS) { lcd.clear (); lcd.setCursor (0, 0); lcd.print ("ความเข้ม:"); lcd.setCursor (11, 0); lcd.print (ความเข้ม); } } }
แค่นั้นแหละ. เราได้เสร็จสิ้นส่วนการเข้ารหัสของการใช้งานคิวแล้ว คุณสามารถดูรหัสที่สมบูรณ์พร้อมวิดีโอที่ใช้งานได้ในตอนท้าย
ตอนนี้เชื่อมต่อ LCD และ LDR กับ Arduino UNO ตามแผนภาพวงจรอัปโหลดรหัส เปิดซีเรียลมอนิเตอร์และสังเกตงาน คุณจะเห็นงานกำลังเปลี่ยนและค่า LDR จะเปลี่ยนไปตามความเข้มของแสง
หมายเหตุ:ไลบรารีส่วนใหญ่ที่สร้างขึ้นสำหรับเซ็นเซอร์ที่แตกต่างกันไม่ได้รับการสนับสนุนโดยเคอร์เนล FreeRTOS เนื่องจากการใช้ฟังก์ชันล่าช้าภายในไลบรารี ความล่าช้าทำให้ CPU หยุดทำงานอย่างสมบูรณ์ดังนั้นเคอร์เนล FreeRTOS ก็หยุดทำงานและโค้ดจะไม่ทำงานต่อไปและเริ่มทำงานผิดปกติ ดังนั้นเราจึงต้องทำให้ไลบรารีไม่ล่าช้าเพื่อทำงานกับ FreeRTOS