Chapter 11: Events

1231·2024년 7월 15일

임베디드 OS 개발

목록 보기
8/8

Event Flag

인터럽트 핸들러의 구체적인 기능을 태스크로 옮기는것이 더 바람직하다.
이를 위해서 인터럽트와 태스크 간의 연결 매체가 필요한데, 이때 사용하는것이 이벤트이다.

이벤트를 bitmap 형태로 만들어 Event Flag 개념을 구현한다.
kernel/event.h

 1 #ifndef KERNEL_EVENT_H
  2 #define KERNEL_EVENT_H
  3
  4 typedef enum KernelEventFlag_t {
  5   KernelEventFlag_UartIn        =       0x00000001,
  6   KernelEventFlag_Reserved01    =       0x00000002,
 ...
 38 } KernelEventFlag_t;
 39
 40 void Kernel_event_flag_init(void);
 41 void Kernel_event_flag_set(KernelEventFlag_t event);
 42 void Kernel_event_flag_clear(KernelEventFlag_t event);
 43 _Bool Kernel_event_flag_check(KernelEventFlag_t event);
 44
 45 #endif

32 bit 변수 한개로는 이벤트 플래그 32개를 표시할 수 있으므로 typedef 를 통해 이벤트 플래그 자리 32개를 예약해놨다.

그렇다면, 이러한 이벤트 플래그를 set, clear, check 하는 함수가 필요하다.
kernel/event.c

  1 #include "stdint.h"
  2 #include "stdbool.h"
  3
  4 #include "stdio.h"
  5 #include "event.h"
  6
  7 static uint32_t sEventFlag;
  8
  9 void Kernel_event_flag_init(void) {
 10         sEventFlag = 0;
 11 }
 12
 13 void Kernel_event_flag_set(KernelEventFlag_t event) {
 14         sEventFlag |= (uint32_t)event;
 15 }
 16
 17 void Kernel_event_flag_clear(KernelEventFlag_t event) {
 18         sEventFlag &= ~((uint32_t)event);
 19 }
 20
 21 _Bool Kernel_event_flag_check(KernelEventFlag_t event) {
 22         if(sEventFlag & (uint32_t)event){
 23           Kernel_event_flag_clear(event);
 24           return true;
 25         }
 26         return false;
 27 }

태스크 관련 함수와 마찬가지로 커널 API 를 통해서 이벤트를 처리하게 한다.
kernel/Kernel.c

 4 #include "event.h"
 5 #include "memio.h"
 ...
 15 void Kernel_send_events(uint32_t eventlist) {
 16   for(uint32_t i = 0; i < 32; i++) {
 17     if((eventlist >> i) & 1){
 18       KernelEventFlag_t sendingEvent = KernelEventFlag_Empty;
 19       sendingEvent = (KernelEventFlag_t)SET_BIT(sendingEvent, i);
 20       Kernel_event_flag_set(sendingEvent);
 21     }
 22   }
 23 }
 24
 25 KernelEventFlag_t Kernel_wait_events(uint32_t eventlist) {
 26   for(uint32_t i = 0; i < 32; i++) {
 27     if((eventlist >> i & 1)) {
 28       KernelEventFlag_t waitingEvent = KernelEventFlag_Empty;
 29       waitingEvent = (KernelEventFlag_t)SET_BIT(waitingEvent,i);
 30       if(Kernel_event_flag_check(waitingEvent)) {
 31         return waitingEvent;
 32       }
 33
 34
 35     }
 36   }
 37   return KernelEventFlag_Empty;
 38
 39 }

파라미터가 KernelEventFlag_t 가 아닌 uint32_t 라는 점이 중요하다. 이는 한번에 이벤트를 여러개 보내고 받기 위한 선택이다.
Kernel_send_events(event1|event2|event3) 와 같이 비트 OR 연산을 통해서 한꺼번에 모든 이벤트를 전달할 수 있다.

Interrupt & Event

인터럽트와 이벤트를 연결해서 인터럽트 핸들러의 기능을 태스크의 이벤트 핸들러로 옮겨보자
먼저 Uart 인터럽트 발생시 이벤트를 send 하도록 하자. 이렇게 인터럽트와 이벤트의 연결이 끝난다.
hal/rvpb/Uart.c

 49 static void interrupt_handler(void) {
 50   uint8_t ch = Hal_uart_get_char();
 51   Hal_uart_put_char(ch);
 52
 53   Kernel_send_events(KernelEventFlag_UartIn); //setting event flag
 54 } 

Main.c 테스크 코드에서 다음과 같은 코드를 추가하여 실행해보자

 72   while(true) {
 73     KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn);
 74     switch(handle_event) {
 75       case KernelEventFlag_UartIn:
 76         debug_printf("\nEvent Handled\n");
 77         break;
 78     }

User-defined Event

Uart 인터럽트 뿐만 아니라 태스크에서 태스크로 이벤트를 보낼 수 있다.
예를 들어, 다음과 같이 CmdIn 와 CmdOut 이벤트를 추가해보자
kernel/event.h

  6   KernelEventFlag_CmdIn         =       0x00000002,
  7   KernelEventFlag_CmdOut        =       0x00000004,

boot/Main.c

 68 void User_task0(void) {
 69   uint32_t local = 0;
 70   debug_printf("User Task #%u\n", local);
 71
 72   while(true) {
 73     KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn);
 74     switch(handle_event) {
 75       case KernelEventFlag_UartIn:
 76         debug_printf("\nEvent Handled by Task0\n");
 77         Kernel_send_events(KernelEventFlag_CmdIn);
 78         break;
 79     }
 80           Kernel_yield();
 81   }
 82 }
 83
 84 void User_task1(void) {
 85   uint32_t local = 1;
 86   debug_printf("User Task #%u\n", local);
 87
 88   while(true) {
 89     KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_CmdIn);
 90     switch(handle_event) {
 91       case KernelEventFlag_CmdIn:
 92         debug_printf("\nEvent Handled by Task1\n");
 93         break;
 94     }
 95           Kernel_yield();
 96   }
 97 }

이런식으로 태스크간에 사용자가 정의한 이벤트를 주고 받을 수 있다.

0개의 댓글