[FreeRTOS] Interrupt
ISR (인터럽트 서비스 루틴) 내에서 시간이 오래 소요되는 작업을 처리하게 되면, 그 동안 다른 인터럽트를 받지 못하고 놓치게 되는 문제가 발생한다.
따라서 ISR 에서는 인터럽트 발생 여부만 체크하고, 실제 작업을 처리하는건 Task에서 수행하도록 하면 인터럽트를 놓치지 않을 수 있다.
간단하게 비교하자면 다음과 같다.
코드레벨에서는 다음과 같이 구현 할 수 있다.
TaskHandle_t xHandlingTask;
void vHandlingTask( void *pvParameters )
{
uint32_t ulNotifiedValue;
xHandlingTask = xTaskGetCurrentTaskHandle(); /* Store the handle of the calling task. */
while(1)
{
ulNotifiedValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY /* Block indefinitely. */ );
while(ulNotifiedValue > 0)
{
처리해야 할 작업( );
ulNotifiedValue--;
}
}
}
void vANInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(xHandlingTask, &xHigherPriorityTaskWoken); /* Unblocking Task */
portYIELD_FROM_ISR(&xHigherPriorityTaskWoken); /* Context Switch */
}
TaskHandle_t xHandlingTask;
void vHandlingTask( void *pvParameters )
{
uint32_t ulNotifiedValue;
xHandlingTask = xTaskGetCurrentTaskHandle(); /* Store the handle of the calling task. */
while(1)
{
ulNotifiedValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY /* Block indefinitely. */ );
while(ulNotifiedValue > 0)
{
처리해야 할 작업( );
ulNotifiedValue--;
}
}
}
void vANInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(xHandlingTask, &xHigherPriorityTaskWoken); /* Unblocking Task */
portYIELD_FROM_ISR(&xHigherPriorityTaskWoken); /* Context Switch */
}
# vTaskNotifyGiveFromISR
"인터럽트 서비스 루틴" (ISR) 에서 호출 할 수 있는 xTaskNotifyGive( ) 버전.
각 task 에는 task가 create 될 때 0으로 초기화 되는 32-bit짜리 notification value가 있다.
"Task Notification"은 blocked된 task를 unblocked 시키고 notification 값을 업데이트 하도록 task에 전달되는 이벤트다.
#include “FreeRTOS.h”
#include “task.h”
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
BaseType_t *pxHigherPriorityTaskWoken );
[Parameters]
- xTaskToNotify
"Task Notification" 이벤트를 받게 될 task의 핸들.
- pxHigherPriorityTaskWoken
*pxHigherPriorityTaskWoken 은 pdFALSE 로 초기화 되어야 함.
vTaskNotifyGiveFromISR( )은 대상 task를 blocked 상태에서, unblocked 상태로 만들고, notification 을 받은 task의 우선순위가 현재 실행 중인 task보다 우선 순위보다 높을 경우 *pxHigherPriorityTaskWoken 을 pdTRUE 로 설정 함.
vTaskNotifyGiveFromISR( )이 *pxHigherPriorityTaskWoken 값을 pdTRUE로 설정하면, 인터럽트가 종료되기 전에 Context-Switching을 요청 해야 함.
# ulTaskNotifyTake
#include “FreeRTOS.h”
#include “task.h”
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
TickType_t xTicksToWait );
[Parameters]
- xClearCountOnExit
pdFALSE : ulTaskNotifyTake( ) 가 종료되기 전에 task의 notification value가 감소 함.
pdTRUE : ulTaskNotifyTake( ) 가 종료되기 전에 task의 notification value가 0(zero)으로 재설정 됨.
- xTicksToWait
ulTaskNotifyTake( ) 가 호출될 때 notification이 아직 pending 중이 아닌 경우 notification 이벤트가 수신될 때 까지 blocked 상태에서 대기하는 최대 시간.
RTOS task는 blocked 상태에서 CPU 시간을 소모하지 않음.
단위는 RTOS tick 이며, pdMS_TO_TICKS( ) 매크로를 사용하여 원하는 밀리세컨드 기간을 tick으로 변환 할 수 있음.
[Return Value]
감소되거나 0으로 클리어 되기 전의 task의 notification value 값
아래와 같이 Task1, Task2 가 있고, 외부 인터럽트를 받아 처리하는 ISR이 있다고 할 때,
현재 진행중인 Task가 무엇인지, 우선순위가 무엇인지에 따라 상황이 달라진다.
( 항상 ISR에서 외부인터럽트를 받으면 Task1에게 Notification 이벤트를 준다고 하자 )
TaskHandle_t xTaskHandle1, xTaskHandle2;
void Task1(void *pvParameters)
{
uint32_t ulNotifiedValue;
while(1)
{
ulNotifiedValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
}
void Task2(void *pvParameters)
{
while(1)
{
// DO Something...
}
}
void ISR( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(xTaskHandle1, &xHigherPriorityTaskWoken); /* Unblocking Task */
portYIELD_FROM_ISR(&xHigherPriorityTaskWoken); /* Context Switch */
}
Case #1 ) 우선순위가 Task1 이 높을 때
task1의 우선순위가 높지만 시작하자마자 ulTaskNotifyTake( )를 호출 하였기 때문에, 인터럽트가 발생 할 때 까지 Blocked 상태로 대기하게 되며, 그 동안 Task2가 CPU를 선점하게 된다.