Baremetal programming of the Infineon XMC2Go

Introduction

The Infineon XMC2Go is a complete development kit not much larger than a matchstick. It costs around 5 Euro making it one of the cheapest kits on the market. In spite of this, it includes a 32MHz XMC1100 microcontroller (ARM Cortex M0 core) and a Segger J-Link debugging interface. It is powered and debugged over a micro USB interface which also provides a virtual COM port.

The XMC1100 microcontroller

The XMC1100 microcontroller can run at 32MHz. It includes 64kB of Flash program space and 16kB of RAM. On-chip peripherals include a 12-bit ADC, a Counter/timer for waveform generation, period measurement etc. Also included is a real-time clock subsystem, a pseudo-random number generator, a temperature sensor and a serial interface capable of working with I2C, SPI, UART, I2S and LIN buses. The XMC1100 includes a boot ROM which can perform some power-on system configuration. Further information can be obtained from the manufacturer data linked below.
XMC1100 Data sheet
XMC1100 Reference guide

The XMC2Go

The XMC2Go board can be interfaced to external devices via two rows of holes in the PCB . If you solder in some header pins you will be able to mount this device on a breadboard for prototyping. There are 16 pins available in total, two of which are used for power leaving the remaining 14 for I/O. Two on-board LED's are also present and controllable by user programs. Further information can obtained from the XMC2Go manual below.
XMC2Go user manual

Software development on the Infineon XMC2Go

Infineon provides an Eclipse based SDK called Dave for programming the kit. I am not a fan of Eclipse and prefer to work with a simple text editor and the command line. Furthermore, the Dave SDK is targetted at Windows users and is not of much use to Linux users like myself. I find the forest of directory trees created by large SDK's more than a little confusing and prefer to work off a single directory (at least for simple projects). For these reasons the work here is focussed on the text-editor/command line approach. This approach will work for both Linux and Windows (and maybe even Mac).
A cross compiler for various OS's is available from https://launchpad.net/gcc-arm-embedded
The J-Link debugging software tool can be obtained from http://www.segger.com/jlink-software.html
You will be asked for a serial number of your J-Link device when you try to download this. If you look further down the page you can see a link of the following form:
"I do not have a serial number because I own an eval board with J-Link on-board. How can I download J-Link software for it?" Click this link and follow the instructions.
Click this and follow the instructions.
Additional software is also required to complete the software development kit. You need a version of GNU make. This can installed in Linux setups using the package management system. In the case of Windows based setups you can install make by installing the MSys component from MinGW (www.mingw.org).
Apart from all of this you may have to fiddle with the PATH environment variable. Just make sure it includes the directory where the compiler, linker (and make) etc are installed (the bin directory in the compiler directory tree).

Example 1: Blinky

The LED's on the XMC2Go are connected to I/O Port 1 bits 0 and 1 (P1.0 and P1.1). This simple example writes to P1.0 and P1.1 so that they LED's flash on and off.

The code

#include "xmc1100.h"
void delay(unsigned len)
{
	while(len--);
}
void initPorts()
{
	// LED's are on P1.0 and P1.1
	// So make these push-pull outputs
	P1_IOCR0 = BIT15 + BIT7;
}
void main()
{
	initPorts();
	while(1)
	{
		P1_OUT = 0x01; // Turn on LED 0 (and LED 1 off)
		delay(50000);
		P1_OUT = 0x02; // Turn on LED 1 (and LED 0 off)
		delay(50000);
	};
}
When the main function is entered, the first task is to configure P1.0 and P1.1 as push-pull outputs. This is done in initPorts. A never ending loop is then entered and the LED's are controlled by writes to P1_OUT. A software delay function controls the flash speed. The symbol P1_OUT is defined in the header file xmc1100.h.
A few support files are needed to convert this C-code into a runnable program. The first of these is init.c. This file contains interrupt vectors that direct execution (eventually) to main each time the processor restarts. It also contains a function that initializes global variables prior to the running of main.
The next file needed is a linker script linker_script.ld. This file tells the GNU linker (ld) the size and location of RAM and Flash ROM. It also marks out certain segments of memory so that interrupt vectors end up in the correct location and it works with the init function so that global and static data can be initialized correctly.
As previously mentioned, the header file xmc1100.h defines special registers such as P1_OUT and was derived from the XMC1100 reference manual.
The last file needed to make it all work is the Makefile. This file controls GNU make (which in turn calls on the compiler and linker). The contents are as follows:
# In order to use this makefile you must have an arm compiler on your
# system. You can get one here: https://launchpad.net/gcc-arm-embedded
# Your PATH environment variable should include the 'bin' folder in
# the arm compiler directory tree.
# One final thing.  You must edit the LIBSPEC variable below to make
# it point at your arm compiler.
# A particular version of the compiler is mentioned in the LIBSPEC 
# line below.  Find out what the equivalent is for your system and change
# it accordingly.
# Tell the linker where to find the libraries -> important: use thumb versions
LIBSPEC=-L /usr/local/gcc-arm-none-eabi-4_8-2013q4/arm-none-eabi/lib/armv6-m

