- RTOS hoạt động như thế nào?
- Các thuật ngữ thường được sử dụng trong RTOS
- Cài đặt Thư viện Arduino FreeRTOS
- Sơ đồ mạch
- Ví dụ về Arduino FreeRTOS- Tạo nhiệm vụ FreeRTOS trong Arduino IDE
- Triển khai tác vụ FreeRTOS trong Arduino IDE
Hệ điều hành hiện diện bên trong các thiết bị nhúng được gọi là RTOS (Hệ điều hành thời gian thực). Trong các thiết bị nhúng, các tác vụ thời gian thực là rất quan trọng trong đó thời gian đóng một vai trò rất quan trọng. Nhiệm vụ thời gian thực là Xác định thời gian có nghĩa là thời gian phản hồi cho bất kỳ sự kiện nào luôn không đổi để có thể đảm bảo rằng bất kỳ sự kiện cụ thể nào sẽ xảy ra vào một thời điểm cố định. RTOS được thiết kế để chạy các ứng dụng với thời gian rất chính xác và độ tin cậy cao. RTOS cũng hỗ trợ đa tác vụ với một lõi đơn.
Chúng tôi đã trình bày một hướng dẫn về cách sử dụng RTOS trong các hệ thống nhúng, nơi bạn có thể biết thêm về RTOS, sự khác biệt giữa hệ điều hành mục đích chung và RTOS, các loại RTOS khác nhau, v.v.
Trong hướng dẫn này, chúng ta sẽ bắt đầu với FreeRTOS. FreeRTOS là một lớp RTOS dành cho các thiết bị nhúng đủ nhỏ để chạy trên vi điều khiển 8/16-bit, mặc dù việc sử dụng nó không giới hạn ở những vi điều khiển này. Nó là một mã nguồn mở hoàn toàn và mã của nó có sẵn trên github. Nếu chúng ta biết một số khái niệm cơ bản về RTOS, thì việc sử dụng FreeRTOS sẽ rất dễ dàng vì nó có các API được ghi chép đầy đủ có thể được sử dụng trực tiếp trong mã mà không cần biết phần phụ trợ của mã. Toàn bộ tài liệu FreeRTOS có thể được tìm thấy tại đây.
Vì FreeRTOS có thể chạy trên MCU 8-bit nên nó cũng có thể chạy trên bảng Arduino Uno. Chúng tôi chỉ cần tải xuống thư viện FreeRTOS và sau đó bắt đầu triển khai mã bằng các API. Hướng dẫn này dành cho người mới bắt đầu hoàn chỉnh, dưới đây là các chủ đề, chúng tôi sẽ trình bày trong hướng dẫn Arduino FreeRTOS này:
- Cách RTOS hoạt động
- Một số thuật ngữ thường dùng trong RTOS
- Cài đặt FreeRTOS trong Arduino IDE
- Cách tạo FreeRTOS Tasks với ví dụ
RTOS hoạt động như thế nào?
Trước khi bắt đầu làm việc với RTOS, hãy xem Tác vụ là gì. Tác vụ là một đoạn mã được lập lịch trên CPU để thực thi. Vì vậy, nếu bạn muốn thực hiện một số tác vụ, thì nó nên được lên lịch bằng cách sử dụng độ trễ của hạt nhân hoặc sử dụng ngắt. Công việc này được thực hiện bởi Scheduler có trong kernel. Trong bộ xử lý lõi đơn, bộ lập lịch giúp các tác vụ thực thi trong một khoảng thời gian cụ thể nhưng có vẻ như các tác vụ khác nhau đang thực hiện đồng thời. Mọi nhiệm vụ đều chạy theo mức độ ưu tiên cho nó.
Bây giờ, hãy xem điều gì xảy ra trong nhân RTOS nếu chúng ta muốn tạo một tác vụ cho đèn LED nhấp nháy với khoảng thời gian một giây và đặt tác vụ này lên mức ưu tiên cao nhất.
Ngoài tác vụ LED, sẽ có một tác vụ nữa được tạo bởi hạt nhân, nó được gọi là tác vụ nhàn rỗi. Tác vụ nhàn rỗi được tạo khi không có tác vụ nào có sẵn để thực thi. Tác vụ này luôn chạy ở mức ưu tiên thấp nhất tức là mức ưu tiên 0. Nếu chúng ta phân tích đồ thị thời gian được đưa ra ở trên, có thể thấy rằng quá trình thực thi bắt đầu với một tác vụ LED và nó chạy trong một thời gian xác định, sau đó trong thời gian còn lại, tác vụ nhàn rỗi sẽ chạy cho đến khi xảy ra dấu hiệu ngắt. Sau đó, hạt nhân quyết định tác vụ nào phải được thực hiện theo mức độ ưu tiên của tác vụ và tổng thời gian đã trôi qua của tác vụ LED. Khi hoàn thành 1 giây, kernel chọn lại tác vụ đã dẫn để thực thi vì nó có mức ưu tiên cao hơn tác vụ nhàn rỗi, chúng ta cũng có thể nói rằng tác vụ LED ưu tiên tác vụ nhàn rỗi. Nếu có nhiều hơn hai nhiệm vụ có cùng mức độ ưu tiên thì chúng sẽ chạy theo kiểu vòng tròn trong một thời gian xác định.
Bên dưới biểu đồ trạng thái vì nó hiển thị việc chuyển tác vụ không chạy sang trạng thái đang chạy.
Mọi tác vụ mới được tạo đều ở trạng thái Sẵn sàng (một phần của trạng thái không chạy). Nếu tác vụ đã tạo (Task1) có mức ưu tiên cao nhất so với các tác vụ khác, thì nó sẽ chuyển sang trạng thái đang chạy. Nếu tác vụ đang chạy này đánh trước tác vụ kia, thì nó sẽ quay trở lại trạng thái sẵn sàng một lần nữa. Nếu không, nếu task1 bị chặn bằng cách sử dụng API chặn, thì CPU sẽ không tham gia vào tác vụ này cho đến khi hết thời gian chờ do người dùng xác định.
Nếu Task1 bị tạm ngừng ở trạng thái đang chạy bằng cách sử dụng các API tạm ngưng, thì Task1 sẽ chuyển sang trạng thái bị treo và nó không có sẵn cho bộ lập lịch nữa. Nếu bạn tiếp tục Task1 ở trạng thái bị treo thì nó sẽ trở lại trạng thái sẵn sàng như bạn có thể thấy trong sơ đồ khối.
Đây là ý tưởng cơ bản về cách Công việc chạy và thay đổi trạng thái của chúng. Trong hướng dẫn này, chúng tôi sẽ triển khai hai tác vụ trong Arduino Uno bằng cách sử dụng API FreeRTOS.
Các thuật ngữ thường được sử dụng trong RTOS
1. Tác vụ: Là một đoạn mã được lập lịch trên CPU để thực thi.
2. Bộ lập lịch: Có nhiệm vụ chọn một công việc từ danh sách trạng thái sẵn sàng sang trạng thái đang chạy. Bộ lập lịch thường được thực hiện để chúng giữ cho tất cả các tài nguyên máy tính luôn bận rộn (như trong cân bằng tải).
3. Preemption: Là hành động tạm thời làm gián đoạn một nhiệm vụ đã được thực thi với ý định loại bỏ nó khỏi trạng thái đang chạy mà không có sự hợp tác của nó.
4. Chuyển đổi theo ngữ cảnh: Trong ưu tiên dựa trên mức độ ưu tiên, bộ lập lịch so sánh mức độ ưu tiên của các tác vụ đang chạy với mức độ ưu tiên của danh sách tác vụ sẵn sàng trên mọi ngắt systick . Nếu có bất kỳ tác vụ nào trong danh sách có mức độ ưu tiên cao hơn tác vụ đang chạy thì chuyển đổi ngữ cảnh xảy ra. Về cơ bản, trong quá trình này, nội dung của các tác vụ khác nhau được lưu trong bộ nhớ ngăn xếp tương ứng của chúng.
5. Các loại chính sách Lập lịch:
- Lập lịch trước: Trong kiểu lập lịch này, các tác vụ chạy với thời gian bằng nhau mà không cần xem xét các mức độ ưu tiên.
- Premptive dựa trên mức độ ưu tiên: Nhiệm vụ có mức độ ưu tiên cao sẽ chạy trước.
- Lập lịch hợp tác: Việc chuyển đổi ngữ cảnh sẽ chỉ xảy ra với hoạt động hợp tác của các tác vụ đang chạy. Tác vụ sẽ chạy liên tục cho đến khi hiệu suất tác vụ được gọi.
6. Đối tượng Kernel: Để báo hiệu nhiệm vụ thực hiện một số công việc, quá trình đồng bộ hóa được sử dụng. Để thực hiện quá trình này, các đối tượng Kernel được sử dụng. Một số đối tượng Kernel là Sự kiện, Semaphores, Hàng đợi, Mutex, Hộp thư,… Chúng ta sẽ xem cách sử dụng các đối tượng này trong các bài hướng dẫn sắp tới.
Từ cuộc thảo luận trên, chúng ta đã có một số ý tưởng cơ bản về khái niệm RTOS và bây giờ chúng ta có thể triển khai dự án FreeRTOS trong Arduino. Vì vậy, hãy bắt đầu bằng cách cài đặt các thư viện FreeRTOS trong Arduino IDE.
Cài đặt Thư viện Arduino FreeRTOS
1. Mở Arduino IDE và đi tới Sketch -> Bao gồm Thư viện -> Quản lý Thư viện . Tìm kiếm FreeRTOS và cài đặt thư viện như hình dưới đây.
Bạn có thể tải xuống thư viện từ github và Thêm tệp.zip trong Sketch-> Bao gồm Thư viện -> Thêm tệp.zip .
Bây giờ, khởi động lại Arduino IDE. Thư viện này cung cấp một số mã ví dụ, cũng có thể được tìm thấy trong Tệp -> Ví dụ -> FreeRTOS như hình dưới đây.
Ở đây chúng tôi sẽ viết mã từ đầu để hiểu cách làm việc, sau này bạn có thể kiểm tra các mã ví dụ và sử dụng chúng.
Sơ đồ mạch
Dưới đây là sơ đồ mạch để tạo tác vụ nhấp nháy LED bằng FreeRTOS trên Arduino:
Ví dụ về Arduino FreeRTOS- Tạo nhiệm vụ FreeRTOS trong Arduino IDE
Hãy xem một cấu trúc cơ bản để viết một dự án FreeRTOS.
1. Đầu tiên, hãy bao gồm tệp tiêu đề Arduino FreeRTOS dưới dạng
#include
2. Cung cấp nguyên mẫu hàm của tất cả các hàm mà bạn đang viết để thực thi được viết dưới dạng
void Task1 (void * pvParameters); void Task2 (void * pvParameters); .. ….
3. Bây giờ, trong hàm void setup () , hãy tạo các tác vụ và khởi động bộ lập lịch tác vụ.
Để tạo tác vụ, API xTaskCreate () được gọi trong hàm thiết lập với các tham số / đối số nhất định.
xTaskCreate (TaskFunction_t pvTaskCode, const char * const pcName, uint16_t usStackDepth, void * pvParameters, UBaseType_t uxPooter, TaskHandle_t * pxCreateTask);
Có 6 đối số nên được chuyển khi tạo bất kỳ tác vụ nào. Hãy xem những lập luận này là gì
- pvTaskCode: Nó chỉ đơn giản là một con trỏ đến hàm thực thi tác vụ (trên thực tế, chỉ là tên của hàm).
- pcName: Tên mô tả cho nhiệm vụ. Điều này không được sử dụng bởi FreeRTOS. Nó được bao gồm hoàn toàn cho mục đích gỡ lỗi.
- usStackDepth: Mỗi tác vụ có ngăn xếp duy nhất của riêng nó được hạt nhân phân bổ cho tác vụ khi tác vụ được tạo. Giá trị chỉ định số từ mà ngăn xếp có thể chứa, không phải số byte. Ví dụ: nếu ngăn xếp rộng 32-bit và usStackDepth được chuyển bằng 100, thì 400 byte không gian ngăn xếp sẽ được phân bổ (100 * 4 byte) trong RAM. Sử dụng điều này một cách khôn ngoan vì Arduino Uno chỉ có 2Kbyte RAM.
- pvParameters: Tham số đầu vào tác vụ (có thể là NULL).
- uxP Priority : Mức độ ưu tiên của nhiệm vụ (0 là mức độ ưu tiên thấp nhất).
- pxCreateTask: Nó có thể được sử dụng để đưa ra một xử lý cho tác vụ đang được tạo. Sau đó, xử lý này có thể được sử dụng để tham chiếu tác vụ trong các lệnh gọi API, ví dụ: thay đổi mức độ ưu tiên của tác vụ hoặc xóa tác vụ (có thể là NULL).
Ví dụ về tạo tác vụ
xTaskCreate (task1, "task1", 128, NULL, 1, NULL); xTaskCreate (task2, "task2", 128, NULL, 2, NULL);
Ở đây, Task2 có mức độ ưu tiên cao hơn và do đó thực thi trước.
4. Sau khi tạo tác vụ, hãy khởi động bộ lập lịch trong thiết lập void sử dụng vTaskStartScheduler (); API.
5. Hàm loop () sẽ trống vì chúng ta không muốn chạy bất kỳ tác vụ nào theo cách thủ công và vô hạn. Bởi vì việc thực thi tác vụ hiện được xử lý bởi Bộ lập lịch.
6. Bây giờ, chúng ta phải triển khai các hàm tác vụ và viết logic mà bạn muốn thực thi bên trong các hàm này. Tên hàm phải giống với đối số đầu tiên của API xTaskCreate () .
void task1 (void * pvParameters) { while (1) { .. ..//your logic } }
7. Hầu hết mã cần chức năng trì hoãn để dừng tác vụ đang chạy nhưng trong RTOS không nên sử dụng hàm Delay () vì nó dừng CPU và do đó RTOS cũng ngừng hoạt động. Vì vậy, FreeRTOS có một API hạt nhân để chặn tác vụ trong một thời gian cụ thể.
vTaskDelay (const TickType_t xTicksToDelay);
API này có thể được sử dụng cho các mục đích trì hoãn. API này trì hoãn một nhiệm vụ đối với một số tích tắc nhất định. Thời gian thực tế mà nhiệm vụ vẫn bị chặn phụ thuộc vào tỷ lệ đánh dấu. Hằng số portTICK_PERIOD_MS có thể được sử dụng để tính toán thời gian thực từ tỷ lệ đánh dấu.
Điều này có nghĩa là nếu bạn muốn độ trễ 200ms, chỉ cần viết dòng này
vTaskDelay (200 / portTICK_PERIOD_MS);
Vì vậy, đối với hướng dẫn này, chúng tôi sẽ sử dụng các API FreeRTOS này để triển khai ba tác vụ.
Các API được sử dụng:
- xTaskCreate ();
- vTaskStartScheduler ();
- vTaskDelay ();
Tác vụ được tạo cho hướng dẫn này:
- Đèn LED nhấp nháy ở chân kỹ thuật số 8 với tần số 200ms
- Đèn LED nhấp nháy ở chân kỹ thuật số 7 với tần số 300ms
- In số trên màn hình nối tiếp với tần số 500ms.
Triển khai tác vụ FreeRTOS trong Arduino IDE
1. Từ giải thích cấu trúc cơ bản ở trên, hãy bao gồm tệp tiêu đề Arduino FreeRTOS. Sau đó tạo các nguyên mẫu hàm. Vì chúng ta có ba nhiệm vụ, vì vậy hãy tạo ba chức năng và nó là nguyên mẫu.
#include void TaskBlink1 (void * pvParameters); void TaskBlink2 (void * pvParameters); void Taskprint (void * pvParameters);
2. Trong hàm void setup () , khởi tạo giao tiếp nối tiếp với tốc độ 9600 bit mỗi giây và tạo cả ba tác vụ bằng cách sử dụng API xTaskCreate () . Ban đầu, hãy đặt mức độ ưu tiên của tất cả các nhiệm vụ là '1' và khởi động trình lên lịch.
void setup () { Serial.begin (9600); xTaskCreate (TaskBlink1, "Task1", 128, NULL, 1, NULL); xTaskCreate (TaskBlink2, "Task2", 128, NULL, 1, NULL); xTaskCreate (Taskprint, "Task3", 128, NULL, 1, NULL); vTaskStartScheduler (); }
3. Bây giờ, thực hiện tất cả ba chức năng như được hiển thị bên dưới cho nhấp nháy LED của task1.
void TaskBlink1 (void * pvParameters) { pinMode (8, OUTPUT); while (1) { digitalWrite (8, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (8, THẤP); vTaskDelay (200 / portTICK_PERIOD_MS); } }
Tương tự, thực hiện chức năng TaskBlink2. Hàm Task3 sẽ được viết là
void Taskprint (void * pvParameters) { int counter = 0; while (1) { counter ++; Serial.println (bộ đếm); vTaskDelay (500 / portTICK_PERIOD_MS); } }
Đó là nó. Chúng tôi đã hoàn thành thành công dự án FreeRTOS Arduino cho Arduino Uno. Bạn có thể tìm thấy mã đầy đủ cùng với video ở cuối hướng dẫn này.
Cuối cùng, kết nối hai đèn LED ở chân số 7 và 8 và tải mã lên bảng Arduino của bạn và mở màn hình Nối tiếp. Bạn sẽ thấy một bộ đếm chạy một lần trong 500ms với tên tác vụ như hình dưới đây.
Ngoài ra, hãy quan sát các đèn LED, chúng nhấp nháy trong các khoảng thời gian khác nhau. Hãy thử chơi với đối số ưu tiên trong hàm xTaskCreate . Thay đổi số và quan sát hành vi trên màn hình nối tiếp và đèn LED.
Bây giờ, bạn có thể hiểu hai mã ví dụ đầu tiên trong đó tác vụ đọc tương tự và đọc kỹ thuật số được tạo. Bằng cách này, bạn có thể thực hiện nhiều dự án nâng cao hơn chỉ bằng các API Arduino Uno và FreeRTOS.