이벤트
- 어떤 임베디드 시스템에서 사용자가 버튼을 누른 상황을 가정하자
- 버튼에 연결된 스위치가 전기신호를 발생시킨다
- 컨트롤러는 전기 신호를 인식한다
- 물리적 전기신호를 소프트웨어적 인터럽트인 IRQ나 FIQ를 발생시킨다
- IRQ나 FIQ 핸들러에서 인터럽트 컨트롤러의 레지스터를 읽고 어떤 인터럽트인지 확인한다
- 해당 인터럽트의 핸들러가 동작한다
- 이번 장에서 인터럽트 핸들러를 태스크로 옮겨보자
- 이때 이벤트가 인터럽트와 태스크 사이의 연결매체 역할을 수행한다
이벤트 플래그
- 이벤트를 bitmap으로 만들면 각가의 이벤트를 명확히 구분할 수 있다
- 예를 들어, 0b1001은 0b1000과 0b0001 두 이벤트가 발생해서 pending 상태라는 뜻으로 표현할 수 있다
# kernel/event.h
typedef enum KernelEventFlag_t
{
KernelEventFlag_UartIn = 0x00000001,
KernelEventFlag_Reserved01 = 0x00000002,
KernelEventFlag_Reserved02 = 0x00000004,
KernelEventFlag_Reserved03 = 0x00000008,
KernelEventFlag_Reserved04 = 0x00000010,
KernelEventFlag_Reserved05 = 0x00000020,
KernelEventFlag_Reserved06 = 0x00000040,
...
KernelEventFlag_Reserved29 = 0x20000000,
KernelEventFlag_Reserved30 = 0x40000000,
KernelEventFlag_Reserved31 = 0x80000000,
KernelEventFlag_Empty = 0x00000000,
} KernelEventFlag_t;
void Kernel_event_flag_set(KernelEventFlag_t event);
void Kernel_event_flag_clear(KernelEventFlag_t event);
bool Kernel_event_flag_check(KernelEventFlag_t event);
- 이벤트 플래그들을 먼저 선언한다
- 우선 UartIn 이벤트만 선언해두었다
- 32bit 변수에 32개 이벤트를 예약해두었다
# kernel/event.c
static uint32_t sEventFlag;
void Kernel_event_flag_init(void)
{
sEventFlag = 0;
}
void Kernel_event_flag_set(KernelEventFlag_t event)
{
sEventFlag |= (uint32_t)event;
}
void Kernel_event_flag_clear(KernelEventFlag_t event)
{
sEventFlag &= ~(uint32_t)event;
}
void Kernel_event_flag_check(KernelEventFlag_t event)
{
if (sEventFlag & (uint32_t)event)
{
Kernel_event_flag_clear(event);
return true;
}
return false;
}
- 이벤트 init/set/clear/check에 대한 함수도 만들었다
- check 함수에서는 파라미터로 전달된 이벤트가 pending 상태면 clear 후 true를 반환한다
- 태스크에서 커널 API를 통해 이벤트를 처리할 수 있도록 구성해보자
# kernel/Kernel.c
void Kernel_send_events(uint32_t event_list)
{
for (uint32_t i = 0; i < 32; i++)
{
if ((event_list >> i) & 1)
{
KernelEventFlag_t sending_event = KernelEventFlag_Empty;
sending_event = (KernelEventFlag_t)SET_BIT(sending_event, i);
Kernel_event_flag_set(sending_event);
}
}
}
void Kernel_wait_events(uint32_t waiting_list)
{
for (uint32_t i = 0; i < 32; i++)
{
if ((waiting_list >> i) & 1)
{
KernelEventFlag_t waiting_event = KernelEventFlag_Empty;
waiting_event = (KernelEventFlag_t)SET_BIT(waiting_event, i);
if (Kernel_event_flag_check(waiting_event))
{
return waiting_event;
}
}
}
return KernelEventFlag_Empty;
}
Kernel_send_events는 이벤트를 전달하는 함수이다
- 파라미터가 uint32_t인 것은, 여러개 이벤트를 한번에 보내고 받기 위함이다
Kernel_send_events(event1|event2|event3) 처럼 caller에서 or연산으로 한번에 보낼 수 있다
- 마찬가지로, 자신이 기다리는 이벤트의 전체 목록을
Kernel_wait_events에 한번에 보내 처리할 수 있다
인터럽트와 이벤트
- 이벤트는 인터럽트와 엮어서 사용하는 것이 일반적이다
- 지금까지 구현으로는 UART 입력 인터럽트가 발생하면 UART 하드웨어에서 입력된 글자를 받아 그대로 UART 출력하는 일을 한다
- 우선 이벤트를 발생시켜서 이벤트 핸들러가 동작하는지 확인해보자
# hal/rvpb/Uart.c
static void interrupt_handler(void)
{
uint8_t ch = Hal_uart_get_char();
Hal_uart_put_char(ch);
Kernel_send_events(KernelEventFlag_UartIn);
}
- Uart 이벤트 핸들러에서 UartIn 이벤트를 등록하자
# boot/Main.c
void my_task(void)
{
uint32_t local = 0;
debug_printf("task_1 SP=0x%x\n", &local);
while (true)
{
KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn);
switch (handle_event)
{
case KernelEventFlag_UartIn:
debug_printf("\nEvent handled\n");
break;
}
Kernel_yield();
}
}
static void Kernel_init(void)
{
uint32_t taskId;
Kernel_task_init();
Kernel_event_flag_init();
if (Kernel_task_create(my_task) == NOT_ENOUGH_TASK_NUM)
{
debug_printf("dummy task creation failed\n");
}
Kernel_start();
}
- Kernel 초기화 부분에서 event를 초기화한다
- my_task에서 UartIn 이벤트를 기다리고 도착했으면 debug_print로 출력해보자

