인터럽트 핸들러의 구체적인 기능을 태스크로 옮기는것이 더 바람직하다.
이를 위해서 인터럽트와 태스크 간의 연결 매체가 필요한데, 이때 사용하는것이 이벤트이다.
이벤트를 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 연산을 통해서 한꺼번에 모든 이벤트를 전달할 수 있다.
인터럽트와 이벤트를 연결해서 인터럽트 핸들러의 기능을 태스크의 이벤트 핸들러로 옮겨보자
먼저 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 }
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 }
이런식으로 태스크간에 사용자가 정의한 이벤트를 주고 받을 수 있다.