본문 바로가기
Embedded

Arduino, FreeRTOS, semaphore, mutex 개요

by gigasound 2021. 10. 23.

 

 

 

 


RTOS 동기화

RTOS에서 태스크 동기화(task synchronization)는 한마디로 특정 자원을 여려 태스크가 공유해 사용할 때 발생하는 교착상태(deadlock)를 해소하는 방법을 의미합니다. 

교착상태는 하나의 자원을 여러 태스크가 서로 사용하겠다고 또는 어떤 이유에서 인지 태스크가 반환하지 못하는 경우에 발생해서 관련된 태스크들이 동작을 멈추는 현상으로 RTOS가 가지는 문제 중 하나입니다. 

예를 들어 아두이노에서 Serial.pintln()을 두 개의 태스크가 서로 사용하려 하면 둘 중에 하나의 태스크만 사용할 수 있거나, 둘 다 사용하지 못하는 경우가 발생합니다. 

이와 같은 문제를 해결하는 방법이 동기화이고 FreeRTOS는 세마포어(semaphore)와 뮤텍스(mutex)를 사용합니다. 


세마포어

세마포어에는 이진 세마포어(binary semaphore)와 계수형 세마포어(counting semaphore)가 있습니다. 

  • 이진형 세마포어 : 두 가지 상태만 가지기 때문에 붙여진 이름입니다. 아래서 설명한 뮤 텍스도 두 가지 상태만 가지기 때문에 FreeRTOS는 이진 세마 모어를 뮤 텍스처 럼 사용합니다. 그러나 엄밀히 말하면 두 내용 조금 다릅니다.
  •  계수형 세마포어 : 자원에 다수의 태스크가 접근이 가능하고, 대기하고 있다가 사용권한을 얻으면 태스크가 자원을 사용하는 방식입니다. 

다른 글에서 세마포어를 사용한 시리얼통신에 대해 설명한 적이 있기 때문에 예제는 생략하겠습니다.


뮤텍스

  • 뮤텍스는 두 개의 상태만 가집니다. 일반적으로 lock, unlock 상태라고 부르지만 FreeRTOS에서는 이진형 세마포어를 사용하기 때문에 다른 형태를 사용합니다.
  • 뮤텍스는 태스크가 특정 자원을 사용할 때 lock을 설정하고 사용을 다했으면 unlock을 해서 다른 태스크가 사용하도록 유도합니다. 그래서 사용하고자 하는 자원에서 뮤텍스 상태를 제어합니다. 
  • 뮤텍스는 인터럽(interrupt)에서 사용할 수 없지만, FreeRTOS에서는 이진형 세마포어를 사용하기 때문에 인터럽 접근이 가능합니다.
  • 단 뮤텍스는 우선순위가 높은 태스크가 계속 자원을 사용하면 낮은 태스크가 접근하지 못할 수 있는 단점이 있습니다. 

다음은 뮤텍스와 Arduino Mega 등에서도 사용이 가능하도록 참조의 _printf()를 확장한 내용입니다. 즉 C언어의 printf 형식을 사용하면서 여러 uart 포트에 접근하면서 태스크 동기화까지 합니다.

RTOS를 사용하지 않는다면 _printf()에서 RTOS와 관련된 두 개의 함수만 삭제하면 됩니다.  

// ref : https://studymake.tistory.com/696
// 설정방법을 참조해야합니다.

#include <FreeRTOS_AVR.h>
#include <stdio.h>
#include <stdarg.h>

#define UART0_USING (0)
#define UART1_USING (1)
SemaphoreHandle_t tx_mutex;

setup(){
	Serial.begin(115200);
    Serial1.begin(115200);
    tx_mutex = xSemaphoreCreateMutex();
    //- freertos 실행
	vTaskStartScheduler();
	//- freerto 오류임을 알림 
	Serial.println("FreeRTOS Error.");
	while(1){;}
}

lop(){
}

// uart_nu = UART1 or UART2
void _printf(int uart_nu, const char *s, ...){
	xSemaphoreTake(tx_mutex,portMAX_DELAY);
	va_list args;
	va_start(args, s);
	int n = vsnprintf(NULL, 0, s, args);
	char *str = new char[n+1]; 
	vsprintf(str, s, args);
	va_end(args);
	//gpio.Control_LED4(LED4_SERIAL_PIN,HIGH);
	switch(uart_nu){
		case UART0_USING:
			Serial.print(str);
			break;
		case UART1_USING:
			Serial1.print(str);
			break;
	}
  	delete [] str;
    xSemaphoreGive(tx_mutex);
}
  • SemaphoreHandle_t tx_mutex로 UART 자원을 태스크 동기화할 뮤텍스를 선언
  • tx_mutex = xSemaphoreMutex()로 이진형 세마포어를 뮤텍스 생성
  • _printf()에서 Serial, Serial1에 접근하기 위한 키값으로 UART0_USING, UART1_USING을 사용 
  • _pritnf()를 어느 함수 또는 태스크에서 사용을 시작하면 xSemaphoreTake()로 뮤텍스를 얻어서 자원에 대한 점유를 lock으로 설정 
  • Serial로 통신이 완료되면 xSemaphoreGive()로 뮤텍스를 반환해서 unlock으로 자원 점유 해제해서 다른 태스크에서 서 uart 자원을 사용하도록 허용.   

광고좀 꾹 눌러주시면 고맙겠습니다. 


위의 내용을 참조용으로만 사용해주세요. 무단 도용이나 무단 복제는 불허합니다.

기타 문의 사항은 gigasound@naver.com에 남겨 주시면 고맙겠습니다.