Bread Boarding the LPC1114 DIP28

I’ve been getting some questions about how I work with the LPC1114 DIP28 chip which will be used in the RC controller.  My approach is a little different (and might even constitute a hack) and thought I would take the time to share the setup.  First of all I’ll mention I’ve decided to focus on using the LPC1114 over something more mainstream (such at the 8bit Atmega chips used by the Arduino family) for the raw performance of a 32 bit processor and ease of migrating to even more powerful processors in the future.

The LPC1114 DIP28 serves as a great hacker/maker chip for electronic projects.  It’s cheap and easy to work with on the breadboard.  What’s even better are the development boards which are available from Embedded Artists which include an SWD (JTAG-like with reduced pin count) debugger.  This debugger allows you to flash the part, single step through your code, and examine memory.  These boards start as low as $20.  I know they now offer a stand alone debugger (called something like LPCLINK2) – however if you buy one of the standard boards you get the debugger plus a target board for the same price.

Hardware

In this tutorial we’ll run the PLL off the internal oscillator at 48Mhz.  It’s not as accurate as with the optional crystal, but for many applications this is good enough and save the crystal plus two additional caps.

In my case I’m starting with the LPC1769 board:

IMG_0648

Figure 1 – LPCLINK and LPC1769 Target Card

The first thing to do is separate the LPC-LINK from the target card (saving the target card for a future project).  It is a little tricky to cut them as there is no scoring on the PCB between the two.  To cut them I used my Dremel tool with my favorite cut-off disk:

IMG_1702

Figure 2 – Separating the LPCLINK from the target card

The actual cut preserves all of the holes on the LPCLINK side and cuts into the holds on the LPC1769 – it’s a sacrifice you have to make, but keeps the LPC1769 useable.  Save it for a later project.

For a cable to connect from the LPCLINK to a pin header which can be plugged into a breadboard I used a piece of leftover Ethernet cable.  The wiring of this goes as follows:

    • White/Green – 3.3V
    • Blue – SWDDATA
    • Green – SWDCLK
    • N/C
    • N/C
    • N/C
    • Black – GND
IMG_1703

Figure 3 – Wiring up the LPCLINK

I found that it was easier to wire the the cable to the backside of the LPCLINK:

IMG_1705

Figure 4 – Connecting the wires to the LPC-LINK

For the pin header connections are as follow:

    • Green – SWDCLK
    • N/C
    • N/C
    • N/C
    • Black – GND
    • White/Green – 3.3V
    • N/C
    • N/C
    • N/C
    • Blue – SWDDATA
IMG_1707

Figure 5 – Connecting the wires to the pin header

Before you put the DIP into the breadboard it is good to press the rows of pins inwards just a little bit – as you’ll need to save one of the rows of pins to fit the breadboard:

IMG_1710

Figure 6 – Close up of the DIP28

I got tired of constantly checking with the databook to see what the pin assignments were – so I made up these stickers to make it easier to remember:

IMG_1714

Figure 7 – With the sticker I made up to easily identify the pins

Seat the chip so that you have three rows of pin above and below the chip:

IMG_1713

Figure 8 – Mounting it on the breadboard

Here it is with the sticker attached:

IMG_1717

Figure 9 – Mounted on the breadboard

The wiring begins with connecting VDD with the AREF and the analog and digital ground supplies:

IMG_1719

Figure 10 – Connecting Power and Ground

Next add a decoupling cap and LED to pin 8:

IMG_1731

Figure 11 – Adding a decoupling cap and LED

Finally seat the pin header and you’re ready to start programming.

IMG_1732

Figure 12 – Connecting the SWD pin header – DONE!

Software

I’ve spent quite a bit of time evaluating Code Red LPCExpresso IDE as well as the other toolchains for programming the LPC1114.  Regarding LPCExpresso – while it does offer an Eclipse based IDE with embedded debugger for programming with a debugging with the LPCLINK there were a few of reasons that it wasn’t my first choice.

  1. It doesn’t support C++ with the free version
  2. To used the optimized library you need to link in the closed source REDLIB (in place of newLib)
  3. I never really liked the overall Eclispse GUI – it was too complicated with too many features

Instead my toolchain / setup consists of  the gcc toolkit which is officially supported by ARM by Joey Ye and his team.  This can be downloaded here:

https://launchpad.net/gcc-arm-embedded

This toolchain contains “newlib-nano” which is nicely code optimized as we’ll see with our upcoming code example.  They also offer ticket based support and are quite responsive when if you run into trouble.

