이벤트

- 인터럽트와 태스크간 연결 매체

- 버튼 클릭 -> 전기 신호 흐름 -> 컨트롤러가 인식 -> 소프트웨어 인터럽트 처리(IRQ, FIQ 발생) -> IRQ FIQ 핸들러가 인터럽트 확인 -> 해당 인터럽트 핸들러가 처리

- GUI의 경우 : 마우스 클릭(인터럽트 발생) -> 닷넷 or x윈도우가 이벤트로 만들어 핸들러 함수에 전달

 * RTOS의 경우 커널이 인터럽트를 이벤트로 바꿔 전달 이벤트 핸들러가 처리

 

 

이벤트 플래그

- 임의로 정한 갑하는 값

- 아래 이벤트 플래그를 16진수로 표현 시 0x8000 0002 -> 31번, 2번 이벤트 발생

 

이벤트 처리 구현

kernel/event.h

 

이벤트 플래그 선언. 유아트 입력, 커맨드 입력, 출력, 잠금해제 이벤트 플래그 존재하고

나머진 미정

#ifndef KERNEL_EVENT_H_
#define KERNEL_EVENT_H_

#include <stdbool.h>

typedef enum KernelEventFlag_t
{
    KernelEventFlag_UartIn      = 0x00000001,
    KernelEventFlag_CmdIn       = 0x00000002,
    KernelEventFlag_CmdOut      = 0x00000004,
    KernelEventFlag_Unlock      = 0x00000008,
    KernelEventFlag_Reserved04  = 0x00000010,
    KernelEventFlag_Reserved05  = 0x00000020,
    KernelEventFlag_Reserved06  = 0x00000040,
    KernelEventFlag_Reserved07  = 0x00000080,
    KernelEventFlag_Reserved08  = 0x00000100,
    KernelEventFlag_Reserved09  = 0x00000200,
    KernelEventFlag_Reserved10  = 0x00000400,
    KernelEventFlag_Reserved11  = 0x00000800,
    KernelEventFlag_Reserved12  = 0x00001000,
    KernelEventFlag_Reserved13  = 0x00002000,
    KernelEventFlag_Reserved14  = 0x00004000,
    KernelEventFlag_Reserved15  = 0x00008000,
    KernelEventFlag_Reserved16  = 0x00010000,
    KernelEventFlag_Reserved17  = 0x00020000,
    KernelEventFlag_Reserved18  = 0x00040000,
    KernelEventFlag_Reserved19  = 0x00080000,
    KernelEventFlag_Reserved20  = 0x00100000,
    KernelEventFlag_Reserved21  = 0x00200000,
    KernelEventFlag_Reserved22  = 0x00400000,
    KernelEventFlag_Reserved23  = 0x00800000,
    KernelEventFlag_Reserved24  = 0x01000000,
    KernelEventFlag_Reserved25  = 0x02000000,
    KernelEventFlag_Reserved26  = 0x04000000,
    KernelEventFlag_Reserved27  = 0x08000000,
    KernelEventFlag_Reserved28  = 0x10000000,
    KernelEventFlag_Reserved29  = 0x20000000,
    KernelEventFlag_Reserved30  = 0x40000000,
	KernelEventFlag_Reserved31  = 0x80000000,

    KernelEventFlag_Empty       = 0x00000000,
} KernelEventFlag_t;

void Kernel_event_flag_init(void);
void Kernel_event_flag_set(KernelEventFlag_t event);
void Kernel_event_flag_clear(KernelEventFlag_t event);
bool Kernel_event_flag_check(KernelEventFlag_t event);

#endif /* KERNEL_EVENT_H_ */

 

 

kernel/event.c

 

sEventflag  이벤트 플래그 32개 기록해서 보관 + 태스크에 전달하기 위한 변수

flag_clear 특정 플래그 반전 함수

