- 세마포는 무엇입니까?
- FreeRTOS에서 Semaphore를 사용하는 방법은 무엇입니까?
- 세마포어 코드 설명
- 회로도
- 뮤텍스 란?
- FreeRTOS에서 Mutex를 사용하는 방법은 무엇입니까?
- 뮤텍스 코드 설명
이전 자습서에서는 Arduino를 사용한 FreeRTOS의 기본 사항과 FreeRTOS Arduino의 Queue 커널 개체에 대해 설명했습니다. 이제이 세 번째 FreeRTOS 자습서에서는 멀티 태스킹 플랫폼을 더 깊이 이해할 수있는 FreeRTOS 및 고급 API에 대해 자세히 알아 봅니다.
Semaphore 및 Mutex (Mutual Exclusion)는 동기화, 리소스 관리 및 손상으로부터 리소스 보호에 사용되는 커널 개체입니다. 이 튜토리얼의 전반부에서는 Semaphore 이면의 아이디어, 사용 방법 및 위치에 대해 알아 봅니다. 후반부에서는 Mutex 를 계속 사용 합니다.
세마포는 무엇입니까?
이전 자습서에서 작업 우선 순위에 대해 논의했으며 우선 순위가 높은 작업이 우선 순위가 낮은 작업을 선점하므로 우선 순위가 높은 작업을 실행하는 동안 우선 순위가 낮은 작업에서 데이터 손상이 발생할 가능성이 있습니다. 아직 실행되지 않았고 데이터가 센서에서이 작업으로 지속적으로 전달되어 전체 애플리케이션의 데이터 손실 및 오작동을 유발합니다.
따라서 데이터 손실로부터 리소스를 보호 할 필요 가 있으며 여기서 Semaphore가 중요한 역할을합니다.
세마포어는 대기 상태의 작업이 실행을 위해 다른 작업에 의해 신호를받는 신호 메커니즘입니다. 즉, task1이 작업을 완료하면 플래그를 표시하거나 플래그를 1 씩 증가시킨 다음 다른 작업 (task2)에서이 플래그를 수신하여 현재 작업을 수행 할 수 있음을 표시합니다. task2가 작업을 완료하면 플래그가 1만큼 감소합니다.
따라서 기본적으로 "Give"및 "Take"메커니즘이고 세마포어는 리소스에 대한 액세스를 동기화하는 데 사용되는 정수 변수입니다.
FreeRTOS의 세마포 유형:
세마포는 두 가지 유형이 있습니다.
- 이진 세마포어
- 세마포 계산
1. 이진 세마포어: 두 개의 정수 값 0과 1이 있습니다. 이는 길이 1의 큐와 다소 유사합니다. 예를 들어 두 개의 작업, task1 및 task2가 있습니다. Task1은 task2에 데이터를 전송하므로 task2는 1이 있으면 큐 항목을 계속 확인한 다음 1이 될 때까지 기다려야하는 데이터를 읽을 수 있습니다. task2에 데이터를 보낼 수 있습니다.
위의 예에서 바이너리 세마포어는 작업 간 또는 작업과 인터럽트 간의 동기화에 사용된다고 말할 수 있습니다.
2. 세마포 계산: 0보다 큰 값을 가지며 1보다 긴 큐 길이로 생각할 수 있습니다.이 세마포는 이벤트를 계산하는 데 사용됩니다. 이 사용 시나리오에서 이벤트 핸들러는 이벤트가 발생할 때마다 세마포어를 '제공'하고 (세마포어 카운트 값 증가), 핸들러 태스크는 이벤트를 처리 할 때마다 세마포어를 '취득'합니다 (세마포어 카운트 값 감소)..
따라서 카운트 값은 발생한 이벤트 수와 처리 된 수의 차이입니다.
이제 FreeRTOS 코드에서 Semaphore를 사용하는 방법을 살펴 보겠습니다.
FreeRTOS에서 Semaphore를 사용하는 방법은 무엇입니까?
FreeRTOS는 세마포어 생성, 세마포어 가져 오기 및 세마포어 제공을위한 다양한 API를 지원합니다.
이제 동일한 커널 개체에 대해 두 가지 유형의 API가있을 수 있습니다. ISR에서 세마포어를 제공해야하는 경우 일반 세마포어 API를 사용할 수 없습니다. 인터럽트 보호 API를 사용해야합니다.
이 튜토리얼에서는 이해하고 구현하기 쉽기 때문에 이진 세마포어를 사용 합니다. 여기서 인터럽트 기능을 사용하므로 ISR 기능에서 인터럽트 보호 API를 사용해야합니다. 작업을 인터럽트와 동기화하는 것은 ISR 직후 작업을 실행 중 상태로 만드는 것을 의미합니다.
세마포어 만들기:
커널 객체를 사용하려면 먼저 생성해야합니다. 이진 세마포를 만들려면 vSemaphoreCreateBinary ()를 사용합니다.
이 API는 매개 변수를 사용하지 않으며 SemaphoreHandle_t 유형의 변수를 리턴합니다. 세마포어를 저장하기 위해 전역 변수 이름 sema_v 가 생성됩니다.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
세마포어 제공:
세마포어를 제공하기 위해 두 가지 버전이 있습니다. 하나는 인터럽트 용이고 다른 하나는 일반 작업용입니다.
- xSemaphoreGive (): 이 API는 세마포어를 생성하는 동안 위에 주어진 sema_v와 같은 세마포어의 변수 이름 인 하나의 인수 만 취합니다. 동기화하려는 모든 일반 작업에서 호출 할 수 있습니다.
- xSemaphoreGiveFromISR (): xSemaphoreGive () 의 인터럽트 보호 API 버전입니다. ISR과 일반 작업을 동기화해야 할 때 ISR 함수에서 xSemaphoreGiveFromISR ()을 사용해야합니다.
세마포어 가져 오기:
세마포어를 가져 오려면 API 함수 xSemaphoreTake ()를 사용하십시오. 이 API는 두 개의 매개 변수를 사용합니다.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: 우리의 경우 sema_v에서 취할 세마포어의 이름.
xTicksToWait: 세마포를 사용할 수있을 때까지 작업이 Blocked 상태에서 대기하는 최대 시간입니다. 프로젝트에서 xTicksToWait 를 portMAX_DELAY 로 설정 하여 task_1이 sema_v를 사용할 수있을 때까지 Blocked 상태에서 무기한 대기하도록합니다.
이제 이러한 API를 사용하고 일부 작업을 수행하는 코드를 작성해 보겠습니다.
여기에서 하나의 푸시 버튼과 두 개의 LED가 인터페이스됩니다. 푸시 버튼은 Arduino Uno의 핀 2에 연결된 인터럽트 버튼 역할을합니다. 이 버튼을 누르면 인터럽트가 발생하고 8 번 핀에 연결된 LED가 켜지고 다시 누르면 꺼집니다.
버튼을 누를 때, xSemaphoreGiveFromISR ()를 ISR 기능에서 호출됩니다 xSemaphoreTake () 함수는 TaskLED 함수에서 호출됩니다.
시스템이 멀티 태스킹처럼 보이게하려면 항상 깜박이는 상태에있는 핀 7에 다른 LED를 연결합니다.
세마포어 코드 설명
Arduino IDE를 열어 코드 작성을 시작하겠습니다.
1. 먼저 Arduino_FreeRTOS.h 헤더 파일을 포함 합니다. 이제 커널 개체가 큐 세마포어처럼 사용되는 경우 헤더 파일도 포함되어야합니다.
# 포함 # 포함
2. SemaphoreHandle_t 유형의 변수를 선언하여 세마포어 값을 저장합니다.
SemaphoreHandle_t interruptSemaphore;
3. void setup ()에서 xTaskCreate () API를 사용하여 두 개의 작업 (TaskLED 및 TaskBlink)을 생성 한 다음 xSemaphoreCreateBinary ()를 사용하여 세마포어를 생성합니다. 우선 순위가 동일한 작업을 생성하고 나중에이 번호로 플레이 해보십시오. 또한 핀 2를 입력으로 구성하고 내부 풀업 저항을 활성화하고 인터럽트 핀을 연결합니다. 마지막으로 아래와 같이 스케줄러를 시작합니다.
void setup () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); if (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. 이제 ISR 기능을 구현합니다. 함수를 만들고 attachInterrupt () 함수 의 두 번째 인수와 동일한 이름을 지정합니다. 인터럽트가 제대로 작동하도록하려면 millis 또는 micros 기능을 사용하고 디 바운싱 시간을 조정하여 푸시 버튼의 디 바운스 문제를 제거해야합니다. 이 함수에서 아래와 같이 interruptHandler () 함수를 호출 합니다.
긴 디 바운싱 _ 시간 = 150; 휘발성 unsigned long last_micros; void debounceInterrupt () { if ((long) (micros ()-last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
에서 인터럽트 핸들러 () 함수를 호출 xSemaphoreGiveFromISR () API를.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
이 기능은 LED를 켜기 위해 TaskLed에 세마포어를 제공합니다.
5. TaskLed 함수를 생성 하고 while 루프 내에서 xSemaphoreTake () API를 호출 하고 세마포어가 성공적으로 사용되었는지 확인합니다. pdPASS (즉 1)와 같으면 아래와 같이 LED를 토글합니다.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, OUTPUT); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8 ,! digitalRead (8)); } } }
6. 또한 7 번 핀에 연결된 다른 LED를 깜박이는 기능을 생성합니다.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, OUTPUT); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. 무효 루프 기능은 비어 있습니다. 잊지 마세요.
무효 루프 () {}
이 튜토리얼의 끝에서 완전한 코드를 찾을 수 있습니다. 이제이 코드를 업로드하고 회로도에 따라 LED와 푸시 버튼을 Arduino UNO에 연결합니다.
회로도
코드를 업로드하면 200ms 후 LED가 깜빡이고 버튼을 누르면 바로 마지막에 주어진 비디오와 같이 두 번째 LED가 켜집니다.
이러한 방식 으로 Arduino가 있는 FreeRTOS에서 세마포어를 사용할 수 있습니다. 여기서는 한 작업에서 다른 작업으로 손실없이 데이터를 전달해야합니다.
이제 Mutex가 무엇이며 FreeRTOS를 사용하는 방법을 살펴 보겠습니다.
뮤텍스 란?
위에서 설명한 바와 같이 세마포어는 시그널링 메커니즘이며, 마찬가지로 뮤텍스는 증가와 감소를위한 별도의 기능을 가진 세마포어와 달리 잠금 메커니즘이지만 뮤텍스에서는 그 기능이 자체적으로 취하고 제공합니다. 공유 자원의 손상을 방지하는 기술입니다.
공유 리소스를 보호하기 위해 리소스에 토큰 카드 (뮤텍스)를 할당합니다. 이 카드를 가진 사람은 누구나 다른 리소스에 액세스 할 수 있습니다. 다른 사람들은 카드가 반환 될 때까지 기다려야합니다. 이러한 방식으로 하나의 리소스 만 작업에 액세스 할 수 있고 다른 리소스는 기회를 기다립니다.
예제를 통해 FreeRTOS 의 Mutex를 이해해 보겠습니다.
여기에는 LCD에 데이터를 인쇄하는 작업, LCD에 LDR 데이터를 보내는 작업, LCD에 온도 데이터를 보내는 마지막 작업의 세 가지 작업이 있습니다. 그래서 여기에 두 개의 작업이 동일한 리소스, 즉 LCD를 공유하고 있습니다. LDR 작업과 온도 작업이 동시에 데이터를 보내면 데이터 중 하나가 손상되거나 손실 될 수 있습니다.
따라서 데이터 손실을 방지하려면 작업 1이 표시 작업을 완료 할 때까지 LCD 리소스를 잠 가야합니다. 그러면 LCD 작업이 잠금 해제되고 task2가 작업을 수행 할 수 있습니다.
아래 다이어그램에서 뮤텍스와 세마포어의 작동을 관찰 할 수 있습니다.
FreeRTOS에서 Mutex를 사용하는 방법은 무엇입니까?
뮤텍스는 또한 세마포어와 같은 방식으로 사용됩니다. 먼저 생성 한 다음 각 API를 사용하여주고받습니다.
뮤텍스 만들기:
Mutex를 생성하려면 xSemaphoreCreateMutex () API를 사용하십시오 . 이름에서 알 수 있듯이 Mutex는 Binary 세마포의 한 유형입니다. 그들은 다른 맥락과 목적으로 사용됩니다. 바이너리 세마포어는 작업 동기화를위한 것이고 Mutex는 공유 자원을 보호하기 위해 사용됩니다.
이 API는 인수를 취하지 않으며 SemaphoreHandle_t 유형의 변수를 반환합니다. 뮤텍스를 만들 수없는 경우 xSemaphoreCreateMutex () 는 NULL을 반환합니다.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
뮤텍스 복용:
작업이 리소스에 액세스하려고 할 때 xSemaphoreTake () API를 사용하여 Mutex를 사용 합니다. 이진 세마포어와 동일합니다. 또한 두 개의 매개 변수를 사용합니다.
xSemaphore: 우리의 경우 mutex_v 에서 취할 뮤텍스의 이름.
xTicksToWait: 뮤텍스를 사용할 수있을 때까지 작업이 차단됨 상태에서 대기하는 최대 시간입니다. 프로젝트에서 xTicksToWait 를 portMAX_DELAY 로 설정 하여 task_1 이 mutex_v 를 사용할 수 있을 때까지 Blocked 상태에서 무기한 대기하도록합니다.
뮤텍스 제공:
공유 리소스에 액세스 한 후 작업은 다른 작업이 액세스 할 수 있도록 Mutex를 반환해야합니다. xSemaphoreGive () API는 Mutex를 돌려주는 데 사용됩니다.
xSemaphoreGive () 함수는 우리의 경우 mutex_v에 주어질 Mutex 인 하나의 인자만을받습니다.
위의 API를 사용하여 Arduino IDE를 사용하여 FreeRTOS 코드에 Mutex를 구현해 보겠습니다.
뮤텍스 코드 설명
여기서이 부분의 목표는 직렬 모니터를 공유 리소스로 사용하고 직렬 모니터에 액세스하여 일부 메시지를 인쇄하는 데 두 가지 다른 작업을 사용하는 것입니다.
1. 헤더 파일은 세마포어와 동일하게 유지됩니다.
# 포함 # 포함
2. Mutex의 값을 저장할 SemaphoreHandle_t 유형의 변수를 선언합니다.
SemaphoreHandle_t mutex_v;
3. void setup ()에서 9600 baud rate로 직렬 모니터를 초기화하고 xTaskCreate () API를 사용하여 두 개의 작업 (Task1 및 Task2)을 만듭니다 . 그런 다음 xSemaphoreCreateMutex ()를 사용하여 Mutex를 만듭니다 . 우선 순위가 동일한 작업을 만들고 나중에이 번호로 플레이 해보십시오.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) {Serial.println ("Mutex를 만들 수 없습니다"); } xTaskCreate (Task1, "작업 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "작업 2", 128, NULL, 1, NULL); }
4. 이제 Task1과 Task2에 대한 작업 기능을 만듭니다. 작업 함수 의 while 루프에서 직렬 모니터에 메시지를 인쇄하기 전에 xSemaphoreTake () 를 사용하여 Mutex를 가져온 다음 메시지를 인쇄 한 다음 xSemaphoreGive () 를 사용하여 Mutex를 반환해야합니다 . 그런 다음 약간 지연하십시오.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Hi from Task1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
마찬가지로, 500ms의 지연으로 Task2 기능을 구현하십시오.
5. Void loop () 는 비어 있습니다.
이제이 코드를 Arduino UNO에 업로드하고 직렬 모니터를 엽니 다.
task1 및 task2에서 메시지가 인쇄되는 것을 볼 수 있습니다.
Mutex의 작동을 테스트하려면 xSemaphoreGive (mutex_v); 모든 작업에서. 프로그램이 마지막 인쇄 메시지에서 멈춘 것을 볼 수 있습니다 .
이것이 Arduino를 사용하여 FreeRTOS에서 Semaphore 및 Mutex를 구현하는 방법입니다. Semaphore 및 Mutex에 대한 자세한 내용은 FreeRTOS의 공식 설명서를 참조하십시오.
Semaphore 및 Mutes에 대한 전체 코드와 비디오가 아래에 나와 있습니다.