1. Download and extract the Lite Edition of Code Sourcery's ARM EABI toolchain. This is a precompiled gcc toolchain targeted for ARM EABI, which can produce binaries for the LPC1768 on the mbed or any other Cortex-M3 microcontroller. It comes with the newlib implementation of libc.
2. Download and extract CMSIS v1.30. This has the header files characterizing the system and peripheral register addresses for the LPC1768 that's on-board the mbed, as well as the startup assembly bootstrap, linker script, etc–essentially the low-level code for setting up Cortex-M3's.
For the next couple of steps, I'll go over in detail the slight modifications I made to the LPC17xx CMSIS example project to compile it for the mbed, but you can just download the final archive in step 5.
3. Prepare a project folder to hold the source and header files.
There'll be a handful that you'll need out of CMSIS:
The first five files comprise what is typically the single LPC21xx or similar ARM7TDMI microcontroller's header file. The following is the startup assembly bootstrap. The next two files come out of an example project employing CMSIS, which we'll adapt for our purposes and future projects. The first of these two is the linker script, the second is a short LED flashing program. The very last file is a Makefile to compile these kinds of projects.
4. Make a few modifications to these files.
Linking with the libc library specified in GROUP(…) provides the _start routine which initializes libc before jumping to your main(). If you would like, you can integrate newlib and link with that to provide libc functionality. For the purpose of this guide, I'm omitting it, as it's not necessary. The cs3 libraries belong to CodeSourcery (probably the full commercial versions?) and also aren't necessary.
5. Alternatively, browse or download the archive here: http://dev.frozeneskimo.com/code/mbed-cmsis/mbed-cmsis.tar.gz
6. Add the toolchain binary folder to your path, and run make.
$ export PATH=$PATH:/path/to/arm-2009q3/bin $ make
7. If everything went well, you should have produced mbed_test.bin. Upload this to your mbed and run the program, LED1 should flash on and off indefinitely. A quick side note: I'm pretty sure that the LED isn't flashing at 100ms like the code specifies, this probably means that the configuration in system_LPC17xx.c isn't tuned exactly for the hardware setup of the mbed.
If you're interested in avoiding as much of CMSIS as you can, check out my barebones bootstrap assembly, linker script, and initialization template here: http://dev.frozeneskimo.com/code/mbed-cmsis/mbed-barebones It's not perfect, but you can easily tweak it to your needs. As a warning, I wrote it out of the LPC21xx/ARM7TDMI style, so it might not be fully up to date with some of the Cortex-M3 practices out there (like the interrupt vector table declared directly in C, see http://www.embedded.com/design/testissue/220900313). I'm still weighing the options of using CMSIS or not in my projects.
Following the guide in the previous section will setup a toolchain that can compile plain code for the mbed. However, to recompile mbed programs, you must link with the mbed library, which requires an implementation of libc. The CodeSourcery toolchain comes with newlib. This is a continuation of the mbed-cmsis project setup from the plain CMSIS how-to.
Here is the sample C++ program, main.cpp, I'm trying to compile. It's very similar to the default program the online compiler gives you:
/* Lack of __dso_handle definition during build, I have a feeling this is a bit hackish */
extern void *__dso_handle __attribute__((__weak__));
#define TARGET_LPC1768
#include "mbed.h"
DigitalOut myled(LED1);
int main() {
while(1) {
myled = 1;
wait(0.2);
myled = 0;
wait(0.2);
}
}
The modified Makefile:
# Project Name PROJECT=mbed_test # List of the objects files to be compiled/assembled OBJECTS=startup_LPC17xx.o core_cm3.o system_LPC17xx.o main.o LDOBJECTS=cmsis_nvic.o LSCRIPT=LPC17xx.ld OPTIMIZATION = 0 DEBUG = -g #LISTING = -ahls # Compiler Options GCFLAGS = -Wall -fno-common -mcpu=cortex-m3 -mthumb -O$(OPTIMIZATION) $(DEBUG) #GCFLAGS += -Wcast-align -Wcast-qual -Wimplicit -Wpointer-arith -Wswitch #GCFLAGS += -Wredundant-decls -Wreturn-type -Wshadow -Wunused LDFLAGS = -mcpu=cortex-m3 -mthumb -O$(OPTIMIZATION) -nodefaultlibs -nostartfiles -Wl,-Map=$(PROJECT).map -T$(LSCRIPT) ASFLAGS = $(LISTING) -mcpu=cortex-m3 # Compiler/Assembler/Linker Paths GCC = arm-none-eabi-gcc GPP = arm-none-eabi-g++ AS = arm-none-eabi-as LD = arm-none-eabi-ld OBJCOPY = arm-none-eabi-objcopy REMOVE = rm -f SIZE = arm-none-eabi-size ######################################################################### all:: $(PROJECT).hex $(PROJECT).bin $(PROJECT).bin: $(PROJECT).elf $(OBJCOPY) -O binary -j .text -j .data $(PROJECT).elf $(PROJECT).bin $(PROJECT).hex: $(PROJECT).elf $(OBJCOPY) -R .stack -O ihex $(PROJECT).elf $(PROJECT).hex $(PROJECT).elf: $(OBJECTS) $(GCC) $(LDFLAGS) $(LDOBJECTS) $(OBJECTS) -o $(PROJECT).elf stats: $(PROJECT).elf $(SIZE) $(PROJECT).elf clean: $(REMOVE) $(OBJECTS) $(REMOVE) $(PROJECT).hex $(REMOVE) $(PROJECT).elf $(REMOVE) $(PROJECT).map $(REMOVE) $(PROJECT).bin $(REMOVE) $(OBJECTS) ######################################################################### # Default rules to compile .c and .cpp file to .o # and assemble .s files to .o .c.o : $(GCC) $(GCFLAGS) -c $< .cpp.o : $(GPP) $(GCFLAGS) -c $< .S.o : $(AS) $(ASFLAGS) -o $(PROJECT)_crt.o $< > $(PROJECT)_crt.lst #########################################################################
Extract the mbed cmsis project from the previous how-to ~$ tar xfvz mbed-cmsis.tar.gz ~$ cd mbed-cmsis Replace main_lpc17xx.c with main.cpp above Replace the Makefile with the one posted above Do an svn checkout of the mbed library and header files ~/mbed-cmsis$ svn checkout --username anonymous http://mbed.org/projects/libraries/svn/mbed/trunk Copy over the header, library, and object files ~/mbed-cmsis$ cp trunk/* . ~/mbed-cmsis$ cp trunk/LPC1768/mbed.ar libmbed.a ~/mbed-cmsis$ cp trunk/LPC1768/capi.ar libcapi.a ~/mbed-cmsis$ cp trunk/LPC1768/*.o . Modify LPC17xx.ld and add GROUP(-lstdc++ -lc -lgcc -lnosys -lm -lcapi -lmbed) after OUTPUT_FORMAT(...) line Also add a PROVIDE(end = _end); below the last PROVIDE(...) line around line 40. This is for libnosys's sbrk routine. ~/mbed-cmsis$ make clean all arm-none-eabi-as -mcpu=cortex-m3 -o startup_LPC17xx.o startup_LPC17xx.s arm-none-eabi-gcc -Wall -fno-common -mcpu=cortex-m3 -mthumb -O0 -g -c core_cm3.c arm-none-eabi-gcc -Wall -fno-common -mcpu=cortex-m3 -mthumb -O0 -g -c system_LPC17xx.c arm-none-eabi-g++ -Wall -fno-common -mcpu=cortex-m3 -mthumb -O0 -g -c main.cpp arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O0 -nodefaultlibs -nostartfiles -Wl,-Map=mbed_test.map -TLPC17xx.ld cmsis_nvic.o startup_LPC17xx.o core_cm3.o system_LPC17xx.o main.o -o mbed_test.elf ./libmbed.a: could not read symbols: Bad value collect2: ld returned 1 exit status make: *** [mbed_test.elf] Error 1 ~/mbed-cmsis$ Ugly work-around, compile object files of mbed library directly in ~/mbed-cmsis$ mkdir mbed_extract ~/mbed-cmsis$ cp libmbed.a mbed_extract/ ~/mbed-cmsis$ cd mbed_extract ~/mbed-cmsis/mbed_extract$ ar x mbed_extract ~/mbed-cmsis/mbed_extract$ cp stdio*.o ../ ; cp Base*.o ../ ; cp Digtal*.o ../ ; cd ../ Add *.lpc1768.*.o to LDOBJECTS= in Makefile Remove -lmbed from GROUP(...) in LPC17xx.ld ~/mbed-cmsis$ make clean all rm -f startup_LPC17xx.o core_cm3.o system_LPC17xx.o main.o rm -f mbed_test.hex rm -f mbed_test.elf rm -f mbed_test.map rm -f mbed_test.bin rm -f startup_LPC17xx.o core_cm3.o system_LPC17xx.o main.o arm-none-eabi-as -mcpu=cortex-m3 -o startup_LPC17xx.o startup_LPC17xx.s arm-none-eabi-gcc -Wall -fno-common -mcpu=cortex-m3 -mthumb -O0 -g -c core_cm3.c arm-none-eabi-gcc -Wall -fno-common -mcpu=cortex-m3 -mthumb -O0 -g -c system_LPC17xx.c arm-none-eabi-g++ -Wall -fno-common -mcpu=cortex-m3 -mthumb -O0 -g -c main.cpp arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O0 -nodefaultlibs -nostartfiles -Wl,-Map=mbed_test.map -TLPC17xx.ld cmsis_nvic.o *.lpc1768.*.o startup_LPC17xx.o core_cm3.o system_LPC17xx.o main.o -o mbed_test.elf /home/../sandbox/tc/home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld: warning: core_cm3.o uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail /home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld: warning: system_LPC17xx.o uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail /home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld: warning: main.o uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail /home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld: warning: /home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/lib/thumb2/libstdc++.a(pure.o) uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail /home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld: warning: /home/../sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/lib/thumb2/libstdc++.a(class_type_info.o) uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail ... a ton of these ... /home/..sandbox/tc/arm-2009q3/bin/../lib/gcc/arm-none-eabi/4.4.1/../../../../arm-none-eabi/bin/ld: warning: ./libnosys.a(_exit.o) uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail arm-none-eabi-objcopy -R .stack -O ihex mbed_test.elf mbed_test.hex arm-none-eabi-objcopy -O binary -j .text mbed_test.elf mbed_test.bin ~/mbed-cmsis$ However, ~/mbed-cmsis$ du -h mbed_test.bin 96K mbed_test.bin Meh, no luck either. It seems like the entirety of the libraries are being linked in blindly. Just for kicks, I tried this out on the mbed with no success. I think that the mbed libraries were compiled with the Keil compiler suite, maybe that has something to do with all of this. I'm also thinking that the startup assembly should enter a newlib _start initialization routine after SystemInit but before main, but I'm not sure exactly where to find this (crt0.s/crt0.o?).
The mbed is a pretty neat development platform based on the LPC1768, part of NXP's new Cortex-M3 series. I suppose it's a kind of marketing project to promote the LPC1768, similar to STM's STM32 Primer2.
However, a somewhat common complaint of the mbed is the online compiler, a “modern cloud solution” to the problem of distributing (and maintaining) a free toolchain, IDE, and other extras to accompany the fairly inexpensive mbed hardware platform. The mbed project hasn't seemed to be particularly open about the details of what the online compiler is actually doing, though there is a svn repository that gives some clue of the underlying firmware glue.
To be honest, the thing I like about these arduino and arduino-“inspired” movements is the nifty development boards that come out of them, not so much the rapid development Wiring-esque language that accompany these platforms. Also, I felt like the mbed hardware at least deserved to be completely “unleashed” to C code I could optimize by hand and larger projects that are easier to manage on my computer rather than the online compiler. So I was interested in actually figuring out how to compile code directly for my mbed without the help of the “cloud”.
The LPC1768 folder of the svn repository trunk reveals several object files that belong to another more general project called CMSIS, or Cortex Microcontroller Software Interface Standard. CMSIS is an interesting project that I'm really having a hard time making up my mind on. I believe it's ARM's attempt at a kind of standardization or at least distribution of the startup code, linker script, and low-level initialization details which you were pretty much “on-your-own” with, on ARM7TDMI microcontrollers (not necessarily a bad thing though, right?).