In addition there are some drivers withing  LPCEXPRESSO which are needed to supporting flashing the LPC1114 as well as the gdb debugger.  This can be downloaded here:

http://lpcware.com/lpcxpresso/download

Install these two packages, but remember where you install the the two package as they will be referenced by the setup script:

#  Step 1:  Set path to GCC toolkit
TOOLKIT=~stevenparker/toolchains/gcc-arm-none-eabi-4_7-2013q2/bin 

#  Step 2:  Set path to Code Red Tools
CRT=/Applications/lpcxpresso_5.1.0_2048/lpcxpresso/bin

#  Add path to GCC
PATH=$TOOLKIT:$CRT:$PATH

#  Boot LPCLINK (Mac/Linux)
dfu-util -d 0x471:0xdf55 -c 0 -t 2048 -R -D $CRT/LPCXpressoWIN.enc

I got the file lpc1114.c from Wouter van Ooijen (www.voti.nl).  He has an excellent tutorial of what this is doing in terms of initializing local memory which can be found here http://www.embeddedrelated.com/showarticle/104.php.  My blinky example is quite a bit different that his, but this initialization code (and explanation) in this tutorial is really excellent..

//===========================================================================
// LPC1114 startup
// Wouter van Ooijen - wouter@voti.nl
// license: zlib
//===========================================================================

int main( void );
extern "C" { void __startup( void ); }
extern int __stack_end;    		

// nr 7 (offset 0x1C) is the vectors checksum
extern void (* const __vectors[ 40 ])(void) 
   __attribute__ ((section(".vectors"))) = {
      (void (*)(void)) & __stack_end,	
      __startup,
      0,          /* NMI Handler */
      0,          /* Hard Fault Handler */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* SVCall Handler */
      0,          /* Reserved */
      0,          /* Reserved */
      0,          /* PendSV Handler */
      0,          /* SysTick Handler */
      0,          /* IRQ0 */
      0,          /* IRQ1 */
      0,          /* IRQ2 */
      0,          /* IRQ3 */
      0,          /* IRQ4 */
      0,          /* IRQ5 */
      0,          /* IRQ6 */
      0,          /* IRQ7 */
      0,          /* IRQ8 */
      0,          /* IRQ9 */
      0,          /* IRQ10 */
      0,          /* IRQ11 */
      0,          /* IRQ12 */
      0,          /* IRQ13 */
      0,          /* IRQ14 - SPI1    */
      0,          /* IRQ15 - I2C     */
      0,          /* IRQ16 - CT16B0  */
      0,          /* IRQ17 - CT16B1  */
      0,          /* IRQ18 - CT32B0  */
      0,          /* IRQ19 - CT32B1  */
      0,          /* IRQ20 - SPI0    */
      0           /* IRQ21 - UART    */

};

void __startup(void){
   extern int __data_init_start;
   extern int __data_start;
   extern int __data_end;
   extern int __bss_start;
   extern int __bss_end;
   extern int __init_array_start;
   extern int __init_array_end;
   
	 int *s, *d, *e;

	 // clear .bss section 
	 d = & __bss_start;
	 e = & __bss_end;
	 while( d != e ){
	    *d++ = 0;
	 }
	 
	 // copy .data section from flash to ram
	 s = & __data_init_start;
	 d = & __data_start;
	 e = & __data_end;
	 while( d != e ){ 
	    *d++ = *s++;
	 } 
	 
	 // call the constructorts of global objects
	 s = & __init_array_start;
	 e = & __init_array_end;
	 while( s != e ){
	    (*((void(**)())s++))();
	 }
  
   // call main   
   (void) main(); 
   
   // when main returns: loop till hell freezes down
   while(1){}	
   
}

// called when a pure virtual method is called
extern "C" void __cxa_pure_virtual( void ){
   while(1){}	
}

The actual C code which is written to blink the LED is simplified in a way which doesn’t use any of the CORETEX libraries.  In this can you can take the value from the data book and simply plug it into the code:

#define  MMIO(addr) (*(volatile int*)addr)

//  SYSCON Registers
#define SYSAHBCLKCTRL  MMIO(0x40048080)

//  GPIO Registers
#define GPIO0DIR       MMIO(0x50008000)
#define GPIO0DATA      MMIO(0x50003FFC)

//  IOCON
#define IOCON_PIO0_8   MMIO(0x40044060)