# Specify the compiler to use
CC=arm-none-eabi-gcc
# Specify the assembler to use
AS=arm-none-eabi-as
# Specity the linker to use
LD=arm-none-eabi-ld

CCFLAGS=-mcpu=cortex-m0 -mthumb -g

# List the object files involved in this project
OBJS=	init.o \
		main.o 

# The default 'target' (output) is main.elf and it depends on the object files being there.
# These object files are linked together to create main.elf
main.elf : $(OBJS)
	$(LD) $(OBJS) $(LIBSPEC) -T linker_script.ld -lc --cref -Map main.map -nostartfiles -o main.elf
	arm-none-eabi-objcopy -O binary main.elf main.bin
	objcopy -O ihex main.elf main.hex
	@echo "done"
	
# The object file main.o depends on main.c.  main.c is compiled to make main.o
main.o: main.c
	$(CC) -c $(CCFLAGS) main.c -o main.o


init.o: init.c
	$(CC) -c $(CCFLAGS) init.c -o init.o

# if someone types in 'make clean' then remove all object files and executables
# associated wit this project
clean: 
	rm $(OBJS) 
	rm main.elf 

Lines beginning with '#' are comments. This file should work with most setups so long as you change the LIBSPEC variable assignment so that it points at the directory in your computer where the GNU runtime libraries can be found. This little program doesn't use library functions but later ones will so you need to get this right. Look at your own gcc setup to figure out what to put here. The important thing is that it should point to the armv6-m version of the GNU libraries (look for a file caled libc.a). For further information about Makefiles see http://www.gnu.org/software/make/
To build the example, simply type make in the command line in the blinky directory.
The full set of files are available here blinky.tar.gz

Example 2: Systick

