본문 바로가기
프로그래밍/Keil RTX

[C/RL-ARM KEIL] event 처리 구현

by 채연2 2022. 12. 28.

 

 

 

 

CAN0_IRQHandler 로 들어온 응답을 __task 함수에서 처리하고 싶은데 마땅히 좋은 방법이 없어 찾다가 ARM Library에서 os_evt_set 이라는 함수를 발견했다. 냅다 갖다 써보기~!

 

 

예제에서 다뤄볼 RL-ARM 함수는 아래와 같다.

  • isr_evt_set
  • os_evt_set
  • os_evt_clr
  • os_evt_wait_and
  • os_evt_wait_or
  • os_evt_get

우선, 각 함수에 대해 알아보자

os_evt_set

#include <rtl.h>
void os_evt_set (
       U16    event_flags,    /* Bit pattern of event flags to set */
       OS_TID task );         /* The task that the events apply to */
  • U16 event_flags : 설정할 이벤트 플래그의 vbit pattern
  • OS_TID task : 이벤트가 적용될 task tid

즉, 내가 설정한 event_flags 를 task tid를 가진 task 함수에 전달하겠다 라는 뜻이다.


isr_evt_set

#include <rtl.h>

void isr_evt_set (
    U16    event_flags,    /* Bit pattern of event flags to set */
    OS_TID task );         /* The task that the events apply to */
  • U16 event_flags : 설정할 이벤트 플래그의 bit pattern
  • OS_TID task : 이벤트가 적용될 task tid

위의 os_evt_set 과 형식은 같다. 

 

하지만, 차이점이 뭐냐?

isr_evt_set 함수는 IRQ interrupt service routine 에서만 호출 가능하다는 점이다. FIQ interrupt service routine 에서는 호출 불가하다.


os_evt_wait_and

#include <rtl.h>

OS_RESULT os_evt_wait_and (
    U16 wait_flags,    /* Bit pattern of events to wait for */
    U16 timeout );     /* Length of time to wait for event */
  • U16 wait_flags : 대기할 이벤트 플래그의 bit pattern
  • U16 timeout : 이벤트 대기 시간

os_evt_wait_and 함수는 wait_flags에 지정된 모든 이벤트가 발생할 때까지 대기하는 함수이다. 최대 16개의 다른 이벤트를 기다릴 수 있다. 이벤트가 발생하지 않은 경우에도 함수가 반환해야 하는 시간을 지정하기 위해 timeout을 사용한다.

 

※ 반환 값

  • OS_R_EVT : wait_flags에서 지정한 모든 플래그가 설정됨
  • OS_R_TMO : 타임아웃

os_evt_wait_or

#include <rtl.h>

OS_RESULT os_evt_wait_or (
    U16 wait_flags,    /* Bit pattern of events to wait for */
    U16 timeout );     /* Length of time to wait for event */

 

  • U16 wait_flags : 대기할 이벤트 플래그의 bit pattern
  • U16 timeout : 이벤트 대기 시간

os_evt_wait_or 함수와 os_evt_wait_and 함수의 다른 점은 wait_flags에 지정된 이벤트 중 하나가 발생할 때까지 기다린다는 점이다. 그 외에는 os_evt_wait_and 함수와 같다.

 

※ 반환 값

  • OS_R_EVT : wait_flags에서 지정한 플래그 중 하나 이상이 설정됨
  • OS_R_TMO : 타임아웃

os_evt_clr

#include <rtl.h>

void os_evt_clr (
    U16    clear_flags,    /* Bit pattern of event flags to clear */
    OS_TID task );         /* The task that the events apply to */
  • U16 clear_flags : 지울 이벤트 플래그의 bit pattern
  • OS_TID task : 이벤트가 적용될 task tid

os_evt_clr 함수는 task에 대한 이벤트 플래그를 지우는 함수이다.


os_evt_get

#include <rtl.h>

U16 os_evt_get (void);

 

os_evt_get 함수는 os_evt_wait_or 함수를 사용할 때 어떤 이벤트 플래그를 받아 완료가 됐는지를 식별할 수 있게 해주는 함수이다. 반환 값으로 받은 이벤트 플래그 값을 얻을 수 있다.


간단한 예제 코드

#define EVENT_TEST1			0x0001
#define EVENT_TEST2			0x0002
#define EVENT_MASK			(EVENT_TEST1|EVENT_TEST2)

OS_TID g_tid;

void CAN0_IRQHandler(void)
{
	uint32_t u8IIDRstatus;
	u8IIDRstatus = CAN0->IIDR;
	if ((u8IIDRstatus >= CAN_NUM_MSG_MIN) && (u8IIDRstatus <= CAN_NUM_MSG_MAX)) {
		if (CAN_Receive(CAN0, u8IIDRstatus - 1, &g_sCANMsg0) == TRUE) {
			isr_evt_set(EVENT_TEST1, g_tid);
		}

		/* clear pending int */
		CAN_CLR_INT_PENDING_BIT(CAN0, u8IIDRstatus - 1);
	}
}

