이전 자습서에서 Arduino Uno에 FreeRTOS를 도입하고 깜박이는 LED에 대한 작업을 생성했습니다. 이제이 자습서에서는 RTOS API의 고급 개념에 대해 자세히 알아보고 서로 다른 작업 간의 통신에 대해 알아 봅니다. 여기서 우리는 또한 한 작업에서 다른 작업으로 데이터를 전송하는 큐에 대해 배우고 16x2 LCD 및 LDR을 Arduino Uno와 인터페이스하여 큐 API의 작동을 시연합니다.
대기열에 대해 논의하기 전에 할당 된 작업이 완료되면 작업을 삭제하는 데 도움이되는 FreeRTOS API를 하나 더 살펴 보겠습니다. 할당 된 메모리를 해제하기 위해 작업을 삭제해야하는 경우가 있습니다. 이전 자습서의 계속 에서 동일한 코드에서 vTaskDelete () API 함수를 사용하여 작업 중 하나를 삭제합니다. 작업은 vTaskDelete () API 함수를 사용하여 자신 또는 다른 작업을 삭제할 수 있습니다.
이 API를 사용하려면 FreeRTOSConfig.h 파일 을 구성해야 합니다. 이 파일은 애플리케이션에 따라 FreeRTOS를 조정하는 데 사용됩니다. 스케줄링 알고리즘 및 기타 여러 매개 변수를 변경하는 데 사용됩니다. 이 파일은 일반적으로 PC의 Documents 폴더에서 사용할 수있는 Arduino 디렉토리에서 찾을 수 있습니다. 제 경우에는 아래와 같이 \ Documents \ Arduino \ libraries \ FreeRTOS \ src 에서 사용할 수 있습니다.
이제 텍스트 편집기를 사용하여이 파일을 열고 검색 #DEFINE INCLUDE_vTaskDelete을 하고 있는지 그 값이 '1'을 만들 (1 개 수단을 활성화하고 0 수단 비활성화). 기본적으로 1이지만 확인합니다.
매개 변수 설정을위한 다음 튜토리얼에서이 구성 파일을 자주 사용할 것입니다.
이제 작업을 삭제하는 방법을 살펴 보겠습니다.
FreeRTOS Arduino에서 작업 삭제
작업을 삭제하려면 vTaskDelete () API 함수를 사용해야합니다. 하나의 인수 만 필요합니다.
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete: 삭제할 작업의 핸들입니다. xTaskCreate () API 의 6 번째 인수 와 동일 합니다. 이전 자습서에서이 인수는 NULL로 설정되었지만 임의의 이름을 사용하여 작업 내용의 주소를 전달할 수 있습니다. 다음과 같이 선언 된 Task2에 대한 작업 핸들을 설정하려면
TaskHandle_t any_name; 예: TaskHandle_t xTask2Handle;
이제 vTaskCreate () API에서 6 번째 인수를 다음과 같이 설정 합니다.
xTaskCreate (TaskBlink2, "task2", 128, NULL, 1, & xTask2Handle);
이제이 작업의 내용은 사용자가 제공 한 핸들을 사용하여 액세스 할 수 있습니다.
또한 작업은 유효한 작업 핸들 대신 NULL을 전달하여 자신을 삭제할 수 있습니다.
태스크 3 자체에서 태스크 3을 삭제하려면 vTaskDelete (NULL); Task3 함수 내부에 있지만 작업 2에서 작업 3을 삭제하려면 vTaskDelete (xTask3Handle); task2 함수 내부.
이전 튜토리얼 코드에서 task2 자체에서 Task2를 삭제하려면 vTaskDelete (NULL); 에 공극 TaskBlink2 (무효 *의 pvParameters) 기능. 그러면 위의 기능은 다음과 같습니다.
void TaskBlink2 (void * pvParameters) { Serial.println ("Task2가 실행 중이며 삭제하려고합니다."); vTaskDelete (NULL); pinMode (7, OUTPUT); while (1) { digitalWrite (7, HIGH); vTaskDelay (300 / portTICK_PERIOD_MS); digitalWrite (7 LOW) vTaskDelay (300 / portTICK_PERIOD_MS); } }
이제 코드를 업로드하고 LED와 직렬 모니터를 관찰하십시오. 두 번째 LED가 현재 깜박이지 않고 삭제 API가 발생한 후 task2가 삭제 된 것을 볼 수 있습니다.
따라서이 API를 사용하여 특정 작업의 실행을 중지 할 수 있습니다.
이제 대기열부터 시작하겠습니다.
FreeRTOS의 대기열이란 무엇입니까?
Queue는 유한 한 개수의 고정 크기 요소를 보유 할 수있는 데이터 구조이며 FIFO 방식 (선입 선출)으로 운영됩니다. 대기열은 작업 대 작업, 작업 대 인터럽트 및 인터럽트 대 작업 통신 메커니즘을 제공합니다.
큐가 보유 할 수있는 최대 요소 수를 "길이"라고합니다. 각 요소의 길이와 크기는 대기열이 생성 될 때 설정됩니다.
대기열이 데이터 전송에 사용되는 방법에 대한 예는 여기에있는 FreeRTOS 설명서에 잘 설명되어 있습니다. 주어진 예를 쉽게 이해할 수 있습니다.
대기열을 이해 한 후 대기열 생성 프로세스를 이해하고 FreeRTOS 코드에서 구현해 보겠습니다.
FreeRTOS에서 대기열 생성
먼저 FreeRTOS 대기열 및 Arduino Uno를 사용하여 구현할 문제 설명을 설명합니다.
16 * 2 LCD에 LDR 센서의 값 을 인쇄 하려고합니다. 이제 두 가지 작업이 있습니다.
- Task1은 LDR의 아날로그 값을 가져옵니다.
- Task2는 LCD에 아날로그 값을 인쇄하고 있습니다.
그래서 여기서 큐는 task1에서 생성 된 데이터를 task2로 보내기 때문에 그 역할을합니다. task1에서는 아날로그 값을 큐에 보내고 task2에서는 큐에서 수신합니다.
대기열 작업에는 세 가지 기능이 있습니다.
- 대기열 생성
- 큐에 데이터 보내기
- 대기열에서 데이터 받기
큐를 생성하려면 xQueueCreate () 함수 API를 사용합니다. 두 가지 인수가 필요합니다.
xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength: 생성중인 대기열이 한 번에 보유 할 수있는 최대 항목 수입니다.
uxItemSize: 대기열에 저장할 수있는 각 데이터 항목의 크기 (바이트)입니다.
이 함수가 NULL을 반환하면 메모리가 부족하여 대기열이 생성되지 않고 NULL이 아닌 값을 반환하면 대기열이 성공적으로 생성됩니다. 이 반환 값을 변수에 저장하여 아래와 같이 큐에 액세스하기위한 핸들로 사용합니다.
QueueHandle_t queue1; queue1 = xQueueCreate (4, sizeof (int));
이것은 int 크기 (각 블록의 2 바이트)의 힙 메모리에 4 개의 요소 큐를 생성하고 반환 값을 queue1 핸들 변수에 저장합니다.
2. FreeRTOS의 대기열로 데이터 보내기
값을 대기열로 보내기 위해 FreeRTOS에는이를위한 API의 두 가지 변형이 있습니다.
- xQueueSendToBack (): 큐의 뒤쪽 (꼬리)으로 데이터를 보내는 데 사용됩니다.
- xQueueSendToFront (): 대기열의 맨 앞 (헤드)에 데이터를 보내는 데 사용됩니다.
이제 , xQueueSend ()가 상당하고, 정확히 동일 xQueueSendToBack ().
이 모든 API는 3 개의 인수를 사용합니다.
xQueueSendToBack (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
xQueue: 데이터가 전송 (쓰기)되는 대기열의 핸들입니다. 이 변수는 xQueueCreate API의 반환 값을 저장하는 데 사용되는 것과 동일합니다.
pvItemToQueue: 큐에 복사 할 데이터에 대한 포인터입니다.
xTicksToWait: 대기열에서 공간을 사용할 수있을 때까지 대기하기 위해 작업이 Blocked 상태로 유지되어야하는 최대 시간입니다.
설정 xTicksToWait을 에 portMAX_DELAY하는 작업이 (시간 초과없이) 무한정 대기하게됩니다 제공 INCLUDE_vTaskSuspend가 1로 설정되어 FreeRTOSConfig.h 다른 매크로 사용할 수 있습니다 pdMS_TO_TICKS ()를 틱으로 지정 시간에 밀리 초 단위로 지정 시간을 변환합니다.
3. FreeRTOS의 대기열에서 데이터 수신
큐에서 항목을 수신 (읽기)하려면 xQueueReceive () 가 사용됩니다. 수신 된 항목이 큐에서 제거됩니다.
이 API는 세 가지 인수도 사용합니다.
xQueueReceive (QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);
첫 번째 및 세 번째 인수는 API 전송과 동일합니다. 두 번째 인수 만 다릅니다.
const pvBuffer: 수신 된 데이터가 복사 될 메모리에 대한 포인터.
세 가지 API를 이해 하셨기를 바랍니다. 이제 Arduino IDE에서 이러한 API를 구현하고 위에서 설명한 문제 설명을 해결하려고합니다.
회로도
브레드 보드에서 다음과 같이 보입니다.
Arduino IDE에서 FreeRTOS 대기열 구현
애플리케이션 코드 작성을 시작하겠습니다.
1. 먼저 Arduino IDE를 열고 Arduino_FreeRTOS.h 헤더 파일을 포함 합니다. 이제 큐와 같은 커널 개체가 사용되는 경우 헤더 파일을 포함합니다. 16 * 2 LCD를 사용하므로 라이브러리도 포함하십시오.
# 포함 # 포함
2. 대기열의 내용을 저장할 대기열 핸들을 초기화합니다. 또한 LCD 핀 번호를 초기화하십시오.
QueueHandle_t queue_1; LiquidCrystal lcd (7, 8, 9, 10, 11, 12);
3. void setup ()에서 9600 baud rate로 LCD와 시리얼 모니터를 초기화합니다. 각각의 API를 사용하여 대기열과 두 개의 작업을 만듭니다. 여기에서는 정수 유형으로 크기 4의 대기열을 생성합니다. 우선 순위가 동일한 작업을 만들고 나중에이 번호로 플레이 해보십시오. 마지막으로 아래와 같이 스케줄러를 시작합니다.
void setup () { Serial.begin (9600); lcd.begin (16, 2); queue_1 = xQueueCreate (4, sizeof (int)); if (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의 우리 LDR은 아두 이노 UNO의 A0 단자에 연결된 역할 가변 아날로그 A0 핀을 읽었다. 이제 변수에 저장된 값을 xQueueSend API 에 전달하여 전송하고 다음과 같이 vTaskDelay () API를 사용하여 1 초 후에 작업을 블록 상태로 보냅니다.
void TaskLDR (void * pvParameters) { int current_intensity; while (1) { Serial.println ("Task1"); current_intensity = analogRead (A0); Serial.println (current_intensity); xQueueSend (queue_1, & current_intensity, portMAX_DELAY); vTaskDelay (1000 / portTICK_PERIOD_MS); } }
5. 마찬가지로, TaskDisplay에 대한 함수를 만들고 xQueueReceive 함수에 전달되는 변수의 값을받습니다. 또한, xQueueReceive는 () 반환 pdPASS를 데이터가 큐에서 성공적으로 수신 및 반환 할 수있는 경우 errQUEUE_EMPTY를 큐가 비어있는 경우.
이제 lcd.print () 함수를 사용하여 값을 LCD에 표시합니다.
void TaskDisplay (void * pvParameters) { int intensity = 0; while (1) { Serial.println ("Task2"); if (xQueueReceive (queue_1, & intensity, 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와 함께 작동하려면 라이브러리를 지연없이 만들어야합니다.