이벤트의 한계

- 지난 글에서 UART 인터럽트 핸들러로 입력 이벤트를 보냈지만 어떤 문자인지는 보낼수가 없었음

 -> 데이터를 메시지로 보내면 된다.

-  비유 : 이벤트(뻴) - 메시지(사서함)

 

 

구현 목표

- 인터럽트 핸들러에서 이벤트 외에 데이터를 메시지로 보내고 이벤트 핸들러에서 메시지를 처리하자

 

 

메시지 설계하기

- 보통 메시지를 큐를 이용해서 구현 -> 메시지 큐

- 큐 구현 : 배열이나 동적 할당으로 링크드 리스트를 만들어 사용하나 임베디드 시스템에선 동적할당안쓰고 배열사용

- 배열로 큐 구현하려면? 데이터 들어갈 인덱스 변수와 꺼낼 데이터 인덱스 변수 필요(front, rear)

 

 

메시지 큐 만들기

 

kernel/msg.h

- 원형 큐 자료구조 KernelCirQ_t 에서 front는 꺼낼 데이터 인덱스, rear는 데이터 넣을 위치 인덱스, 배열 크기는 512

- 태스크가 3개여서 메시지 큐를 3개 사용. 각 메시지 큐를 지시하도록 KernelMsgQ_t 이넘 선언

 

#ifndef KERNEL_MSG_H_
#define KERNEL_MSG_H_

#define MSG_Q_SIZE_BYTE		512

typedef enum KernelMsgQ_t
{
	KernelMsgQ_Task0,
	KernelMsgQ_Task1,
	KErnelMsgQ_Task2,

	KernelMsgQ_Num
} KernelMsgQ_t;

typedef struct KernelCirQ_t
{
	uint32_t front;
	uint32_t rear;
	uint8_t Queue[MSG_Q_SIZE_BYTE];
} KernelCirQ_t;

void Kernel_msgQ_init(void);
bool Kernel_msgQ_is_empty(KernelMsqQ_t Qname);
bool Kernel_msgQ_is_full(KernelMsgQ_t Qname);
bool Kernel_msgQ_enqueue(KernelMsgQ_t Qname, uint8_t data);
bool Kernel_msgQ_dequeue(KernelMsgQ_t Qname, uint8_t* out_data);

#endif

 

kernel/msg.c

여기선 그렇게 어려운 내용은 없었다.

메시지 큐 3개 초기화 하는 함수나

비었는지, 꽉찻는지 확인하는 함수

유효성 검사후 데이터 입력하거나 빼는 함수

 

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

#include "msg.h"

KernelCirQ_t sMsgQ[KernelMsgQ_Num];

void Kernel_msgQ_init(void)
{
	for (uint32_t i = 0; i < KernelMsgQ_Num ; i++)
	{
		sMsgQ[i].front = 0;
		sMsgQ[i].rear = 0;
		memclr(sMsgQ[i].Queue, MSG_Q_SIZE_BYTE);
	}
}

bool Kernel_msgQ_is_empty(KernelMsgQ_t Qname)
{
	if (Qname >= KernelMsgQ_Num)
	{
		return false;
	}

	if (sMsgQ[Qname].front == sMsgq[Qname].rear)
	{
		return true;
	}

	return false;
}

bool Kernel_msgQ_is_full(KernelMsgQ_t Qname)
{
	if (Qname >= KernelMsgQ_Num)
	{
		return false;
	}

	if (((sMsgQ[Qname].rear + 1) % MSG_Q_SIZE_BYTE) == sMsgQ[Qname].front)
	{
		return true;
	}

	return false;
}

bool Kernel_msgQ_enqueue(KernelMsgQ_t Qname, uint8_t data)
{
	if (Qname >= KernelMsgQ_Num)
	{
		return false;
	}

	if (Kernel_msgQ_is_full(Qname))
	{
		return false;
	}

	sMsgQ[Qname].rear++;
	sMsgQ[Qname].rear %= MSG_Q_SIZE_BYTE;

	uint32_t idx = sMsgQ[Qname].rear;
	sMsgQ[Qname].Queue[idx] = data;

	return true;
}

bool Kernel_msgQ_dequeue(KernelMsgQ_t Qname, uint8_t* out_data)
{
	if (Qname >= KernelMsgQ_Num)
	{
		return false;
	}

	if (Kernel_msgQ_is_empty(Qname))
	{
		return false;
	}

	sMsgQ[Qname].front++;
	sMsgQ[Qname].front %= MSG_Q_SIZE_BYTE;

	uint32_t idx = sMsgQ[Qname].front;
	*out_data = sMsgQ[Qname].Queue[idx];

	return true;
}

 

메시지 송수신 API 구현하기

Kernel_send_msg

메시지큐와 데이터 포인터, 길이를 인자로 전달받아

길이만큼 루프돌리며 1바이트씩 enqueue 수행 

메시지큐가 꽉차 false 리턴받으면 deque시켜 비워준다.

 

 

kernel_recv_msg

메시지큐 이름과 저장할 위치 포인터, 길이 전달받아

메시지 디큐 함수로 1바이트씩 꺼내서 저장한다.

메시지큐가 비어있어 false를 반환받거나 정상적으로 수행되면 받은 길이 수를 반환한다.

 

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

bool Kernel_send_msg(Kernel_MsgQ_t Qname, void* data, uint32_t count)
{
	uint8_t* d = (uint8_t*)data;

	for (uint32_t i = 0; i < count ; i++)
	{
		if (false == Kernel_msgQ_enqueue(Qname, *d))
		{
			for (uint32_t j = 0; j < i; j++)
			{
				uint8_t rollback;
				Kernel_msgQ_dequeue(Qname, &rollback);
			}
			return false;
		}
		d++;
	}
	return true;
}