- 이렇게 수정한 코드를 실행해보면 위와 같이 확인할 수 있다
사용자 정의 이벤트
- 이벤트는 인터럽트에서 파생된 경우에만 사용해야 하는 것은 아니다
- Task에서 Task로 이벤트를 전송할 수도 있다
# kernel/event.h
typedef enum KernelEventFlag_t
{
KernelEventFlag_UartIn = 0x00000001,
KernelEventFlag_CmdIn = 0x00000002,
...
} KernelEventFlag_t;
- Task0에서 Task1로 보내는 이벤트를
CmdIn으로 정의하고 동작시켜보자
void task_0(void)
{
uint32_t local = 0;
debug_printf("task_1 SP=0x%x\n", &local);
while (true)
{
KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn);
switch (handle_event)
{
case KernelEventFlag_UartIn:
debug_printf("\nEvent handled by Task0\n");
Kernel_send_events(KernelEventFlag_CmdIn);
break;
}
Kernel_yield();
}
}
void task_1(void)
{
uint32_t local = 0;
debug_printf("task_1 SP=0x%x\n", &local);
while (true)
{
KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_CmdIn);
switch (handle_event)
{
case KernelEventFlag_CmdIn:
debug_printf("\nEvent handled by Task1\n");
break;
}
Kernel_yield();
}
}
static void Kernel_init(void)
{
uint32_t taskId;
Kernel_task_init();
Kernel_event_flag_init();
if (Kernel_task_create(task_0) == NOT_ENOUGH_TASK_NUM)
{
debug_printf("dummy task creation failed\n");
}
if (Kernel_task_create(task_1) == NOT_ENOUGH_TASK_NUM)
{
debug_printf("dummy task creation failed\n");
}
Kernel_start();
}
- Uart 인터럽트가 발생하면 Uart 인터럽트 핸들러에서
UartIn 이벤트를 보낸다
- 이후에 스케줄러에 의해 실행된 Task0이
UartIn 이벤트를 확인하고 받아온다
- Task0의 이벤트 처리 코드가 실행되면서
CmdIn 이벤트를 보낸다
- 이후 스케줄러에 의해 실행된 Task1이
CmdIn 이벤트를 확인하고 받아온다
- Task1의 이벤트 처리코드가 실행된다
