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를 추가했다.
'Firmware > RTOS' 카테고리의 다른 글
임베디드 OS 개발 프로젝트 8(Uart Interrupt) (0) | 2020.02.15 |
---|---|
임베디드 OS 개발 프로젝트 7(Uart Interrupt) (0) | 2020.02.15 |
임베디드 OS 개발 프로젝트 5 (0) | 2020.01.26 |
임베디드 OS 개발 프로젝트 A-1 (2) | 2020.01.25 |
임베디드 OS 개발 프로젝트 4 (0) | 2020.01.25 |