본문 바로가기

Firmware/RTOS

임베디드 OS 개발 프로젝트 15(메시징)

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
│   ├── msg.c
│   ├── msg.h
│   ├── task.c
│   └── task.h
├── lib
│   ├── armcpu.c
│   ├── armcpu.h
│   ├── stdio.c
│   ├── stdio.h
│   ├── stdlib.c
│   └── stdlib.h
├── Makefile
└── navilos.ld

 

이전 장에서 인터럽트 핸들러에서 이벤트를 통해 Task에서 인터럽트 처리를 할 수 있었다.

 

그렇나 이벤트는 bit 검사를 통해 1이냐 0이냐를 판단 하기 때문에 더 많은 정보를 보낼 수 없다.

 

이번에는 메시징을 통해 좀 더 많은 데이터를 Task에 전달 해보고자 한다.

 

책에서 메시지 기능을 구현하기 위해 큐를 사용 했다.

 

큐는 FIFO 선입선출로 구현된 데이터 구조이다.(먼저 들어간 데이터는 먼저 나온다)

 

큐를 구현 할때는 데이터가 들어갈 위치를 알고 있는 변수(sMsgQ.rear)와 데이터가 나갈 위치를 알고 있는 변수(sMsgQ.front) 두 개가 필요하다.

 

1. 처음 Message Queue 초기화 하였을 때

 

 

2. 메시지 큐에 데이터가 들어 왔을 떄 (ex - hello)

 

hello 라는 데이터가 들어 왔으므로 MsgQ.Rear 변수가 증가함.

 

3. Task에서 들어온 데이터를 읽었을 때

 

Task에서 데이터를 읽었으므로 MsgQ.front가 증가하고 MsgQ.front 와 MsgQ.Rear변수 index는 같다.

1번 그림과 같은상황인데 이와 같이 MsgQ.front = MsgQ.Rear 일 경우 buffer가 완전히 비어있다.

 

msg.h

#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_Task3,
    KernelMsgQ_Task4,

    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(KernelMsgQ_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);

KernelCirQ_t sMsgQ[KernelMsgQ_Num];

 

메시지 큐는 KernelMsgQ_t 구조체를 통해 태스크당 한 개 씩 배정하였다.

 

KernelCirQ_t 구조체는 앞에 언급했던 front와 rear 변수이다. 마지막 멤버는 실제 데이터가 저장되는 메모리 공간을 잡고 있다. 배열의 크기는 MSG_Q_SIZE_BYTE 라는 이름으로 512Byte를 잡고 있다.

 

msg.c

 

Kernel_msgQ_init

void Kernel_msgQ_init(void)
{
    for (uint32_t i = 0 ; i < KernelMsgQ_Num ; i++)
    {
        sMsgQ[i].front = 0; //Queue에 데이터를 읽으면++
        sMsgQ[i].rear = 0;  //Queue에 데이터가 들어오면++
        memclr(sMsgQ[i].Queue, MSG_Q_SIZE_BYTE);
    }
}

task0~4까지 queue 인덱스 변수인 front. rear 값, queue 메모리 값을 0으로 초기화 한다. 

 

Kernel_msgQ_is_empty

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;
} /* Q가 비어있는지 확인하는 함수이다. 비어있으면 true */

앞서 3번 그림을 보고 코드를 보면 이해 하기 쉽다. 인덱스 front == rear 이면 메시지큐 버퍼가 완전히 비여 있다면 

true를 return 그외에는 false를 return 한다.

 

Kernel_msgQ_is_full

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) //Queue.rear 값이 Queue.front 값 바로 직전 index까지 커지면 full
    {
        return true;
    }

    return false;
}

메시지큐의 rear 인덱스 값이 front 변수 이전까지 오면 메시지큐의 버퍼가 full 이라는 뜻이다.

 

full 이면 true. full이 아니라면 false를 return 한다.

 

Kernel_msgQ_enqueue

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; //Queue size가 index byte를 넘지 않도록한다.

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

    return true;
}

메시지큐에 데이터를 넣는 함수이다.

넣기전에 메시지 큐가 full이면 false를 return 하고 그렇지 않으면

smsgQ.rear 값을 증가 시키고 이값을 index로 활용하여

sMsgQ.Queue[index] = data; 데이터를 집어 넣는다.

 

Kernel_msgQ_dequeue

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

메시지큐에서 데이터를 빼는 함수이다.

메시지 큐 자체에 데이터가 없으면 false를 return 하고 아니면

smsgQ.front를 증가 시키고 이 값을 index로 사용하여 

데이터를 뺀다.