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