uint32_t Kernel_recv_msg(KernelMsgQ_t Qname, void* out_data, uint32_t count)
{
	uint8_t* d = (uint8_t*)out_data;

	for (uint32_t i = 0; i < count ; i++)
	{
		if (false == Kernel_msgQ_dequeue(Qname, d))
		{
			return i;
		}
		d++;
	}
	return count;
}

 

지금까지 한게

 

메시지 큐 설계, 기능 구현

메시지 송수신 커널 API 

 

이제 할건

 

유아트 입력 인터럽트 발생시 이벤트 송신과 함께 메시지 보내도록 구현하는데

잠깐 이벤트랑 보면

이벤트는 uint32_t sEventFlag를 이용해서 송수신을 했다면

메시지는 메시지 큐를 구현해서 송수신하는게 차이인거같다.

 

 

 

아무튼 유아트 입력 인터럽트시 메시지도 같이 보내도록 수정하면

* 주의 : 지난 이벤트 장에서 유아트 인이랑 CMDIN 이벤트를 같이 보내도록 했는데 그러면 유아트 입력떄마다 task1에서 리시빙 처리한다. 유아트 인 이벤트만 보내도록 뺴야한다.

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

#include "Kernel.h"
#include "msg.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);
	Kernel_send_msg(KernelMsqQ_Task0, &ch, 1);
    if (ch == 'x')
    {
    	Kernel_send_events(KernelEventFlag_CmdOut);
    }
}

 

 

 

 

 

빌드하는 중간에

오타나 수정하다보면

memclr를 정의하지 않아서 링킹이 안된다.

 

lib/stdlib.c , .h에 그냥 구현해주자.

#ifndef LIB_STDLIB_H_
#define LIB_STDLIB_h_

void delay(uint32_t ms);
void memclr(void* dst, uint32_t count);

#endif

 

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

void delay(uint32_t ms)
{
	uint32_t goal = Hal_timer_get_1ms_counter() + ms;

	while(goal != Hal_timer_get_1ms_counter());
}

void memclr(void* dst, uint32_t count)
{
	uint8_t *d = (uint8_t)dst;

	while(count--)
	{
		*d++ = 0;
	}
}

 

다 하고

 

 태스크0이  CmdIn 이벤트와함께 메시지를 보내는데

책에서 안전하게 보내는법이라 나와있는데로 구현하면 좀 이상하게 동작한다.

어쩔때는 잘동작하고 지금은 이상한데 뭐가 문젠지는 잘모르겠지만

 

원래대로 돌려서 쓰니 이젠 또 잘된다.

 

 

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

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

#include "task.h"
#include "Kernel.h"
#include "msg.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 < 2 ; 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);

	uint8_t		cmdBuf[16];
	uint32_t	cmdBufIdx = 0;
	uint8_t		uartch = 0;

	while(true)
	{
		KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn | KernelEventFlag_CmdOut);
		switch(handle_event)
		{
			case KernelEventFlag_UartIn:
				Kernel_recv_msg(KernelMsgQ_Task0, &uartch, 1);
				if (uartch == '\r')
				{
					cmdBuf[cmdBufIdx] = '\0';

					Kernel_send_msg(KernelMsgQ_Task1, &cmdBufIdx, 1);
					Kernel_send_msg(KernelMsgQ_Task1, cmdBuf, cmdBufIdx);
					Kernel_send_events(KernelEventFlag_CmdIn);
					/*
					while(true)
					{
						Kernel_send_events(KernelEventFlag_CmdIn);
						if (false == Kernel_send_msg(KernelMsgQ_Task1, &cmdBufIdx, 1))
						{
							Kernel_yield();
						}
						else if (false == Kernel_send_msg(KernelMsgQ_Task1, cmdBuf, cmdBufIdx))
						{
							uint8_t rollback;
							Kernel_recv_msg(KernelMsgQ_Task1, &rollback, 1);
							Kernel_yield();
						}
					}
					*/
					cmdBufIdx = 0;
				}
				else
				{
					cmdBuf[cmdBufIdx] = uartch;
					cmdBufIdx++;
					cmdBufIdx %= 16;
				}
				break;
			case KernelEventFlag_CmdOut:
				debug_printf("\nCmdOut Event by Task0\n");
				break;
		}
		Kernel_yield();
	}
}

void User_task1(void)
{
	uint32_t local = 0;

	debug_printf("User Task #1 SP = 0x%x\n", &local);

	uint8_t cmdlen = 0;
	uint8_t cmd[16] = {0};

	while(true)
	{
		KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_CmdIn);
		switch(handle_event)
		{
			case KernelEventFlag_CmdIn:
				memclr(cmd, 16);
				Kernel_recv_msg(KernelMsgQ_Task1, &cmdlen, 1);
				Kernel_recv_msg(KernelMsgQ_Task1, cmd, cmdlen);
				debug_printf("\nRecv Cmd : %s\n", cmd);
				break;
		}
		Kernel_yield();
	}
}

void User_task2(void)
{
	uint32_t local = 0;

	debug_printf("User Task #2 SP = 0x%x\n", &local);

	while(true)
	{
		Kernel_yield();
	}
}

 

 

 

처음에는 원래 코드도 안돌아갔는데

어째저째 고치다보니 지금 잘 동작하니 

여기서 너무 시간 지체해서 넘어가야겠다.

 

 

+ Recent posts