Bare metal programming of the Tiva C Series Launchpad

Supporting programs

You need to download and install a toolchain (compiler, linker etc.). I use this one:
You also need openocd version 0.7 or higher


ARM processors use memory mapped I/O. What this means is that all special registers can be accessed using the same address and data buses you would use to access variables you might use in a program. So, lets say there is a 32 bit register at address 0x12345678 that you need to write to. How can this be done in a language like C? Well the trick is to use pointers. In C, a pointer represents the address of something. Pointers point at data storage locations that have an associated data type. This is important as it allows you to move the pointer on the next element simply by adding '1' to it. The compiler figures out how far away the next data storage location is based on the type of data being pointed to. An example might help.

int K;
int L;
int *ptr;
ptr = &K; // ptr now points to space occupied by variable K
ptr = ptr +1; // ptr now points to space occupied by variable L, 4 byte away.

The code above shows a trivial example where two integers are declared along with a “pointer to integer” variable. The next statement places the address of K into ptr making it 'point at' K. You might thing that adding 1 to K migh simply move it on 1 byte in memory, but C doesn't work that way. Adding 1 to ptr makes it point at the next integer which is 4 bytes along (ARM uses 32 bit or 4 byte integers).
Now, how can we use this mechanism to write to a register at address 0x12345678? Well, it would be nice if we could do this:

int *ptr;

But, life and compilers is seldom this straightforward. If we do this we will likely get a bunch of warnings and, depending upon the compiler, some error messages. C-compilers complain if the datatype on the left side of the '=' sign is not equal to the datatype on the right. Instead we have to do something like this:

int *ptr;
ptr=(int *)0x12345678;

To write data to this memory location, we can now do the following:
*ptr = 0;
To read the contents of this memory location into a variable called D, we could do this:

Now, we could go and declare pointers to all of the ARM special function registers in this manner however there is a problem. The pointer takes up space, four bytes in fact. This would mean that we would be consuming the same amount of RAM as special function registers simply to allow our program reference them. This is clearly inefficient and may not even be possible on some processors with very limited RAM.

A more efficient way of doing this is a follows:

*((int *)0x12345678) = 0; // put zero into memory location 0x12345678

and to read from this location we can do this:

D = *((int *)0x12345678);

This is hardly a very intuitive way of programming and will inevitably lead to a number of errors as programmers mix up register addresses. The load on the programmer can be lessened by using the C pre-processor as follows:

// this is declared at the top of your source file or in an included header file.
#define THE_REGISTER (*((int *)0x12345678))
THE_REGISTER = 0; // put 0 into the register at 0x12345678
D = THE_REGISTER; // read the register contents into the variable D

Just prior to compiling, the C-preprocessor replaces all occurrences of THE_REGISTER with (*((int *)0x12345678)). All of the special function registers can be declared in this way without consuming additional RAM for pointers. It is usual practice these days to name the registers in the same way as they appear in the device's datasheet. If you look around on the web you will probably find a header file for your device from the manufacturer with all of this tedious work done for you. I spent a few hours with the TI-LM4F1205QR data sheet and user guide and came up with this one for the Tiva C (LM4F120) Launchpad board. You can download it here:.tilm4f120hqr.h

Sample programs

Blinky: Flashing the tri-colour LED
Using the systick timer to generate interrupts
Serial communications using the UART built-into the debug interface (works with Linux :)
From interrupts to objects: Using C++ instead of C
Simple PWM example that controls the brightness of the LED's
Using the FPU.This example makes use of the hardware FPU in the Tiva C series launchpad. This is a little tricky to set up as you have to be careful to use the correct gcc libraries that are compiled for Thumb-2, hardware FPU and the float-abi=hard calling convention (uses FPU registers to pass parameters). Get the code
Update 1 I noticed that constants in source code were causing illegal instruction interrupts. Adding the option -fsingle-precision-constant to the compiler flags fixes this. Makefile has been updated accordingly
Update 2 It turns out the reason for the processor exception I experienced was that the compiler generated code for the wrong version of the FPU. Adding this option -mfpu=fpv4-sp-d16 does the trick. In short this tells the compiler the FPU version number, that it is single precision (sp) and that its 32 single precision registers can be combined into 16 double precision ones (d16). Updated Makefile accordingly
Using the ADC. This example repeatedly converts AIN6 (PD1) and outputs the result in hex to the UART (9600 bps)
Multithreading on the Tiva C Launchpad (link to my blog)

Further reading

Other microcontroller examples are in Bare-metal ARM home