Arm Cortex-M Serial I/O Example

The lm3s6965evb QEMU target has an emulated  serial port peripheral which works by writing out any serial data to the terminal window in which the QEMU instance is running.

From the lm3s6965evb user manual a serial port data register is mapped to the memory location 0x4000C000, character bytes written to this register will appear in the terminal window running QEMU.

Using the original introduction code, add the following file serial.c with the following contents:

#include <stdint.h>

#define UART0_R ((uint32_t *) 0x4000C000)

int putchar(int c) 
    *UART0_R = c;

int puts(unsigned char *str)
    int i = 0;

        while(str[i]) {

Change main.c as follows:

int main() 
    puts("Hello World!");

    return 0;    

Change the Makefile as follows:

OBJ := vector_table.o main.o serial.o
CFLAGS := -nostdlib -g -mcpu=cortex-m3 -mthumb
LDFLAGS := -g -o
ASMFLAGS := -g -o

TTY := $(shell tty)
MACH := lm3s6965evb
CDEV := tty,id=any,path=$(TTY)

QEMU_FLAGS := -gdb tcp::1234 -nographic -kernel

all: $(TARGET)

%.o: %.s
    $(CROSS)as $(ASMFLAGS) $@ $<

%.o: %.c
    $(CROSS)gcc $(CFLAGS) -c $<

    $(CROSS)ld $(LDFLAGS) $@ -T $(LDSCRIPT) $(OBJ)

    $(RM) *.o *.elf

run: $(TARGET)
    qemu-system-arm -machine $(MACH) $(QEMU_FLAGS) $< -chardev $(CDEV)    

debug: $(TARGET)
    $(CROSS)gdb -x gdbinit

Now use make run to verify the output, as follows:

make run
arm-none-eabi-as -g -o vector_table.o vector_table.s
arm-none-eabi-gcc -nostdlib -g -mcpu=cortex-m3 -mthumb -c main.c
arm-none-eabi-as -g -o serial.o serial.s
arm-none-eabi-ld -g -o startup.elf -T startup.ld vector_table.o main.o serial.o
qemu-system-arm -machine lm3s6965evb -gdb tcp::1234 -nographic -kernel startup.elf -chardev tty,id=any,path=/dev/pts/2    
Hello World!

This shows the example working correctly, however the code is very simplistic, a real life serial driver would be able to target different serial devices in the case of a board having more than one UART.