int main()
{
  int i;

  //  Enable clocks on advanced high-performance bus
  //  SYSCLK on IRC - for simplicity
  SYSAHBCLKCTRL = 0x3FFFF;

  //  Configure PIO0_8 as General Purpose IO
  IOCON_PIO0_8 = 0x00;

  //  Configure PIO0_8 as output
  GPIO0DIR= 0x100;

  while(1) {
    GPIO0DATA = GPIO0DATA | 0x100;      // Turn on
    for(i=0;i<1000000;i++) asm("NOP");  // Pause
    GPIO0DATA = GPIO0DATA & ~(0x100);   // Turn on
    for(i=0;i<1000000;i++) asm("NOP");  // Pause
  }  
}

Finally to pull this all together in a way that really made sense to me I needed to get Freddie Chopin’s Makefile (although I added the debug and debugtui steps to interface with the LPCLINK):

#=============================================================================#
# ARM makefile
#
# author: Freddie Chopin, http://www.freddiechopin.info/
# last change: 2010-10-13
#
# this makefile is based strongly on many examples found in the network
#=============================================================================#

#=============================================================================#
# toolchain configuration
#=============================================================================#

TOOLCHAIN = arm-none-eabi-
CRTEMU =  crt_emu_lpc11_13_nxp

CXX = $(TOOLCHAIN)g++
CC = $(TOOLCHAIN)gcc
AS = $(TOOLCHAIN)gcc -x assembler-with-cpp
OBJCOPY = $(TOOLCHAIN)objcopy
OBJDUMP = $(TOOLCHAIN)objdump
SIZE = $(TOOLCHAIN)size
RM = rm -f

#=============================================================================#
# project configuration
#=============================================================================#

# project name
PROJECT = project

# core type
CORE = cortex-m0

# linker script
LD_SCRIPT = lpc1114.ld

# output folder (absolute or relative path, leave empty for in-tree compilation)
OUT_DIR = out

# C++ definitions (e.g. "-Dsymbol_with_value=0xDEAD -Dsymbol_without_value")
CXX_DEFS =

# C definitions
C_DEFS =

# ASM definitions
AS_DEFS =

# include directories (absolute or relative paths to additional folders with
# headers, current folder is always included)
INC_DIRS = 

# library directories (absolute or relative paths to additional folders with
# libraries)
LIB_DIRS =

# libraries (additional libraries for linking, e.g. "-lm -lsome_name" to link
# math library libm.a and libsome_name.a)
LIBS = -lc -lc -lnosys

# additional directories with source files (absolute or relative paths to
# folders with source files, current folder is always included)
SRCS_DIRS = 

# extension of C++ files
CXX_EXT = cpp