__task void task2(void) {
	uint8_t cnt = 0;
	while(1) {
		if (cnt % 10 == 0) {
			os_evt_set(EVENT_TEST2, g_tid);
		}
        
		os_dly_wait(1000 * TICK_FACTOR);
	}
}

__task void task1(void)
{
	uint16_t evt;
    
	while(1) {
		if (os_evt_wait_or(EVENT_MASK, 0x01) == OS_R_EVT) {
			evt = os_evt_get();
			switch(evt) {
				case EVENT_TEST1:
					printf("OCCURED EVENT_TEST1\n");
					break;
				case EVENT_TEST2:
					printf("OCCURED EVENT_TEST2\n");
					break;
			}
            
			os_evt_clr(EVENT_MASK, *g_tid);
		}
	}
}

__task void os_init(void)
{
	g_tid = os_tsk_create(task1, 1);
	os_tsk_create(task2, 1);
    
	os_tsk_delete_self();
}

int main(void)
{
	os_sys_init(os_init);
}

 

 

 

위 예제를 분석해보자.

#define EVENT_TEST1			0x0001
#define EVENT_TEST2			0x0002
#define EVENT_MASK			(EVENT_TEST1|EVENT_TEST2)

나는 이벤트 플래그를 다음과 같이 define으로 정의해줬다.

여기서 EVENT_MASK는 os_evt_wait_and 또는 os_evt_wait_or 함수의 wait_flags에 해당한다.

 

 

 

__task void os_init(void)
{
	g_tid = os_tsk_create(task1, 1);
	os_tsk_create(task2, 1);
    
	os_tsk_delete_self();
}

int main(void)
{
	os_sys_init(os_init);
}

os_init 함수에서 task1 태스킹 함수, task2 태스킹 함수를 생성해줬다.

task1 태스킹 함수는 이벤트를 받고 처리하는 함수, task2 함수는 이벤트를 던져주는 함수이다.

이벤트를 던져주기 위해서는 이벤트를 받을 대상이 필요하니, task1 함수를 생성하고 반환된 tid를 따로 저장해준다.

 

 

 

void CAN0_IRQHandler(void)
{
	uint32_t u8IIDRstatus;
	u8IIDRstatus = CAN0->IIDR;
	if ((u8IIDRstatus >= CAN_NUM_MSG_MIN) && (u8IIDRstatus <= CAN_NUM_MSG_MAX)) {
		if (CAN_Receive(CAN0, u8IIDRstatus - 1, &g_sCANMsg0) == TRUE) {
			isr_evt_set(EVENT_TEST1, g_tid);
		}

		/* clear pending int */
		CAN_CLR_INT_PENDING_BIT(CAN0, u8IIDRstatus - 1);
	}
}

위 함수는 CAN0으로 들어오는 인터럽트 신호를 받을 수 있는 핸들러 함수이다. 위 함수를 사용하기 위한 세부적인 설정 코드들은 생략한다.

IRQ 인터럽트 핸들러에서는 CAN0으로 통신이 들어오면 isr_evt_set 함수를 사용하여 g_tid의 tid를 가진 태스킹 함수에 EVENT_TEST1 이벤트를 던져준다.

 

 

 

__task void task2(void) {
	uint8_t cnt = 0;
	while(1) {
		if (cnt++ % 10 == 0) {
			os_evt_set(EVENT_TEST2, g_tid);
		}
        
		os_dly_wait(1000 * TICK_FACTOR);
	}
}

task2 태스킹 함수에서는 os_evt_set 함수를 사용하여 약 10초마다 g_tid의 tid를 가진 태스킹 함수에 EVENT_TEST2 이벤트를 던져준다.

 

 

 

__task void task1(void)
{
	uint16_t evt;
    
	while(1) {
		if (os_evt_wait_or(EVENT_MASK, 0x01) == OS_R_EVT) {
			evt = os_evt_get();
			switch(evt) {
				case EVENT_TEST1:
					printf("OCCURED EVENT_TEST1\n");
					break;
				case EVENT_TEST2:
					printf("OCCURED EVENT_TEST2\n");
					break;
			}
            
			os_evt_clr(EVENT_MASK, *g_tid);
		}
	}
}

task1 태스킹 함수에서는 이벤트를 받고 처리하는 함수이다.

os_evt_wait_or 함수를 사용하여 EVENT_MASK에 설정된 이벤트 플래그들 중 1개의 이벤트 플래그를 받으면 처리되도록 구현했다.

 

os_evt_get 함수로 어떤 이벤트 플래그를 받았는지 출력해주고, os_evt_clr 함수를 이용하여 받은 이벤트들을 즉시 지우도록 처리했다.

 


 

항상 전역변수, extern 변수들로 여기저기서 값을 처리하기에는 동기화 문제도 있고, 로직이 더 복잡해질 것이다. 이 이벤트 처리 함수를 이용하면 좀 더 편하고 간결하게 사용할 수 있어서 좋은 것 같다.!

 

 

 

 

 

 

320x100

댓글