본문 바로가기

Firmware/RTOS

임베디드 OS 개발 프로젝트 6(UART 송수신)

main에서 uart를 쓰기 위한 tree는 다음과 같다.


├── boot
│   ├── Entry.S
│   └── Main.c
├── build
│   ├── Entry.os
│   ├── Main.o
│   ├── navilos.axf
│   ├── navilos.bin
│   ├── navilos.map
│   ├── Regs.o
│   ├── stdio.o
│   └── Uart.o
├── hal
│   ├── HalUart.h
│   └── rvpb
│       ├── Regs.c
│       ├── Uart.c
│       └── Uart.h
├── include
│   ├── ARMv7AR.h
│   └── MemoryMap.h
├── lib
│   ├── stdio.c
│   └── stdio.h
├── Makefile
└── navilos.ld

 

hal 파일과 lib 파일이 추가 되었다.

 

hal 파일에서는 UART를 쓰기 위한 코드가 들어가 있고 lib 는 UART 코드를 가지고 문자열로 입력 받기 위해 짠 코드이다.

 

hal 파일

HalUart.h

#ifndef HAL_HALUART_H_
#define HAL_HALUART_H_

void Hal_uart_init(void);
void Hal_uart_put_char(uint8_t ch);
uint8_t Hal_uart_get_char(void);

#endif

함수 선언 구문이다.

Hal_uart_put_char는 값을 출력,Hal_uart_get_char는 값을 입력받기 위해 사용하는 함수이다.

 

 

Regs.c

#include "stdint.h"
#include "Uart.h"

volatile PL011_t* Uart = (PL011_t*)UART_BASE_ADDRESS0;

Uart0 포트를 사용하겠다는 코드이다. UART_BASE_ADDRESS0 = 0x10009000으로 선언되어 있다.

자세한건 User Guide에 다 나온다.(https://developer.arm.com/docs/dui0303/latest/programmers-reference/uart)

 

Uart.c

#include "stdint.h"
#include "Uart.h"
#include "HalUart.h"

extern volatile PL011_t* Uart;

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

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;

    if(data&0xffffff00)
    {
        Uart->uartrsr.all = 0xff;
        return 0;
    }

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

 

Hal_uart_init: uart를 활성화한다. 특이한 점은 처음에 UARTEN 레지스터를 0으로 하여 비활성화 시킨 다음에 TXE,RXE 레지스터를 활성화 후 다시 EN레지스터를 1로 세팅한다. 처음부터 1로 SETTING 해도 차이는 없더라...

 

Hal_uart_put_char: 데이터 1byte를 출력한다.

 

Hal_uart_get_char: 데이터 1byte를 입력받는다. while문을 통해 입력 받을 때 까지 대기한다.

 

자세한 레지스터사용법은 http://infocenter.arm.com/help/topic/com.arm.doc.ddi0183f/DDI0183.pdf나와 있다.

 

 

stdio.c

#include "stdint.h"
#include "HalUart.h"
#include "stdio.h"

uint32_t putstr(const char* s)
{
	uint32_t c = 0;
	while(*s)
	{
		Hal_uart_put_char(*s++);

	}

	return c;
}


uint32_t getstr(void)
{
	uint32_t c = 0;
	uint8_t ch[100];
	while(1)
	{
		ch[c] = Hal_uart_get_char();
		Hal_uart_put_char(ch[c]);
		if(ch[c] !='\r')
		{
			c++;
		}
		else
		{
			ch[c] ='\n';
			Hal_uart_put_char(ch[c]);
			break;
		}
		
	
	}
	putstr("Receive: ");
	putstr(ch);

	return c;
}

putstr: while문을 이용해 1byte가 아닌 string으로 문자 출력하는 함수이다.

 

getstr: 책에는 없는데 문자를 1byte가 아닌 string으로 문자를 받기위해 만든 함수이다.

ch배열을 사용하여 문자를 받으면 Enter를 칠때 까지 최대 98byte를 받을 수 있다.

 

함수 알고리즘에 문제가 있는데 만약 98byte를 받을 때까지 '\r'(Enter)를 치지 않으면 프로그램이 먹통이 된다.

**작성하면서 알았는데 while(1)를 while(c<98) 로 수정하면 위 문제가 해결된다.** 

 

 

Main.c

#include "stdint.h"
#include "HalUart.h"

#include "stdio.h"

static void Hw_init(void);

int main(void)
{
    Hw_init();

    uint32_t length;
    putstr("Hello world!\n");
    putstr("send: ");
    length = getstr();


   return 0;
}

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

 프로그램을 동작 시키면 다음과 같이 출력된다.

 

Receive: 출력은 getstr 함수안에 putstr로 출력된다.

 

 

 

Makefile

ARCH = armv7-a

MCPU = cortex-a8

 

TARGET = rvpb

 

CC = arm-none-eabi-gcc

AS = arm-none-eabi-as

LD = arm-none-eabi-ld

OC = arm-none-eabi-objcopy

 

LINKER_SCRIPT = ./navilos.ld

MAP_FILE = build/navilos.map

 

ASM_SRCS = $(wildcard boot/*.S)

ASM_OBJS = $(patsubst boot/%.S, build/%.os, $(ASM_SRCS))

 

VPATH = boot \

hal/$(TARGET) \

lib

 

C_SRCS = $(notdir $(wildcard boot/*.c))

C_SRCS += $(notdir $(wildcard hal/$(TARGET)/*.c))

C_SRCS += $(notdir $(wildcard lib/*.c))

C_OBJS = $(patsubst %.c, build/%.o, $(C_SRCS))

 

INC_DIRS =-I include \

-I hal \

-I hal/$(TARGET) \

-I lib

 

CFLAGS = -c -g -std=c11

 

navilos = build/navilos.axf

navilos_bin = build/navilos.bin

 

.PHONY: all clean run debug gdb

 

all: $(navilos)

 

clean:

# 책에는 rm -fr 로 되어있는데 오타인거 같아 rm -rf로 수정함 #

@rm -rf build



run: $(navilos)

qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -nographic -S -gdb tcp::1234,ipv4

 

debug: $(navilos)

qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -S -gdb tcp::1234,ipv4

 

gdb:

gdb-multiarch

 

$(navilos): $(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)

$(LD) -n -T $(LINKER_SCRIPT) -o $(navilos) $(ASM_OBJS) $(C_OBJS) -Map=$(MAP_FILE)

$(OC) -O binary $(navilos) $(navilos_bin)

 

build/%.os: %.S

mkdir -p $(shell dirname $@) #mkdir -p build 실행 됨 #

$(CC) -march=$(ARCH) -mcpu=$(MCPU) $(INC_DIRS) $(CFLAGS) -o $@ $<

#$(AS) 에서 $(CC)로 수정 include "~~.h" 는 c언어 문법이므로#

 

build/%.o: %.c

mkdir -p $(shell dirname $@)

$(CC) -march=$(ARCH) -mcpu=$(MCPU) $(INC_DIRS) $(CFLAGS) -o $@ $<



hal과 lib 파일을 추가했다. 책과 다른 점은 run 부분인데 디버깅을 위해 -S -gdb tcp::1234,ipv4를 추가했다.