UART를 사용하기 위해서는, 우선 UART 하드웨어의 레지스터를 코드로 만들어야 한다
UART 레지스터는 아래에서 확인할 수 있다
UART에는 상당히 많은 레지스터가 있다
레지스터를 코드로 옮기는 방법은 아래 두가지가 있다
C언어 매크로를 사용
#define UART_BASE_ADDR 0x10009000
#define UARTDR_OFFSET 0x00
#define UARTDR_DATA (0)
#define UARTDR_FE (8)
...
uint32_t *uartdr = (uint32_t*)(UART_BASE_ADDR + UARTDR_OFFSET);
*uartdr = (data) << UARTDR_DATA
bool fe = (bool)((*uartdr >> UARTDR_FE) & 0x1);
if(fe)
{
// fe 오류 처리 코드
}
구조체를 이용
typedef union UARTDR_t
{
uint32_t all;
struct
{
uint32_t DATA:8;
uint32_t FE:1;
...
} bits;
} UARTDR_t;
typedef union UARTCR_t
{
uint32_t all;
struct
{
uint32_t UARTEN:1;
uint32_t SIREN:1;
...
} bits;
} UARTCR_t;
typedef struct PL011_t
{
UARTDR_t uartdr;
UARTCR_t uartcr;
} PL011_t;
PL011_t Uart = (PL011_t*)UART_BASE_ADDR;
Uart->uartdr.DATA = data & 0xFF;
if (Uart->uartdr.FE)
{
// fe 오류 처리 코드
}
hal/rvpb 디렉토리를 만들고 그 안에 Uart.h 파일을 만들어 PL011의 레지스터를 코드로 넣어보자
다음으로 UART 하드웨어를 제어할 수 있는 변수를 Regs.c 파일을 만들어 선언하자
#include "stdint.h"
#include "Uart.h"
volatile PL011_t* Uart = (PL011_t*)UART_BASE_ADDRESS0;
struct Flags {
unsigned int a : 1; // 1비트
unsigned int b : 3; // 3비트
unsigned int c : 7; // 7비트
};
# HalUart.h
#ifndef HAL_HALUART_H_
#define HAL_HALUART_H_
void Hal_uart_init(void);
void Hal_uart_put_char(uint8_t ch);
#endif /* HAL_HALUART_H_ */
#include "stdint.h"
#include "Uart.h"
#include "HalUart.h"
extern volatile PL011_t* Uart;
void Hal_uart_init(void)
{
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 하드웨어 출력 버퍼가 0이 될 때까지 기다림
Uart->uartdr.all = (ch & 0xFF); // 데이터 레지스터를 통해 알파벳 한 글자를 출력 버퍼로 보냄
}
#include "stdint.h"
#include "HalUart.h"
static void Hw_init(void)
{
Hal_uart_init();
}
void main(void)
{
Hw_init();
uint32_t i = 100;
while(i--)
{
Hal_uart_put_char('N');
}
}
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)
C_SRCS = $(notdir $(wildcard boot/*.c))
C_SRCS += $(notdir $(wildcard hal/$(TARGET)/*.c))
C_OBJS = $(patsubst %.c, build/%.o, $(C_SRCS))
INC_DIRS= -I include \
-I hal \
-I hal/$(TARGET)
CFLAGS = -c -g -std=c11
navilos= build/navilos.axf
navilos_bin = build.navilos.navilos_bin
.PHONY: all clean run debug gdb
all: $(navilos)
clean:
@rm -rf build
run: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -nographic
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 $@)
$(CC) -march=$(ARCH) -mcpu=$(MCPU) $(INC_DIRS) $(CFLAGS) -o $@ $<
build/%.o: %.c
mkdir -p $(shell dirname $@)
$(CC) -march=$(ARCH) -mcpu=$(MCPU) $(INC_DIRS) $(CFLAGS) -o $@ $<

#ifndef LIB_STDIO_H_
#define LIB_STDIO_H_
uint32_t putstr(const char* s);
#endif /* LIB_STDIO_H_ */
#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++);
c++;
}
return c;
}
#include "stdint.h"
#include "HalUart.h"
#include "stdio.h"
static void Hw_init(void)
{
Hal_uart_init();
}
void main(void)
{
Hw_init();
putstr("Hello world\n");
}
(...)
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
(...)
