Bài 1: Quản lý ngắt sử dụng QueueViết chương trình tạo Task_Printer đọc chuỗi kí tự từ hàng đợi StringQueue và in chuỗi đó ramàn hình Serial Monitor. Các phần tử của hàng đợi được ghi bởi một chương trình con phụcvụ ngắt (ISR) sử dụng Timer1 của Arduino.Bài 2: xQueuePeek, xQueueOverwriteViết chương trình tạo 2 tác vụ có cùng mức ưu tiên, trong đó: Task1: Tạo queue có 3 phần tử kiểu chuỗi kí tự (tối đa 15 kí tự); nhập vào queue 2phần tử cho trước (message 1; message 2), sau đó ghi đè vào 1 phần tử khác (message3). In ra “Data waiting to be read: n” (n là số lượng phần tử có thể đọc ra); và“Available spaces: m” (m là số không gian trống trong queue) Task2: Đọc dữ liệu từ Queue với hàm xQueuePeek và in ra màn hìnhBài 3: Thay đổi mức ưu tiên của tác vụViết chương trình tạo 2 tác vụ Task1, Task2 có mức ưu tiên tương ứng là 2, 1 trước khi gọitrình lập lịch. Trong đó: Kích thước ngăn xếp 64 từ máy (words); Task1: In ra màn hình “Task1 is running, N” cứ mỗi 1s, N là biến đếm khởi tạo bằng0. Nếu N =4 thì thay đổi mức ưu tiên của Task2 thành 3 và in ra màn hình “Back fromTask2”. Task2: Thay đổi mức ưu tiên của mình thành 2, và cứ mỗi 1s in ra màn hình “Task2 isrunning”Bài 4: Lập trình vàora trên Arduino với UARTLập trình giao tiếp giữa hai Arduino sử dụng giao thức UART, với yêu cầu sau: 1 board gửi kí tự X và giá trị 235 sang cho board nhận cứ mỗi 0,5s Board nhận có kết nối LCD I2C và hiển thị đúng ký tự và giá trị nhận về lên dòng thứ2 của LCDBài 5: QueueViết chương trình thực hiện tạo hai tác vụ có tên “Task1”, “Task2” sao cho: Cả 2 tác vụ có: Stack 250 từ máy (word), mức ưu tiên (tskIDLE_PRIORITY + 1),Handle lần lượt là HTask1, HTask2 Task1 thực hiện tạo hàng đợi có tên “CT2Queue” có 6 phần tử kiểu chuỗi 20 ký tự;gửi 3 chuỗi ký tự (“CT02Hello”; “DT01Hi”; “ATWelcome” cho trước) vào hàngđợi. Task2 đọc từng phần tử từ hàng đợi và lần lượt in ra màn hình sau mỗi lần đọc các nộidung sau: giá trị của phần tử; số phần tử có thể đọc ra từ hàng đợi và không gian khảdụng còn lại tương ứng của hàng đợi.
Trang 1HỆ ĐIỀU HÀNH NHÚNG THỜI GIAN THỰCBài 1: Quản lý ngắt sử dụng Queue
Viết chương trình tạo Task_Printer đọc chuỗi kí tự từ hàng đợi StringQueue và in chuỗi đó ra
màn hình Serial Monitor Các phần tử của hàng đợi được ghi bởi một chương trình con phục
vụ ngắt (ISR) sử dụng Timer1 của Arduino
Trang 2Bài 2: xQueuePeek, xQueueOverwrite
Viết chương trình tạo 2 tác vụ có cùng mức ưu tiên, trong đó:
Task1: Tạo queue có 3 phần tử kiểu chuỗi kí tự (tối đa 15 kí tự); nhập vào queue 2phần tử cho trước (message 1; message 2), sau đó ghi đè vào 1 phần tử khác (message3) In ra “Data waiting to be read: n” (n là số lượng phần tử có thể đọc ra); và
“Available spaces: m” (m là số không gian trống trong queue)
Task2: Đọc dữ liệu từ Queue với hàm xQueuePeek và in ra màn hình
void Task1(void *pvParameters);
void Task2(void *pvParameters);
Trang 3//xQueueSend(myQueue, (void *)txBuff, (TickType_t)0);
Serial.print("Data waiting to be read: ");
Bài 3: Thay đổi mức ưu tiên của tác vụ
Viết chương trình tạo 2 tác vụ Task1, Task2 có mức ưu tiên tương ứng là 2, 1 trước khi gọitrình lập lịch Trong đó:
Kích thước ngăn xếp 64 từ máy (words);
Task1: In ra màn hình “Task1 is running, N” cứ mỗi 1s, N là biến đếm khởi tạo bằng
0 Nếu N =4 thì thay đổi mức ưu tiên của Task2 thành 3 và in ra màn hình “Back fromTask2”
Task2: Thay đổi mức ưu tiên của mình thành 2, và cứ mỗi 1s in ra màn hình “Task2 isrunning”
void Task1(void *pvParameters);
void Task2(void *pvParameters);
Trang 4xTaskCreate(Task1, "Task 1", 128, NULL, 2, &xTask1_Handler);
xTaskCreate(Task2, "Task 2", 128, NULL, 1, &xTask2_Handler);
Bài 4: Lập trình vào/ra trên Arduino với UART
Lập trình giao tiếp giữa hai Arduino sử dụng giao thức UART, với yêu cầu sau:
1 board gửi kí tự X và giá trị 235 sang cho board nhận cứ mỗi 0,5s
Board nhận có kết nối LCD I2C và hiển thị đúng ký tự và giá trị nhận về lên dòng thứ
Trang 6Bài 5: Queue
Viết chương trình thực hiện tạo hai tác vụ có tên “Task1”, “Task2” sao cho:
Cả 2 tác vụ có: Stack 250 từ máy (word), mức ưu tiên (tskIDLE_PRIORITY + 1),Handle lần lượt là HTask1, HTask2
Task1 thực hiện tạo hàng đợi có tên “CT2Queue” có 6 phần tử kiểu chuỗi 20 ký tự;gửi 3 chuỗi ký tự (“CT02-Hello”; “DT01-Hi”; “AT-Welcome” cho trước) vào hàngđợi
Task2 đọc từng phần tử từ hàng đợi và lần lượt in ra màn hình sau mỗi lần đọc các nộidung sau: giá trị của phần tử; số phần tử có thể đọc ra từ hàng đợi và không gian khảdụng còn lại tương ứng của hàng đợi
void Task1(void *pvParameters);
void Task2(void *pvParameters);
//define var
Trang 7unsigned int count = 0;
// creat task
void setup() {
Serial.begin(9600);
xTaskCreate(Task1, "Task 1", 250, NULL, tskIDLE_PRIORITY + 1, &HTask1);
xTaskCreate(Task2, "Task 2", 250, NULL, tskIDLE_PRIORITY + 1, &HTask2);
xQueueSend(myQueue, (void *)txBuff, (TickType_t)0);
sprintf(txBuff, "AT - Welcome");
xQueueSend(myQueue, (void *)txBuff, (TickType_t)0);
Bài 6: Using Interrupt to resume task
Viết chương trình tạo 3 tác vụ MyTask1, MyTask2, MyTask3 có mức ưu tiên tương ứng 1, 2,
3, trong đó:
MyTask1, MyTask2: đều in ra màn hình Serial Monitor dòng chữ “Task N is running,
Trang 8Deleting Itself”, với N tương ứng là 1 và 2
MyTask3: In ra màn hình Serial Monitor dòng chữ “Task 3 is running, Suppending all
tasks”; thực hiện đình chỉ cả 3 tác vụ; tiếp tục in ra dòng chữ “Back in Task3, Deleting
Itself” và xóa chính nó (MyTask3)
Lần lượt từng tác vụ được khôi phục khi nhận được tín hiệu nút bấm (ngắt ngoài) từ
Serial.println(F("In Setup function"));
/* Use INT0(pin2) falling edge interrupt for resuming tasks */
attachInterrupt(digitalPinToInterrupt(2), ExternalInterrupt, FALLING);
/* Create 3-tasks with priorities 2-4 Capture the Task details to respective handlers */
xTaskCreate(MyTask2, "Task2", 100, NULL, 2, &TaskHandle_2);
xTaskCreate(MyTask3, "Task3", 100, NULL, 3, &TaskHandle_3);
xTaskCreate(MyTask4, "Task4", 100, NULL, 4, &TaskHandle_4);
* Tasks are resumed every time a Falling edge interrupt is detected on PIN2
* One task is resumed at a time, a counter is used to resume 3taks and after which no tasks are resumed
* xTaskResumeFromISR() returns True if Context switch is required and accordingly we need to call
portYIELD_FROM_ISR/taskYield(AVR)
* Serial data is printed in ISR only for demonstarting the control flow This should not be done as it takes long time
to send data on Serial port
* Tasking to much ISR time will starve the other tasks or User application
Trang 9/* Task2 with priority 2 */
static void MyTask2(void* pvParameters)
{
Serial.println(F("Task2, Deleting itself"));
vTaskDelete(NULL); //Delete own task by passing NULL(TaskHandle_2 can also be used)}
/* Task3 with priority 3 */
static void MyTask3(void* pvParameters)
{
Serial.println(F("Task3, Deleting Itself"));
vTaskDelete(NULL); //Delete own task by passing NULL(TaskHandle_3 can also be used) }
/* Task4 with priority 4 */
static void MyTask4(void* pvParameters)
{
Serial.println(F("Task4 Running, Suspending all tasks"));
Trang 10vTaskSuspend(TaskHandle_2); //Suspend Task2/3
vTaskSuspend(TaskHandle_3);
vTaskSuspend(NULL); //Suspend Own Task
Serial.println(F("Back in Task4, Deleting Itself"));
vụ được tiếp tục Khi ngắt INT0 được tạo, nó sẽ tiếp tục Task3 Task3 sẽ chạy trong một thời gian và tự xóa
Chỉ còn lại tác vụ IDLE và nó tiếp tục chạy
Bài 7: Task suspend, resume:
Viết chương trình tạo 2 tác vụ có mức ưu tiên (tskIDLE_PRIORITY + 2)
Task1: In ra màn hình “Hello World, N” cứ mỗi 1s, N là biến đếm
Task2: Cứ 5s sẽ đình chỉ Task1, sau 5s lại khôi phục Task1
void Task1(void *pvParameters);
void Task2(void *pvParameters);
xTaskCreate(Task1, "Task 1", 128, NULL, tskIDLE_PRIORITY, &xTask1_Handler);
xTaskCreate(Task2, "Task 2", 128, NULL, tskIDLE_PRIORITY, &xTask2_Handler);
Trang 11Viết chương trình thực hiện tạo hai tác vụ có tên “Task1”, “Task2” sao cho:
Task 1 thực hiện tạo hàng đợi có tên “MyQueue” có 6 phần tử kiểu chuỗi 20 ký tự; ghi
3 chuỗi ký tự cho trước (message 1; message 2; message 3) vào hàng đợi
Task2 đọc từng phần tử từ hàng đợi và lần lượt in ra màn hình sau mỗi lần đọc các nộidung sau: giá trị của phần tử; số phần tử có thể đọc ra từ hàng đợi và không gian khảdụng còn lại tương ứng của hàng đợi
void Task1(void *pvParameters);
void Task2(void *pvParameters);
}
void loop() {
}
Trang 12Bài 9: Task&Queue, Sensor, LCD
Viết chương trình tạo hai tác vụ Sender, Receiver và hàng đợi Queue có 5 phần tử kiểunguyên, sao cho:
Tác vụ Receiver có mức ưu tiên cao hơn tác vụ Sender
Tác vụ Sender đọc giá trị ADC gửi vào Queue và in giá trị đó ra màn hình Serialmonitor
Tác vụ Receiver đọc giá trị từ Queue và hiển thị lên màn hình LCD như sau: Dòng 1
“ADC Value:”; dòng 2: giá trị đọc được
#include <Arduino_FreeRTOS.h>
//define task handles
TaskHandle_t HTask1;
TaskHandle_t HTask2;
Trang 13// define tasks
void Task1(void *pvParameters);
void Task2(void *pvParameters);
xTaskCreate(Task1, "Task 1", 100, NULL, tskIDLE_PRIORITY, &HTask1);
xTaskCreate(Task2, "Task 2", 100, NULL, tskIDLE_PRIORITY, &HTask2);
Bài 10: Using Mutex to handle Multiple Task
Viết chương trình tạo hai tác vụ Task1, Task2 và sử dụng Mutex để xử lý hai tác vụ sao chochúng được thực hiện luân phiên Biết:
Hai tác vụ cùng mức ưu tiên
Task1, Task 2 đều liên tục in ra màn hình các giá trị từ 0 đến 4 cứ mỗi 0.5s
Trang 14Kết quả: Task1 in cả 5 giá trị rồi mới đến Task2 thực hiện
void Task1(void *pvParameters);
void Task2(void *pvParameters);
Trang 15Bài 11: Using Mutex to Resource management
Viết chương trình tạo hai tác vụ có tên “Task1”, “Task2” sao cho:
Cả hai tác vụ Task1, Task2 đều thực hiện gán từng ký tự của thông báo tương ứngMsg1, Msg2 cho từng phần tử trong SharedResource; các ký tự còn lại trống Sau đó
in ra màn hình dòng chữ “Task n: A”, với A là giá trị của biến SharedResource; n là
void Task1(void *pvParameters);
void Task2(void *pvParameters);
Trang 16Bài 12: Mailbox Example using Queue and Arduino
Viết chương trình tạo hai tác vụ gửi và nhận dữ liệu vào/từ mailbox, trong đó:
Task gửi: Ghi 1 giá trị vào mailbox mỗi 5s và in ra màn hình Serial Monitor dòng chữ
“Data written to mailbox”
Task nhận: Đọc dữ liệu từ mailbox mỗi 1s và in ra dòng chữ “Data read from mailbox
Trang 17// define tasks
void Task1(void *pvParameters);
void Task2(void *pvParameters);
xTaskCreate(Task1, "Task 1", 100, NULL, tskIDLE_PRIORITY, &HTask1);
xTaskCreate(Task2, "Task 2", 100, NULL, tskIDLE_PRIORITY, &HTask2);
xQueuePeek(xMailBox, &rxdata, portMAX_DELAY);
Serial.print("Data read from mailbox: ");
Serial.println(rxdata);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
Bài 13: Using Binary Semaphore: Task Synchronization
Viết chương trình sử dụng Binary Semaphore để thực hiện đồng bộ hai tác vụ Task1, Task2điều khiển bật/tắt đèn tại chân 13 của Arduino, biết:
Hai tác vụ có cùng mức ưu tiên
Trang 18Không thực thi tại cùng một thời điểm
In ra màn hình Serial Monitor trạng thái hiện tại của đèn
- Khi semaphore nhị phân trở nên khả dụng, “LedoffTask” sẽ thực thi nó vì nó được đưa vào trạng thái khối do tính sẵn có của semaphore nhị phân tài nguyên được chia sẻ
Trang 19Bài 14: Using Binary Semaphore: Task – Interrupt Synchronization
Viết chương trình sử dụng Binary Semaphore để thực hiện đồng bộ hai tác vụ Task1, Task2điều khiển bật/tắt đèn tại chân 13 của Arduino, biết:
Hai tác vụ có cùng mức ưu tiên
Không thực thi tại cùng một thời điểm
In ra màn hình Serial Monitor trạng thái hiện tại của đèn
void Task1(void *pvParameters);
void Task2(void *pvParameters);
Trang 20if (xSemaphoreTake(ISR_Semaphore, portMAX_DELAY) == pdPASS) {
Serial.println("TaskLedon Received Semaphore");
if (xSemaphoreTake(ISR_Semaphore, portMAX_DELAY) == pdPASS) {
Serial.println("TaskLedoff Received Semaphore");
digitalWrite(LED, LOW);
}
}
}
Bài 15: Using Counting Semaphore: Resource Management
Viết chương trình sử dụng Couting Semaphore để quản lý tài nguyên trên Arduino, đảm bảotại một thời điểm chỉ có một tác vụ được sử dụng tài nguyên Biết: Hai tác vụ Task1, Task2cùng sử dụng giao tiếp Serial (UART module) của Arduino và gửi chuỗi ký tự ra SerialMonitor
void Task1(void *pvParameters);
void Task2(void *pvParameters);
xSemaphoreGive(CountingSemaphore);
//vTaskStartScheduler();
}
void loop() {
Trang 21Bài 16: One-shot Timer & Auto-reload Timer
Viết chương trình thực thi hai hàm gọi lại chương trình với One-shot Timer và Auto-reload
timer có tên tương ứng là OneShotTimerCallback và AutoReloadTimerCallback
Hai hàm đều in ra màn hình thông báo trạng thái thực thi của mình kèm số thứ tự của
#define mainONE_SHOT_TIMER_PERIOD pdMS_TO_TICKS(3333)
#define mainAUTO_RELOAD_TIMER_PERIOD pdMS_TO_TICKS(500)
#define mainAUTO_RELOAD_TIMER_PERIOD2 pdMS_TO_TICKS(1000)
//define timer handle
TimerHandle_t xAutoReloadTimer, xOneShotTimer;
BaseType_t xTimer1Started, xTimer2Started;
void setup() {
Serial.begin(9600);
xOneShotTimer = xTimerCreate("OneShot", mainONE_SHOT_TIMER_PERIOD, pdFALSE, 0,
Trang 22//one shot timer
static void prvOneShotTimerCallback(TimerHandle_t xTimer) {
//auto reload timer
static void prvAutoReloadTimerCallback(TimerHandle_t xTimer) {
Bài 17: Lập trình vào/ra trên Arduino với I2C
Lập trình giao tiếp giữa hai Arduino sử dụng giao thức I2C, với địa chỉ nhận dữ liệu là 10
Master gửi từng ký tự trong chuỗi “Hello Slave”, yêu cầu Slave phản hồi lại 11 ký tự
và in chuỗi ký tự phản hồi này lên Serial Monitor
Slave đọc từng ký tự Master gửi đến và in cả chuỗi lên Serial Monitor
Trang 23String txbuff = "Hello"; //data
Wire.beginTransmission(10); // Slave's Address
for (int i = 0; i < 5; i++) {
Trang 24Bài 18: Using Queue
Viết chương trình sử dụng Queue để thực hiện truyền thông giữa hai tác vụ trên Arduino, biết: Hai chân 8,9 có hai đèn led (xanh, đỏ), ban đầu đèn số 8 bật, đèn số 9 tắt
Khởi tạo 2 task, một task gửi dữ liệu và một task nhận dữ liệu
Task gửi tạo ra một giá trị cứ mỗi 1 giây và thêm giá trị này vào cuối hàng đợi
Task nhận sẽ đọc một giá trị từ đầu hàng đợi cứ sau 500 ms, nếu không có giá trị, nó
sẽ đợi trong 1 giây Nếu giá trị nhận được lớn hơn hoặc bằng 4 và nhỏ hơn hoặc bằng
10 thì đèn số 8 sẽ tắt và đèn số 9 sẽ bật Ngược lại nếu giá trị nhận được nhỏ hơn 4 và
Trang 25Bài 19: Using Mutex to Resource management
Viết chương trình tạo hai tác vụ có tên “Task1”, “Task2” sao cho:
Cả hai tác vụ Task1, Task2 đều thực hiện gán từng ký tự của thông báo tương ứngMsg1, Msg2 cho từng phần tử trong biến có tên SharedResource; các ký tự còn lạitrống Sau đó in ra màn hình dòng chữ “Task n: A”, với A là giá trị của biến
SharedResource; n là số thứ tự của tác vụ; Msg1 =“13579”; Msg2 = “02468”. Sử dụng Mutex để đồng bộ việc sử dụng tài nguyên chia sẻ của hai tác vụ trên
void Task1(void *pvParameters);
void Task2(void *pvParameters);
//define var
char SharedResource[10];
Trang 27Bài 20: Using Event Group
Viết chương trình tạo ra một Event group và 3 tác vụ cùng mức ưu tiên, và thực hiện việcunlock một tác vụ sử dụng các event bit tương ứng, biết:
Hai tác vụ InputTask1, InputTask2 thực hiện thiết lập event bit tương ứng tại bit 0, bit
2 cứ mỗi 1 giây
Tác vụ OutputTask đọc giá trị event bit tương ứng trong Event group cứ 2 giây để in
ra màn hình thông báo sự kiện ứng với tác vụ Input nào đang xảy ra
#include <Arduino_FreeRTOS.h>
#include "event_groups.h"
// define three event flag bit variable
#define TASK1_BIT (1UL << 0UL) // zero shift for bit0
#define TASK2_BIT (1UL << 1UL) // 1 shift for flag bit 1
#define TASK3_BIT (1UL << 2UL) // 2 shift for flag bit 2
// declare a event grounp handler variable
EventGroupHandle_t xEventGroup;
// A constant to define 500ms delay
//because we use to periodically execute each task
// Create three tasks which are used to release event with
// the help of above defined event flags
xTaskCreate(InputTask1, "Input Task 1", 100,NULL,1,NULL);
xTaskCreate(InputTask2, "Input Task 2", 100, NULL, 1, NULL );
xTaskCreate(InputTask3, "Input Task 3", 100, NULL, 1,NULL);
//Create output task that will execute only when all three events occured
xTaskCreate(OutputTask, "Output Task", 100, NULL, 1, NULL);
}
// defintion of input task1
void InputTask1(void *pvParameters)
Trang 28// defintion of input task2
void InputTask2(void *pvParameters)
// defintion of input task3
void InputTask3(void *pvParameters)
// Definition of output task
void OutputTask(void *pvParameters)
{
// define a variable which holds the state of events
const EventBits_t xBitsToWaitFor = (TASK1_BIT | TASK2_BIT | TASK3_BIT); EventBits_t xEventGroupValue;
Trang 29Bài 21: Using Queue
Viết chương trình trên Arduino đọc giá trị cảm biến ADC và in ra màn hình LCD sao cho: Sử dụng queue để lưu giá trị đọc được từ cảm biến, queue có 7 phần tử kiểu nguyên Có 2 tác vụ riêng biệt thực hiện việc đọc và ghi vào queue
Giá trị in trên 2 dòng của màn hình LCD: Dòng 1 là giá trị đọc được từ queue, dòng 2
in chuỗi “Sensor: ”
Bài 22: Lập trình vào/ra trên Arduino với SPI
Lập trình giao tiếp giữa hai Arduino sử dụng giao thức SPI, với yêu cầu sau:
Khi bấm nút trên chân A1, master sẽ gửi giá trị 250 cho slave, slave trả lời về giá trị130
Cả master và slave đều đọc giá trị nhận được và in ra màn hình Serial monitor