flag_check 해당 이벤트가 1인지 체크. 1이면 호출한 함수에게 대기중임을 알려주고 플래그 클리어

#include <stdint.h>
#include <stdbool.h>

#include "stdio.h"
#include "event.h"

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

bool Kernel_event_flag_check(KernelEventFlag_t event)
{
	if (sEventFlag & (uint32_t) event)
	{
		Kernel_event_flag_clear(event);
		return true;
	}
	return false;
}

 

 

커널 API로 이벤트 처리하기위해 kernel/Kernel.c, .h에 함수 추가

#ifndef KERNEL_KERNEL_H_
#define KERNEL_KERNEL_H

#include "task.h"
#include "event.h"

void Kernel_start(void);
void Kernel_yield(void);

void Kernel_send_events(uint32_t event_list);
KernelEventFlag_t Kernel_wait_events(uint32_t waiting_list);

#endif

 

 

kernel/Kernel.c

 

Kernel_send_events

이벤트를 전달하기 위한 함수로 uint32_t로 이벤트들의 목록을 받아

  * 1100 0000 0000 0000, 0010 0000 0000 0000과 같이 이벤트가 하나 혹은 여러개가 발생한 경우를 매개변수로 받는듯

비트 연산자로 해당 플래그가 올라왔는지 확인

플래그가 올라온 이벤트가 있으면 uint32_t 이벤트 목록을 KernelEventFlag_t인 sending_event에다 담아서(SET_BIT) 

Kernel_event_flag_set으로 sEventFlag에 등록

* sEventFlag에 이벤트리스트를 등록함으로서 sending을 하나 어떻게 받아서 처리하는지는 don't care

 

 

Kernel_wait_events 

위와 비슷하게 대기할 이벤트 목록을 받아서

올라온 이벤트 플래그가 있으면 내리고, 내린 대기 이벤트 플래그 반환

waiting list에 올라온 플래그가 없으면 엠프티 반환

 

uint32_t 로 전달하는건 event1 | event2 .. 와같여 어러 이벤트를 or 연산해서 한번에 보내기 위함.

#include <stdint.h>
#include <stdbool.h>

#include "memio.h"

#include "Kernel.h"
#include "event.h"

void Kernel_start(void)
{
	Kernel_task_start();
}

void Kernel_yield(void)
{
	Kernel_task_scheduler();
}

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

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

 

 

 

인터럽트로 이벤트 발생시키기

QEMU에는 인터럽트가 별로 없으며, 타이머는 자주 발생해 Uart 만 사용

 

 

Uart 입력 인터럽트 발생 시 유아트 입력 이벤트 발생 시키기

interrupt_handler에서 Kernel_send_events에다가 enum인 UartIn을 전달하여 인터럽트 처리 후 커널에 이벤트 전달

#include <stdint.h>
#include "Uart.h"
#include "HalUart.h"
#include "HalInterrupt.h"

#include "Kernel.h"

extern volatile PL011_t* Uart;

static void interrupt_handler(void);

void Hal_uart_init(void)
{
	//enable UART
	Uart->uartcr.bits.UARTEN=0;
	Uart->uartcr.bits.TXE=1;
	Uart->uartcr.bits.RXE=1;
	Uart->uartcr.bits.UARTEN=1;

	Uart->uartimsc.bits.RXIM=1;

	Hal_interrupt_enable(UART_INTERRUPT0);
	Hal_interrupt_register_handler(interrupt_handler, UART_INTERRUPT0);
}

void Hal_uart_put_char(uint8_t ch)
{
	while(Uart->uartfr.bits.TXFF);
	Uart->uartdr.all = (ch & 0xFF);
}

uint8_t Hal_uart_get_char(void)
{
	uint32_t data;

	while(Uart->uartfr.bits.RXFE);

	data = Uart->uartdr.all;

	//check for and error flag
	if (data & 0xFFFFFF00)
	{
		//clear the error
		Uart->uartrsr.all = 0xFF;
		return 0;
	}

	return (uint8_t)(data & 0xff);
}

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

	Kernel_send_events(KernelEventFlag_UartIn);
}

 

 

 

