본문 바로가기

Firmware/RTOS

임베디드 OS 개발 프로젝트 14(Event 처리)

http://www.yes24.com/Product/Goods/84909414

.
├── boot
│   ├── Entry.S
│   ├── Handler.c
│   ├── Main.c
│   └── main.h
├── hal
│   ├── HalInterrupt.h
│   ├── HalTimer.h
│   ├── HalUart.h
│   └── rvpb
│       ├── Interrupt.c
│       ├── Interrupt.h
│       ├── Regs.c
│       ├── Timer.c
│       ├── Timer.h
│       ├── Uart.c
│       └── Uart.h
├── include
│   ├── ARMv7AR.h
│   ├── memio.h
│   ├── MemoryMap.h
│   ├── stdarg.h
│   ├── stdbool.h
│   └── stdint.h
├── kernel
│   ├── event.c
│   ├── event.h
│   ├── Kernel.c
│   ├── Kernel.h
│   ├── task.c
│   └── task.h
├── lib
│   ├── armcpu.c
│   ├── armcpu.h
│   ├── stdio.c
│   ├── stdio.h
│   ├── stdlib.c
│   └── stdlib.h
├── Makefile
└── navilos.ld

 

Non os 같은 경우 interrupt가 발생하면 interrupt handler 에서 해당 인터럽트를 처리하지만 

 

RTOS 같은 경우는 Task가 돌아가며 동작하기 때문에 특정 Task에서만 이미 발생된 interrupt를 처리해야 하는 경우도

있다

 

위와 같이 인터럽트와 Task 간의 연결 매체가 필요한데 이때 사용하는 것이 이벤트이다.

 

event.c

static uint32_t sEventFlag;

void Kernel_event_flag_init(void)
{
    sEventFlag = 0;   //event flag 초기화
}

void Kernel_event_flag_set(KernelEventFlag_t event)
{
    sEventFlag |= (uint32_t)event;    //event flag를 1로 setting 한다.
}

void Kernel_event_flag_clear(KernelEventFlag_t event)
{
    sEventFlag &= ~((uint32_t)event); //event flag를 0으로 clear 한다.
}

bool Kernel_event_flag_check(KernelEventFlag_t event)
{
    if (sEventFlag & (uint32_t)event)  //event를 Check하여 값이 있으면
    {
        Kernel_event_flag_clear(event);  //event를 clear 한 후
        return true;                     //true return한다.
    }
    return false; //event가 없으므로 false를 return한다.
}

sEventFlag는 32bit로 최대 32개의 flag를 사용 할 수 있다.

 

Kernel_send_events

void Kernel_send_events(uint32_t event_list)
{
    for (uint32_t i = 0; i < 32; i++)
    {
        if((event_list >> i) & 1) // i번째 event flag가 있으면(모든 event를 검사한다)
        {
            KernelEventFlag_t sending_event = KernelEventFlag_Empty; //event 구조체 초기화
            sending_event = (KernelEventFlag_t)SET_BIT(sending_event,i); // i번째 비트 1로 setting
            Kernel_event_flag_set(sending_event);  //event flag를 1로 setting.
            
        }
    }
}

for문을 통해 event가 있는지 확인 후 event가 있으면 event_flag 변수에 1로 setting한다.

 

-> event flag를 1로 set 한다.

 

 

Kernel_wait_events

KernelEventFlag_t 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;
}

for문을 통해 보고자 하는 event가 있는지 확인한다.

 

만약 event flag가 set 되어 있으면  Kernel_event_flag_check 함수를 통해 해당 비트를 clear 하고

 

set 되어 있는 bit를 return 한다.

 

-> event flag를 검사하여 1로 되어있으면 clear 후 해당 이벤트 코드를 실행한다.

 

 

인터럽트와 이벤트 연결

위 그림과 같이 인터럽트와 event를 연결 할 것이다.

 

키보드가 입력 할시 QEMU는 계속 interrupt가 발생하며 입력 받은 키가 만약 'x' 일 경우 0번째 event flag를 1로 

set 한다.

 

/* 실제 코드 */

interrupt_handler

static void interrupt_handler(void)
{
    uint8_t ch = Hal_uart_get_char();
    Hal_uart_put_char(ch);
    Hal_uart_put_char('\n');

    if(ch =='x')
    {
        Kernel_send_events(KernelEventFlag_UartIn); //0번째 비트를 1로 set 한다.
        
    }
}

 코드는 입력받은 키보드가 x일 경우 0번째 비트를 1로 set 한다.

 

 

 

실행 중인 Task 중에서 Task#0은 event flag 중에 0번 flag만 검사하여 만약 1로 set 되어 있으면

 

해당 flag를 0으로 clear 한 후 2번 flag를 1로 set 한다.

 

/* 실제 코드 */

User_task#0

void User_task0(void)
{
    uint32_t local = 0;

    while(true)
    {
        debug_printf("User Task #0\n", &local);
        KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn);
        switch(handle_event)
        {
            case KernelEventFlag_UartIn:
            debug_printf("Uart Event Handle \n");
            Kernel_send_events(KernelEventFlag_CmdOut);
            break;
        }
        
        delay(1000);
        Kernel_yield();
    }
}

task#0에서는 Kernel_wait_event 함수를 통해 0번째 비트가 1로 set 되어 있는지 check 한다.

 

만약 1로 set 되어 있다면 switch case 문을 통해 debug_printf 로 message를 보낸 다음

 

Kernel_send_events함수로 2번째 비트를 1로 set 한다.

 

 

마지막으로 Task#1이 Event flag 에서 2번 flag가 set 되어 있는지 확인 후 만약 1로 set 되어 있다면

0으로 Clear 후 해당 이벤트 코드를 실행한다.

 

/* 실제 코드 */

User_task#1

void User_task1(void)
{
      int32_t local = 0;

    while(true)
    {
        debug_printf("User Task #1\n", &local);
        KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_CmdOut);
        switch(handle_event)
        {
            case KernelEventFlag_CmdOut:
            debug_printf("Cmd Event Handle \n");
            break;
        }
        
        delay(1000);
        Kernel_yield();
    }

}

Task#1 에서는 wait event 함수를 호출하여 2번째 비트가 1인지 검사한다.(KernelEventFlag_Cmdout)

 

만약 1로 set 되어 있으면 0으로 clear 한 후 switch case문을 통해 이벤트 코드를 실행한다.

 

실제 실행 화면(Termeinal)