안녕하세요. 오늘은 RTOS에서 사용하는 portYIELD_FROM_ISR에 대해 알아보겠습니다.
제가 업로드했던 글 기준으로 3편에서 portYIELD_FROM_ISR 함수를 사용했었습니다.
portYIELD_FROM_ISR 는 ISR종료 후 즉시 태스크 전환이 필요한 경우 Context Switching을 수행하게 해줍니다.
port : FreeRTOS가 다양한 하드웨어에서 동작할 수 있도록 CPU별 맞춤 구현을 제공하는 하드웨어 적응 계층입니다.
YIELD : (양보하다) 현재 실행중인 태스크가 CPU실행을 포기하고 다른 태스크에게 실행을 양보합니다.
FROM_ISR : 해당기능(매크로 또는 함수)이 ISR에서 호출될 수 있음을 의미합니다.
portYIELD_FROM_ISR의 코드를 보겠습니다. 매크로로 되어있고, 만약 x가 pdFALSE라면 아무동작도 안하게 됩니다.
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
결국에는 0xe000ed04은 SCB->ICSR (Interrupt Control and State Register)이고 값을 (1UL<<28UL)로 세팅합니다.

PendSV(Pending Supervisor Call)는 주로 태스크 전환(Context Switching)을 수행할 때 사용됩니다.
PendSV는 현재 실행 가능한(Ready 상태) 태스크 중에서 가장 높은 우선순위의 태스크를 실행하도록 합니다.
예를 들어, ISR에서 `xQueueSendFromISR()` 함수를 호출하면 큐를 대기하고 있던 태스크가 Ready 상태로 변경됩니다.
이후, `portYIELD_FROM_ISR()`을 호출하여 PendSV 인터럽트를 발생시키면, ISR 종료 후 즉시 FreeRTOS 스케줄러가 실행됩니다. 스케줄러는 가장 높은 우선순위의 Ready 상태 태스크를 선택하고, PendSV는 해당 태스크로 전환을 수행합니다.
이제 실제로 코드에서 위와같이 동작하는지 확인해보겠습니다.
task1 : 우선순위1
task2 : 우선순위2
task1동작 : "C"를 딜레이없이 계속 출력
task2동작 : 큐에 데이터가들어오면 "B"출력
ISR동작 : 큐에 데이터 보내고 portYIELD_FROM_ISR를 사용/미사용으로 테스트
#include <stdio.h>
#include "cmsis_os.h"
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#define USE_YIELD_FROM_ISR 1
void task1(void *pvParameters);
void task2(void *pvParameters);
QueueHandle_t xQueue;
uint8_t input;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
input = 1;
if (xQueueSendFromISR(xQueue, &input, &xHigherPriorityTaskWoken) != pdPASS) {
printf("[ISR] Queue full! Failed to send button press event.\n");
}
#if USE_YIELD_FROM_ISR
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
printf("A");
}
void myTask()
{
xQueue = xQueueCreate(5, sizeof(uint8_t));
xTaskCreate(task1, "Task1", 256, NULL, 1, NULL);
xTaskCreate(task2, "Task2", 256, NULL, 2, NULL);
}
void task1(void *pvParameters)
{
while(1)
{
printf("C");
}
}
void task2(void *pvParameters)
{
uint8_t receivedValue = 0;
while(1)
{
if (xQueueReceive(xQueue, &receivedValue, portMAX_DELAY) == pdPASS)
{
printf("B");
}
}
}
테라텀으로 log를 저장해서 검색하는형식으로 찾았습니다.
portYIELD_FROM_ISR 사용 (USE_YIELD_FROM_ISR : 1)
ISR이후에 task1이 실행될때가 없었습니다.

1. 버튼 인터럽트 발생 -> HAL_GPIO_EXTI_Callback 실행하며 "A"출력
2. xQueueSendFromISR가 실행되며 task2가 Ready상태가 됨
3. portYIELD_FROM_ISR(xHigherPriorityTaskWoken)실행 : PendSV 인터럽트 발생
4. task2 실행하며 "B"출력
5. task2는 xQueueReceived에서 다시 대기(Blocked)
6. task1 실행 시작 -> "C" 계속출력
"AC"가 출력되는경우가 없음
portYIELD_FROM_ISR 미사용 (USE_YIELD_FROM_ISR : 0)
ISR이후에 task1가 바로 실행될때가 있었습니다.

1. 버튼 인터럽트 발생 -> HAL_GPIO_EXTI_Callback 실행하며 "A"출력
2. xQueueSendFromISR가 실행되며 task2가 Ready상태가 됨
3. task1이나 task2가 실행됨
4. (task1가 실행된 경우) "C"출력
"AC" 가 출력되는 경우가 존재함
예제를 통해서 portYIELD_FROM_ISR가 context Switching을 수행하게하는것을 확인했습니다.
예제에서 portYIELD_FROM_ISR(xHigherPriorityTaskWoken)를 보면 xHigherPriorityTaskWoken가 pdTRUE여야만 portYIELD_FROM_ISR가 수행되는데 xQueueSendFromISR를 호출했을때 xQueueReceive에 따라서 pdTRUE가 되는지 확인해보겠습니다.
xQueueReceive가 없는경우

xQueueReceive가 대기하고있는경우

'STM32 > STM32_RTOS' 카테고리의 다른 글
STM32 RTOS 알아보기 6편(Mutex) (0) | 2025.02.16 |
---|---|
STM32 RTOS 알아보기 4편(xTimerCreate,xTimerStart,xTimerStop) (0) | 2025.01.28 |
STM32 RTOS 알아보기 3편(xQueueSendFromISR,xQueueReceiveFromISR) (0) | 2024.10.04 |
STM32 RTOS 알아보기 2편(xQueueCreate,xQueueSend,xQueueReceive) (0) | 2024.08.18 |
STM32 RTOS 알아보기 1편(xTaskCreate, vTaskDelay, vTaskDelayUntil) (0) | 2024.08.11 |