본문 바로가기
DSP, MCU/STM32 (ARM Cortex-M)

STM32 ] FreeRTOS + Semaphore ISR 사용 예제

by eteo 2022. 8. 8.

 

추가설명 되지 않은 기타 설정은 이전 글과 동일하다.

2022.08.08 - [MCU/STM32 (ARM Cortex-M)] - STM32 ] FreeRTOS 사용해보기

 

STM32 ] FreeRTOS 사용해보기

정의 RTOS는 실시간 시스템을 위해 개발된 운영체제로, 멀티태스킹 환경에서 Task 처리시간을 일관되게 유지하기 위한 용도로 사용한다. RTOS는 시분할 시스템 하에서 우선순위 기반 스케줄링을 통

eteo.tistory.com

 

 

 

 

예제 코드 출처 : https://m.blog.naver.com/eziya76/220951244572

 

Mutex는 ISR(Interrupt Service Routine)에서 신호를 보내는게 불가능한데 세마포어는 ISR에서 신호를 보낼 수 있다. Task 중 세마포어를 대기하고 있는 Task가 있다면 ISR에서 세마포어를 Release하여 해당 Task가 구동되도록 구현할 수 있다.

 

아래는 EXTI 로 세마포어 Release를 하면 Task에서 wait 하고 있다가 신호를 받고 LED를 100ms 간 켜는 예제를 해 본 것이다.

 

 

 

 

유저버튼 핀 EXTI로 설정

 

GPIO에서 NVIC enable

 

 

 

 

 

 

그리고 NVIC 탭에서 보면 ETXI line[15:10]의 우선권이 5인데 이 부분을 코드상에서 수정해줘야 한다.

 

 

NVIC-Code generation 부분에서 EXTI 인터럽트의 Select for init sequence ordering 란 에 체크한 후 코드 생성하고

 

 

main.c 아랫부분에 생성된 EXTI Priority를 Set하는 코드를 다음과 같이 수정한다.

 

static void MX_NVIC_Init(void)
{
  /* EXTI15_10_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY+1, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}

 

이렇게 해주는 이유는 FreeRTOSConfig.h 에 아래와 같은 경고문구가 있고 이를 만족하지 못하면 ISR이 호출된 후에 Task로 제어권이 넘어가지 않기 때문이라고 한다.

우선순위가 같은건 문제되지 않는것 같다.

참고로 인터럽트 우선권의 숫자는 낮을 수록 높은 우선순위를 갖게 된다.

(반면, Task 는 0에서 FreeRTOSConfig.h에 정의된 configMAX_PRIORITIES-1 사이의 우선순위를 가질 수 있는데 높은 숫자일 수록 높은 우선순위를 갖는다.)

 

 

 

 

 

그리고 샘플 코드를 따라하다 보니 중간에 오류가 생겨 CMSIS_V1로 바꾸어 주었다.

 

 

 

 

버전1 과 버전2 의 API 는 아래와 같은차이가 있다.

 

cmsis_os.h 가 cmsis_os2.h 로 변경되었다.

-Create 대신 -New 함수를 쓰고 -Wait 대신 -Acquire 함수를 쓴다.

 

 

버전1

 

 

버전2

 

 

 

 

디폴트로 생성된 Task를 아래처럼 Task Name 과 Task 함수명을 수정해준다.

 

 

 

그리고 바이너리 세마포어도 아래와 같이 만들어주었다.

 

 

 

 

 

 

 

stm32f42xx_it.c 파일에 따라 들어가서 EXTI 콜백 함수를 가져와 재정의 한다.

 

main.c

/* USER CODE BEGIN PV */
extern osSemaphoreId hBinarySemHandle;
/* USER CODE END PV */

//...

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_13){
		osSemaphoreRelease(hBinarySemHandle);
	}
}
/* USER CODE END 4 */

 

file을 per peripheral 로 구분하여 생성했기 때문에 extern으로 세마포어 핸들러를 가져오고, EXTI 콜백함수에선 osSemaphoreRelease 로 신호 플래그를 보낸다.

 

 

freertos.c

 

void MX_FREERTOS_Init(void) {

  osSemaphoreDef(hBinarySem);
  hBinarySemHandle = osSemaphoreCreate(osSemaphore(hBinarySem), 1);

  osThreadDef(hSemTask, SemaphoreTask, osPriorityNormal, 0, 128);
  hSemTaskHandle = osThreadCreate(osThread(hSemTask), NULL);

  osSemaphoreWait(hBinarySemHandle, 0);

}

//...

/* USER CODE END Header_SemaphoreTask */
void SemaphoreTask(void const * argument)
{
  /* USER CODE BEGIN SemaphoreTask */
  /* Infinite loop */
  for(;;)
  {
	  if(hBinarySemHandle != NULL)
	  {
		  if(osSemaphoreWait(hBinarySemHandle, 0) == osOK)
		  {
			  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
			  osDelay(100);
			  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
		  }
	  }
  }
  /* USER CODE END SemaphoreTask */
}

 

SemaphoreTask 함수를 살펴보면 첫번째 매개변수는 세마포어 핸들러이고, 두번째 매개변수는 대기시간이다. 두번째에 osWaitForever를 쓰면 이 Task 는 0xFFFFFFFFU 시간동안 hBinarySemHandle 세마포어의 신호 플래그를 기다리고 있을 것이다. 그리고 반환값이 osOK인 경우는 세마포어를 획득했다는 뜻으로 0이다.

 

그리고 EXTI 콜백함수에서 osSemaphoreRelease(hBinarySemHandle); 함수를 쓰면 이 세마포어를 사용할 수 있게되고 이를 기다리는 다른 작업들이 획득할 수 있는 상태가 된다.

 

위 코드 상에는 시스템 리셋시 최초에 한번 LED가 켜지는 것을 막기 위해 쓰레드 생성 구문 바로 아래에 osSemaphoreWait() 함수를 한번 썼다.

 

+ 참고로 카운팅 세마포어의 경우 FreeRTOS API에선 MAX Count와 Initial Count 를 설정할 수 있는데 CMSIS API에서는 그냥 Count 만 설정할 수 있고 초기 Count 값이 MAX Count 인채로 시작한다.

바이너리 세마포어의 경우도 마찬가지로 CMSIS API에서는 초기 Count 값이 1인채로 시작한다. 위에서 나는 osSemaphoreWait() 함수를 써주는 식으로 처리했는데 아마 세마포어 생성 함수 내부를 파보면 설정을 바꿀 수 있을 것 같다.

 

 

 

FreeRTOS 관련해서는 나중에 Controllerstech 영상보고 좀더 공부해서 정리글을 올리려고 계획하고 있다.

 

 

 

참고 : https://m.blog.naver.com/eziya76/220951244572