STM32/STM32_RTOS

STM32 RTOS 알아보기 7편(Semaphore)

원원 2025. 7. 21. 20:37

안녕하세요. 오늘은 세마포어에 대해 알아보겠습니다.

세마포어는 여러 태스크나 인터럽트가 서로 순서를 지키거나, 정해진 개수만큼만 어떤 자원을 사용할 수 있게 해주는 장치입니다. RTOS에서는 이진 세마포어(Binary Semaphore), 카운팅 세마포어(Counting Semaphore) 두 가지 형태로 사용합니다.
* 이진 세마포어 : 값이 0 또는 1만 가지며, 주로 태스크 동기화나 인터럽트에서 신호를 전달할 때 사용합니다.
* 카운팅 세마포어 : 0 이상의 값을 가지며, 한정된 개수의 리소스(버퍼,큐 등)를 관리할 때 사용합니다.
이전에 뮤텍스는 take, give를 한 태스크에서만 해야했는데 세마포어는 다른 태스크, 다른 인터럽트에서 take, give를 해도 됩니다.
task에서 사용하는 이진세마포어, 카운팅세마포어, 뮤텍스 모두 획득과 반환하는 함수명이 같고 생성하는 함수만 다릅니다.
- 획득
1. xSemaphoreTake()
- 반환
1. xSemaphoreGive()
2. xSemaphoreGiveFromISR() - 이진세마포어, 카운팅세마포어만 사용


이진 세마포어부터 알아보겠습니다.
예시로 어떤 task에서 버튼이벤트를 처리하고, 버튼이벤트가 들어오지않으면 해당 task는 block 상태입니다.
버튼은 3개를 사용합니다.

void task(void *pvParameters);

SemaphoreHandle_t xButtonSemaphore;

int global_i=0;
uint16_t GPIO_Pin_BTN;

void myTask()
{

  xButtonSemaphore  = xSemaphoreCreateBinary(); // Binary Semaphore
  //xButtonSemaphore  = xSemaphoreCreateCounting(3,0); // Counting Semaphore


  xTaskCreate(task, "Task1", 256, "TASK1", 3, NULL);
}

void task(void *pvParameters)
{
  int local_i = 0;

  while(1)
  {
    if (xSemaphoreTake(xButtonSemaphore, portMAX_DELAY) == pdTRUE)
    {
      printf("BTN PIN , Counting , local_i : %x,  %lu,  %d \n",GPIO_Pin_BTN, uxSemaphoreGetCount(xButtonSemaphore), local_i);
      local_i++;
    }
    vTaskDelay(100);
  }
}


#define DEBOUNCE_TIME_MS 50
uint32_t debounce_time;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  //Debounce
  uint32_t now = HAL_GetTick();
  if (now-debounce_time < DEBOUNCE_TIME_MS)
    return;
  debounce_time = now;


  GPIO_Pin_BTN = GPIO_Pin;

  xSemaphoreGiveFromISR(xButtonSemaphore, &xHigherPriorityTaskWoken);
  //xSemaphoreGiveFromISR(xButtonSemaphore, &xHigherPriorityTaskWoken);
  //xSemaphoreGiveFromISR(xButtonSemaphore, &xHigherPriorityTaskWoken);

  portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

xSemaphoreCreateBinary()를 통해서 이진세마포어를 생성하고 외부인터럽트 발생시 세마포어를 give합니다.
uxSemaphoreGetCount() 함수는 현재 세마포어에 쌓여있는 개수를 리턴하는 함수입니다. take한 이후에 읽으므로 항상 0이 나오게 됩니다.

코드를 보면 xSemaphoreGiveFromISR 주석이 2개 있습니다.
xSemaphoreGiveFromISR를 연속으로 3번 하게되도 세마포어 카운터는 1이상 올라가지않으므로 결과는 같습니다.


다음으로는 카운팅 세마포어에 대해 알아보겠습니다.
위의 코드에서 바이너리세마포어생성을 주석하고 카운팅세마포어의 주석을 해제합니다.
그리고 give도 연속으로 3번하고 결과를 보겠습니다.
버튼을 한번 클릭할때마다 로그가 3개씩 나옵니다. 세마포어의 갯수 또한 2->1->0으로 나오게 됩니다.


세마포어와 뮤텍스의 사용 용도입니다.
1. Binary Semaphore (이진 세마포어)
- 값이 0 또는 1만 될 수 있음 (On/Off, Mutex 비슷하게 쓸 수 있음)
- 주로 이벤트 알림이나 인터럽트 신호 전달에 사용

2. Counting Semaphore (카운팅 세마포어)
- 값이 0 ~ N까지 가능
- 동시에 여러 개의 리소스 제어(예: 버퍼 슬롯, 멀티 리더 등)에 사용

3. Mutex (뮤텍스)
- 뮤텍스(Mutex)도 세마포어의 일종으로 취급함
- 리소스 보호용(임계구역, 공유 자원 보호)에 최적화

소스코드(Commits: Semaphore) 
https://github.com/yhunterr/STM32_RTOS_STUDY/commits/main/