This example is a variation of blinky above except that it uses an interrupt service routine to toggle the LED's. This is big step-up from blinky as it involves changes to init.c and main.c
First of all, init.c directs execution to our interrupt handler when a SysTick interrupt happens. This requires a little assembler coding. The XMC1100 manufacturer ROM redirects interrupt vectors to RAM where (typically) user code uses a jump table to redirect execution to user Flash-ROM routines. The jump table needs to be coded in assembler because each entry must take up exactly 4 bytes. The jump table for this example is as follows:
inline void JumpTable(void) __attribute__(( section(".remapped_vectors")));
inline void JumpTable(void) 
{
	asm(" .long 0 "); // -15 reserved
	asm(" .long 0 "); // -14 reserved
	asm(" .long 0 "); // -13 HardFault
	asm(" .long 0 "); // -12 reserved
	asm(" .long 0 "); // -11 reserved
	asm(" .long 0 "); // -10 reserved
	asm(" .long 0 "); // -9 reserved
	asm(" .long 0 "); // -8 reserved
	asm(" .long 0 "); // -7 reserved
	asm(" .long 0 "); // -6 reserved
	asm(" .long 0 "); // -5 SVCall
	asm(" .long 0 "); // -4 reserved 
	asm(" .long 0 "); // -3 reserved 
	asm(" .long 0 "); // -2 PendSV
	
	asm(" ldr R0,=SysTick_Handler "); // -1 Systick handler
	asm(" mov PC,R0 ");

	asm(" .long 0 "); // IRQ 0
	asm(" .long 0 "); // IRQ 1
	asm(" .long 0 "); // IRQ 2
	asm(" .long 0 "); // IRQ 3
	asm(" .long 0 "); // IRQ 4
	asm(" .long 0 "); // IRQ 5
	asm(" .long 0 "); // IRQ 6
	asm(" .long 0 "); // IRQ 7
	asm(" .long 0 "); // IRQ 8
	asm(" .long 0 "); // IRQ 9
	asm(" .long 0 "); // IRQ 10
	asm(" .long 0 "); // IRQ 11
	asm(" .long 0 "); // IRQ 12
	asm(" .long 0 "); // IRQ 13
	asm(" .long 0 "); // IRQ 14
	asm(" .long 0 "); // IRQ 15
	asm(" .long 0 "); // IRQ 16
	asm(" .long 0 "); // IRQ 17
	asm(" .long 0 "); // IRQ 18
	asm(" .long 0 "); // IRQ 19
	asm(" .long 0 "); // IRQ 20
	asm(" .long 0 "); // IRQ 21
	asm(" .long 0 "); // IRQ 22
	asm(" .long 0 "); // IRQ 23
	asm(" .long 0 "); // IRQ 24
	asm(" .long 0 "); // IRQ 25
	asm(" .long 0 "); // IRQ 26
	asm(" .long 0 "); // IRQ 27
	asm(" .long 0 "); // IRQ 28
	asm(" .long 0 "); // IRQ 29
	asm(" .long 0 "); // IRQ 30
	asm(" .long 0 "); // IRQ 31
	
};
This jump table is a pseudo function that is mapped to a particular segment of memory (look at the linker script). The function (which is never called directly) contains a set of jump statements (or zeros for unused interrupts). Each interrupt has a jump statement at a particular location in the table. The entry for SystTick is as shown. When a SysTick interrupt happens, the statements
ldr R0,=SysTick_Handler
mov PC,R0
are executed causing the function Systick_Handler in main.c to be executed.
The main.c file is as follows:
#include "xmc1100.h"

void initPorts()
{
	// LED's are on P1.0 and P1.1
	// So make these push-pull outputs
	P1_IOCR0 = BIT15 + BIT7;
}
void SysTick_Handler(void)
{
	// Toggle LED's
	P1_OUT ^= 0x03;
}
void initSysTick()
{
		
	// Use processor clock, enable interrupt request and enable counter
	SYST_CSR |=(BIT0+BIT1+BIT2); 
	// Set the reload register (timebase in effect)
	// The default processor clock into the SysTick system is 8MHz
	// So, enter a reload value of 8000000-1 for a 1 second timebase
	SYST_RVR = 8000000 -1; // generate 1 second time base
	SYST_CVR=5; // Start the counter at a value close to zero
	enable_interrupts();
	P1_OUT = 1;
}
void main()
{
	initPorts();
	initSysTick();
	while(1)
	{
		
	};
}
It begins by setting up P1.0 and P1.1 as push-pull outputs. It then configures the SysTick system to generate an interrupt every second. The code to flash the LED's is contained in the SysTick_Handler function.
Full source code and support files are available in systick.tar.gz

Getting code on the chip

In order to program the XMC1100 you need to work with the JLink programming tool. There are two basic approaches here. One approach is to use the JLinkExe program directly. This is simple but does not include a debugger. The other approach is to use JLinkGDBServer (I prefer this). This program behaves in a way similar to openocd in that it provides a link between the GNU debugger (arm-none-eabi-gdb) and the hardware. A typical session works as follows:
Begin by plugging the XMC2Go into a USB port on your computer. Now run the JLinkGDBServer as follows
JLinkGDBServer -device XMC1100-0064 -if SWD -speed 4000
In a separate console window (don't close the first one) go to the directory where you compiled your code and enter the following:
arm-none-eabi-gdb A prompt will appear (gdb) and you should enter the following:
target remote :2331
This connects the GNU debugger to the JLinkGDBServer. Next enter the following:
monitor reset
monitor halt
load main.elf
This resets the XMC1100, halts it and then loads your program into flash. To run your code enter
monitor go
Hopefully LED's should now flash :)

Background reading


Manufacturer's Reference manual
ARM Information centre
Jonathan Valvano's website
Back to my ARM home directory