# wildcard for C++ source files (all files with CXX_EXT extension found in
# current folder and SRCS_DIRS folders will be compiled and linked)
CXX_SRCS = $(wildcard $(patsubst %, %/*.$(CXX_EXT), . $(SRCS_DIRS)))

# extension of C files
C_EXT = c

# wildcard for C source files (all files with C_EXT extension found in current
# folder and SRCS_DIRS folders will be compiled and linked)
C_SRCS = $(wildcard $(patsubst %, %/*.$(C_EXT), . $(SRCS_DIRS)))

# extension of ASM files
AS_EXT = S

# wildcard for ASM source files (all files with AS_EXT extension found in
# current folder and SRCS_DIRS folders will be compiled and linked)
AS_SRCS = $(wildcard $(patsubst %, %/*.$(AS_EXT), . $(SRCS_DIRS)))

# optimization flags ("-O0" - no optimization, "-O1" - optimize, "-O2" -
# optimize even more, "-Os" - optimize for size or "-O3" - optimize yet more) 
OPTIMIZATION = -Os

# warning options 
CXX_WARNINGS = # -Wall -Wextra
C_WARNINGS = # -Wall -Wstrict-prototypes -Wextra

# C++ language standard ("c++98", "gnu++98" - default, "c++0x", "gnu++0x")
CXX_STD = gnu++98

# C language standard ("c89" / "iso9899:1990", "iso9899:199409",
# "c99" / "iso9899:1999", "gnu89" - default, "gnu99")
C_STD = gnu89

#=============================================================================#
# set the VPATH according to SRCS_DIRS
#=============================================================================#

VPATH = $(SRCS_DIRS)

#=============================================================================#
# when using output folder, append trailing slash to its name
#=============================================================================#

ifeq ($(strip $(OUT_DIR)), )
    OUT_DIR_F =
else
	OUT_DIR_F = $(strip $(OUT_DIR))/
endif

#=============================================================================#
# various compilation flags
#=============================================================================#

# core flags
CORE_FLAGS = -mcpu=$(CORE) -mthumb

# listing flags
LISTING_FLAGS = -Wa,-ahlms=$(OUT_DIR_F)$(notdir $(<:.c=.lst))

# flags for C++ compiler
CXX_FLAGS = --specs=nano.specs -std=$(CXX_STD) -g -ggdb3 -fno-rtti -fno-exceptions -fverbose-asm $(LISTING_FLAGS)

# flags for C compiler
C_FLAGS = --specs=nano.specs -std=$(C_STD) -g -ggdb3 -fverbose-asm -Wa,-ahlms=$(OUT_DIR_F)$(notdir $(<:.$(C_EXT)=.lst))

# flags for assembler
AS_FLAGS = -g -ggdb3 -Wa,-amhls=$(OUT_DIR_F)$(notdir $(<:.$(AS_EXT)=.lst))

# flags for linker
LD_FLAGS = -T$(LD_SCRIPT) -O -g -Wl,-Map=$(OUT_DIR_F)$(PROJECT).map,--cref,--no-warn-mismatch

# remove unused code and data
LD_FLAGS += -Wl,--gc-sections
OPTIMIZATION += -ffunction-sections -fdata-sections

# do not use the standard startup
LD_FLAGS += 


#=============================================================================#
# do some formatting
#=============================================================================#

CXX_OBJS = $(addprefix $(OUT_DIR_F), $(notdir $(CXX_SRCS:.$(CXX_EXT)=.o)))
C_OBJS = $(addprefix $(OUT_DIR_F), $(notdir $(C_SRCS:.$(C_EXT)=.o)))
AS_OBJS = $(addprefix $(OUT_DIR_F), $(notdir $(AS_SRCS:.$(AS_EXT)=.o)))
OBJS = $(AS_OBJS) $(C_OBJS) $(CXX_OBJS) $(USER_OBJS)
DEPS = $(OBJS:.o=.d)
INC_DIRS_F = -I. $(patsubst %, -I%, $(INC_DIRS))
LIB_DIRS_F = $(patsubst %, -L%, $(LIB_DIRS))

ELF = $(OUT_DIR_F)$(PROJECT).elf
HEX = $(OUT_DIR_F)$(PROJECT).hex
BIN = $(OUT_DIR_F)$(PROJECT).bin
LSS = $(OUT_DIR_F)$(PROJECT).lss
DMP = $(OUT_DIR_F)$(PROJECT).dmp

# format final flags for tools, request dependancies for C++, C and asm
CXX_FLAGS_F = $(CORE_FLAGS) $(OPTIMIZATION) $(CXX_WARNINGS) $(CXX_FLAGS)  $(CXX_DEFS) -MD -MP -MF $(OUT_DIR_F)$(@F:.o=.d) $(INC_DIRS_F)
C_FLAGS_F = $(CORE_FLAGS) $(OPTIMIZATION) $(C_WARNINGS) $(C_FLAGS) $(C_DEFS) -MD -MP -MF $(OUT_DIR_F)$(@F:.o=.d) $(INC_DIRS_F)
AS_FLAGS_F = $(CORE_FLAGS) $(AS_FLAGS) $(AS_DEFS) -MD -MP -MF $(OUT_DIR_F)$(@F:.o=.d) $(INC_DIRS_F)
LD_FLAGS_F = $(CORE_FLAGS) $(LD_FLAGS) $(LIB_DIRS_F)

#contents of output directory
GENERATED = $(wildcard $(patsubst %, $(OUT_DIR_F)*.%, bin d dmp elf hex lss lst map o))

#=============================================================================#
# make all
#=============================================================================#

all : make_output_dir $(ELF) $(LSS) $(DMP) $(HEX) $(BIN) print_size 

# make object files dependent on Makefile
$(OBJS) : Makefile
# make .elf file dependent on linker script
$(ELF) : $(LD_SCRIPT)


#-----------------------------------------------------------------------------#
# linking - objects -> elf
#-----------------------------------------------------------------------------#

$(ELF) : $(OBJS)
	@echo 'Linking target: $(ELF)'
	$(CXX) $(LD_FLAGS_F) $(OBJS) $(LIBS) -o $@
	@echo ' '

#-----------------------------------------------------------------------------#
# compiling - C++ source -> objects
#-----------------------------------------------------------------------------#

$(OUT_DIR_F)%.o : %.$(CXX_EXT)
	@echo 'Compiling file: $<'
	$(CXX) -c $(CXX_FLAGS_F) $< -o $@
	@echo ' '

#-----------------------------------------------------------------------------#
# compiling - C source -> objects
#-----------------------------------------------------------------------------#

$(OUT_DIR_F)%.o : %.$(C_EXT)
	@echo 'Compiling file: $<'
	$(CXX) -c $(CXX_FLAGS_F) $< -o $@
	@echo ' '

#-----------------------------------------------------------------------------#
# assembling - ASM source -> objects
#-----------------------------------------------------------------------------#

$(OUT_DIR_F)%.o : %.$(AS_EXT)
	@echo 'Assembling file: $<'
	$(AS) -c $(AS_FLAGS_F) $< -o $@
	@echo ' '

#-----------------------------------------------------------------------------#
# memory images - elf -> hex, elf -> bin
#-----------------------------------------------------------------------------#

$(HEX) : $(ELF)
	@echo 'Creating IHEX image: $(HEX)'
	$(OBJCOPY) -O ihex $< $@
	@echo ' '

$(BIN) : $(ELF)
	@echo 'Creating binary image: $(BIN)'
	$(OBJCOPY) -O binary $< $@
	@echo ' '

#-----------------------------------------------------------------------------#
# memory dump - elf -> dmp
#-----------------------------------------------------------------------------#

$(DMP) : $(ELF)
	@echo 'Creating memory dump: $(DMP)'
	$(OBJDUMP) -x --syms $< > $@
	@echo ' '

#-----------------------------------------------------------------------------#
# extended listing - elf -> lss
#-----------------------------------------------------------------------------#

$(LSS) : $(ELF)
	@echo 'Creating extended listing: $(LSS)'
	$(OBJDUMP) -S $< > $@
	@echo ' '

#-----------------------------------------------------------------------------#
# print the size of the objects 
#-----------------------------------------------------------------------------#

print_size :
	@echo 'Size of modules:'
	$(SIZE) -B -t --common $(OBJS) $(USER_OBJS)
	@echo ' '

#-----------------------------------------------------------------------------#
# run
#-----------------------------------------------------------------------------#

run : all 
	$(CRTEMU) -2 -pLPC1114FN/102 -wire=winusb -flash-load-exec=$(ELF) -load-base=0x0000

debug: $(ELF)
	arm-none-eabi-gdb -x gdb_commands $(ELF)

debugtui: $(ELF)
	arm-none-eabi-gdbtui -x gdb_commands $(ELF)

#-----------------------------------------------------------------------------#
# create the desired output directory
#-----------------------------------------------------------------------------#

make_output_dir :
	$(shell mkdir $(OUT_DIR_F) 2>/dev/null)

#=============================================================================#
# make clean
#=============================================================================#

clean:
ifeq ($(strip $(OUT_DIR_F)), )
	@echo 'Removing all generated output files'
else
	@echo 'Removing all generated output files from output directory: $(OUT_DIR_F)'
endif
ifneq ($(strip $(GENERATED)), )
	$(RM) $(GENERATED)
else
	@echo 'Nothing to remove...'
endif

#=============================================================================#
# global exports
#=============================================================================#

.PHONY: all clean dependents

.SECONDARY:

# include dependancy files
-include $(DEPS)

Compiling and Running

Compiling the code is a breeze under Mac OSX, Linux, or mingw:

TerminalScreenSnapz002

 The code compiles to just 346 bytes!

Debugging

Running “make debug”

TerminalScreenSnapz003

This gets you into an interactive gdb session with the code we just wrote.  I’m not going to go into the details of gdb here, but it is a powerful command line debugger.

Well there you have it 32bit ARM development with a SWD debugger for about $25..  I hope this tutorial helped.  You can find all the source code at (check the newstyle branch):

https://github.com/scparker/minimum

Comments please.

 

This entry was posted in Uncategorized. Bookmark the permalink.

7 Responses to Bread Boarding the LPC1114 DIP28

  1. Pingback: Breadboarding with a ARM microcontroller

  2. Rob Gray says:

    As of a couple of weeks ago the free LPCXpresso allows C++ and (IIRC) 256k of code.

    Probably too late for you now but a nice change.

    Rob

  3. Ihor says:

    Good article! Thanks, and where did you get such nice mat?

  4. Scott says:

    For those of us who don’t have steady hands, a simpler way of using the LPC-Link side of the LPCXpresso board to the breadboard is to use a soldering iron to remove the jumpers between the two sides of the board, and put two single-row pin headers in the holes. A simple set of jumper cables (which every Arduino person has) can be used to connect the LPC-Link to the LPC1114 breadboard. To restore the connection between the two sides of the board simply put short blocks across the single-row headers that were soldered on.

    That way you don’t have to worry about any accidents cutting the board in half, and the board is still fully functional for the LPC1769 on the other half.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>