boot/Main.c에

이벤트 플래그 초기화와

User Task0에 유아트 입력 이벤트 처리 코드 구현

 

Kernel_wait_event(KernelEventFlag_UartIn) 으로

태스크 0 동작때마다 유아트 입력이 들어왔는지 확인. 확인 후

sEventFlag에서 유아트인 플래그는 off되고 handle_event에 일단 유아트 이벤트 플래그 등록

스위칭을 통해 처리할 이벤트 핸들 이벤트가 유아트인 이면 이벤트 처리됨 문자열 출력

 

#include <stdint.h>
#include <stdbool.h>

#include "HalInterrupt.h"
#include "HalUart.h"
#include "HalTimer.h"

#include "task.h"
#include "Kernel.h"

#include "stdio.h"
#include "stdlib.h"

static void Hw_init(void);
static void Kernel_init(void);

static void	Printf_test(void);
static void Timer_test(void);

void User_task0(void);
void User_task1(void);
void User_task2(void);

void main(void)
{
	Hw_init();

	uint32_t i = 100;
	while(i--)
	{
		Hal_uart_put_char('N');
	}
	Hal_uart_put_char('\n');
	putstr("Hello World!\n");

	Printf_test();
	Timer_test();

	Kernel_init();
	
	while(true);
}

static void Hw_init(void)
{
	Hal_interrupt_init();
	Hal_uart_init();
	Hal_timer_init();
}

static void Printf_test(void)
{
	char* str = "printf pointer test";
	char* nullptr = 0;
	uint32_t i = 5;
	uint32_t* sysctrl0 = (uint32_t*)0x10001000;

	debug_printf("%s\b", "Hello printf");
	debug_printf("output string pointer: %s\n", str);
	debug_printf("%s is null pointer, %u number\n", nullptr, 10);
	debug_printf("%u=5\n", i);
	debug_printf("dec=%u hex=%x\n", 0xff, 0xff);
	debug_printf("print zero %u\n", 0);
	debug_printf("SYSCTRL0 %x\n", *sysctrl0);

}

static void Timer_test(void)
{
	for(uint32_t i = 0; i < 5 ; i++)
	{
		debug_printf("current count : %u\n", Hal_timer_get_1ms_counter());
		delay(1000);
	}
}

static void Kernel_init(void)
{
	uint32_t taskId;

	Kernel_task_init();
	Kernel_event_flag_init();

	taskId = Kernel_task_create(User_task0);
	if (NOT_ENOUGH_TASK_NUM == taskId)
	{
		putstr("Task0 creation fail\n");
	}

	taskId = Kernel_task_create(User_task1);
	if (NOT_ENOUGH_TASK_NUM == taskId)
	{
		putstr("Task1 creation fail\n");
	}

	taskId = Kernel_task_create(User_task2);
	if (NOT_ENOUGH_TASK_NUM == taskId)
	{
		putstr("Task2 creation fail\n");
	}

	Kernel_start();
}

void User_task0(void)
{
	uint32_t local = 0;
	
	debug_printf("User Task #0 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();
	}
}

void User_task1(void)
{
	uint32_t local = 0;

	while(true)
	{
		debug_printf("User Task #1 SP = 0x%x\n", &local);
		Kernel_yield();
	}
}

void User_task2(void)
{
	uint32_t local = 0;

	while(true)
	{
		debug_printf("User Task #2 SP = 0x%x\n", &local);
		Kernel_yield();
	}
}

 

딜레이를 안줬더니 너무 빨리 지나가서

위 코드의 각 태스크에 delay(100) 추가해서 돌리니

잘 나온다. User task #0은 루프 밖이라 한번 출력하고 마니 안나오는게 당연하고

 

 

+ Recent posts