Aeolus Development

Newlib Port and Basic Microcontroller Support

ARMStick 100

ARMStick 101

ARMStick 102

In System Programming (ISP)

Documentation and Application Notes


Other Resources

Warranty and Returns


Product Feedback and Suggestions


Whats New

Privacy Policy


By Robert Adsett

Sunday, August 08, 2004

Note: This library of routine only provides the adaptation of newlib to the LPC2000. See the download page for pointer to the required newlib sources and build instructions.

* Marks items that are new or changed since release 2. Note that the chip support routines no longer use errno to record errors but simply return the appropriate error code.

Microcontroller Support

In addition to straight newlib functionality this port provides support for setting up and controlling aspects of the LPC2000. In addition it provides a timer that can be used to wait for a specific amount of time.

Register Support

lpc210x.h provides declarations of the peripheral registers in the 2104/5/6. lpc210x.ld is a linker script that provides the actual definitions.

lpc2119.h provides declarations of the peripheral registers in the 2119/2129/2194/2292/2294. lpc2119.ld is a linker script that provides the actual definitions.


Several enums are used to control the microcontroller. These enums are typedef'd to make their use in parameters clearer.

VPB_param -- used to set the ratio between the CPU clock and the VP bus clock. Possible values are:

  • VPB_DIV1 -- VPB clock = CPU clock
  • VPB_DIV2 -- VPB clock = CPU clock / 2
  • VPB_DIV4 -- VPB clock = CPU clock / 4

MAM_CONTROL -- used to control the memory access module. Possible values are:

  • MAM_disabled -- MAM disabled.
  • MAM_part_enable -- MAM partially enabled.
  • MAM_full_enable -- MAM fully enabled.


PROCESSOR_STATUS - Holds the processor status.  Not the current status but usually the value returned from DisableInterrupts. Should be treated as a magic cookie and not manipulated.

INT_MASK - A set of bits for masking the processors interrupt status.

INTERRUPT_SOURCE - Interrupt source, a number identifying the interrupt source.  Each variant header file (I.E. "lpc2119.h") will define its own set of values that are valid.

INT_PRIORITY - The priority to assign to a given interrupt source using the VIC



unsigned long ActualSpeed( void);

Returns the actual operating speed of the microcontroller. It takes into account the operation of the PLL. Relies on SetNativeSpeed being called earlier with the correct value.


PROCESSOR_STATUS DisableInterrupts( INT_MASK imask);

Disables IRQ/FIQ at the CPU level using the flags passed.  Returns the current state of the processor status register (I.E. before the disabling of interrupts).

  • INT_MASK imask (r0) -- Flag bits to be set in the processor status register.

INTERRUPT_DISABLE_MASK - A constant available from "lpc_sys.h" to pass to DisableInterrupts to turn off both IRQ and FIQ.


void RestoreInterrupts( PROCESSOR_STATUS stat);

Restores IRQ/FIQ using the processor status passed (presumably saved from a previous call to DisableInterrupts).

  • PROCESSOR_STATUS stat (r0)  -- Processor status register containing previous interrupt state to restore.     



unsigned long long GetUs( void);

Returns the microseconds since the timer was started.


unsigned int MinimumAchievableWait(void);

Returns the minimum number of microseconds that can be expected to be reasonably accurate in a WaitUs call. Values below this will not fail but may wait for a larger or smaller amount of time than requested. Indeed, the actual wait could well be identical for different values of requested wait time used below this.


int SetDesiredSpeed( unsigned long desired_speed);

Set the CPU to desired frequency. Relies on an earlier call to SetNativeSpeed to inform the timing routines of the correct oscillator speed. Returns 0 if successful an error otherwise.

  • unsigned long desired_speed -- CPU operating frequency in kHz.


int SetMAM( unsigned int cycle_time, MAM_CONTROL ctrl);

Set up the MAM. Minimal error checking, not much more than a wrapper around the register. Returns 0 if successful,   an error number otherwise.

  • unsigned int cycle_time -- number of cycles to access the flash.
  • MAM_CONTROL ctrl -- Mode to place MAM in. One of:
    • MAM_disabled
    • MAM_part_enable
    • MAM_full_enable


int SetNativeSpeed( unsigned long speed);

Set the oscillator frequency of the external oscillator. This is used to inform the routines that deal with CPU frequencies what the starting point is. Any error here will be multiplied later. Note: There is no way to determine or verify this value so we have to trust the caller to get it right. Returns 0 if successful, an error number otherwise.

  • unsigned long speed -- external oscillator/crystal frequency in kHz.


int StartClock( void);

Starts up the clock used for internal timing. Attempts to match the desired clock speed (CLOCK_SPEED) and initializes timing_scale_factor to a compensating scale. Returns 0 if successful, otherwise an error number. Note: Should be called only after all clocks have been set up. Otherwise time scale will not be correct.


int VICInit( void (*defadd)(void));

Initializes the VIC (Vectored Interrupt Controller). Does anything required for initial setup. In particular it ensures that the default vector is set up correctly and all interrupt sources are disabled. Returns 0 if successful, otherwise an error.

  • void (*defadd)(void) -- Address of the default interrupt service routine, must be non-zero.


int VICSetup( INTERRUPT_SOURCE interrupt_source, INT_PRIORITY priority, void (*service)(), unsigned int FIQ);

Sets up an interrupt source in the Vectored Interrupt Controller. Although it checks against some errors there is no  check against the interrupt source since the allowable range varies from variant to variant. Returns 0 if successful, otherwise an error.

  • INTERRUPT_SOURCE interrupt_source -- Source of the interrupt to setup. This is the interrupt number assigned to the peripheral in hardware.
  • INT_PRIORITY priority -- The priority of the interrupt if assigned to the IRQ. This also ends up being the index to its vector. Not used if the interrupt is assigned to the FIQ.  If the interrupt is assigned the lowest priority the service routine passed in (if not 0) will replace the current default vector routine.
  • void (*service)() -- A pointer to the service routine to use for this vector. Should be 0 for FIQ, may be 0 for non-vectored IRQs.
  • unsigned int FIQ -- Set to non-zero if interrupt will be serviced as an FIQ.


int VPBControl( VPB_param p);

Control the clock divider on the peripheral bus. Returns 0 if successful, otherwise an error number.

  • VPB_parm p -- requested VPB to CPU freq rate.


unsigned long VPBRate( void);

Finds and returns the rate of the clock on the peripheral bus (in Hz).


unsigned int UsToCounts( unsigned int us);

Converts to internal units in counts from uS. Other modules use this counter for a timebase so this needs to be available to them. Returns number of counts corresponding to us. Saturates on overflow so for large time periods it is possible to get a result lower than requested.

  • unsigned int us -- microseconds to convert to counts.


void WaitUs( unsigned int wait_time);

Wait for 'wait_time' us (microseconds) Will break wait into multiple waits if needed to avoid saturation.

  • unsigned int wait_time -- microseconds to convert to counts.

Device driver structure

Structure used to define a device driver.

struct device_table_entry {
   const char *name;  /* Device name.   */
        /* Device open method.*/
   int (*open)(struct _reent *r,const char *name, int flags, int mode);
        /* Device close method.*/
   int (*close)(struct _reent *r,int file);
        /* Device read method.*/
   _ssize_t (*read)(struct _reent *r,int file, void *ptr, size_t len);

        /* Device write method.*/
   _ssize_t (*write)(struct _reent *r,int file, const void *ptr, size_t len);
        /* Device ioctl method (controls device operating parameters. */
   int (*ioctl)(struct _reent *r, int file, unsigned long request, void *ptr);
The name of the device. Used by open to find the device to open.
Implements open for this device. The parameters are the same as those passed to open_r. open_r (and through it open) breaks down the name passed to it and uses the device portion of the name to search for a matching device. When a matching device is found the non-device portion of the name is passed to this function. For many devices this portion will be an empty string. For some devices this will be the name of a sub-device or file. It is the responsibility of the driver to check and parse this name. The format of the name passed to open should be of the form "device_name/device_specific_sub_dev_or_file". The portion up to the / is used to match against name. The / and everything after it is optional and the portion after the / will be passed on to the device driver. Some devices may require it, some may require that there be only the device_name. On success this routine should return a non-negative number that can be used by other calls to refer to the specific file or device opened (drivers that only implement a single device will typically return 0 on success). open combines the return value with the driver index to form an unique file number newlib uses to refer to the open device/file. On failure returns a negative number and sets errno to indicate the error source.
The inverse of open. Takes similar parameters to close_r. close_r first breaks out the driver index from the file number passed to it and passes the remainder to this function. This function should perform any cleanup needed. For some devices drivers this will be an empty operation.
Reads up to len bytes from an open file/device. Bytes are read into buffer pointed to by ptr. Returns the number of bytes read or a negative number in the case of an error (errno will be set). May return 0 or block if there are no bytes to read.
Writes up to len bytes to an open file/device. Bytes to be written are taken from the buffer pointed to by ptr. Returns a negative number and sets errno in the case of an error, otherwise returns the number of bytes actually written.
I/O control. See ioctl_r for a full description. Device specific control. Returns 0 if successful. Otherwise the return will be negative and errno will be set. This is the only entry that may be a null pointer. Set this to 0 if the device does not support ioctl calls, then any attempts to call ioctl for that device will result in an error return and errno will be set to ENOSYS.

List of devices. This list must be provided by the application program. Newlib adaptation uses this to determine what devices are available and how to control them. List is terminated by a null pointer entry. Note: Entries 0, 1, 2 are reserved to address stdin, stdout and stderr. These must be able to work from a standard open w/o needing further setup if they are to be transparent, alternatively they must be setup before any I/O is done. They must also not require the use of a sub-device/file to open successfully.

extern const struct device_table_entry *device_table[];

Included Device Drivers

Serial port driver for Uart 0. Set baud rate before using any I/O. Supports simple polled I/O. Use ioctl to set serial line characteristics.
Serial port driver for Uart 0. Set baud rate before using any I/O. Supports simple interrupt driven I/O. Use ioctl to set serial line characteristics.
System device. Originally held some control functionality. Now essentially a dummy that discards anything sent to it.

Error Codes Specific to this port

Can't perform requested operation.
Argument out of range.
Internal error encountered.

IO Control

Control over the characteristics of device is provided through a facility somewhat similar to that provided by Unix and Linux. Unlike that facility it takes a fixed number of arguments with the last argument being a pointer to an input/output structure that varies based on the call being made. The only action defined at the moment is for setting serial line characteristics.


int _ioctl_r( struct _reent *r, int fd, unsigned long request, void *ptr);

Support function. Provides a version with explicit re-entrancy variable. If you would normally use open_r directly then this is the appropriate call, otherwise the companion call ioctl is probably better suited. Device specific control. Returns 0 if successful, otherwise errno will be set to indicate the source of the error.

  • struct _reent *r -- re-entrancy structure, used by newlib to support multiple threads of operation.
  • int fd -- number referring to the open file. Generally obtained from a corresponding call to open.
  • unsigned long request -- Number to indicate the request being made of the driver.
  • void *ptr -- pointer to data that is either used by or set by the driver. Varies by request.


int ioctl( int fd, unsigned long request, void *ptr);

Support function. Device specific control. A shell to convert requests into a re-entrant form. Returns 0 if successful, otherwise errno will be set to indicate the source of the error.

  • int fd -- number referring to the open file. Generally obtained from a corresponding call to open.
  • unsigned long request -- Number to indicate the request being made of the driver.
  • void *ptr -- pointer to data that is either used by or set by the driver. Varies by request.

Request definitions. These define the requested actions.

Set up baud rate parity etc. Pass pointer to a serial_param structure that contains the information on how to set up the serial port.

structure to pass via ptr with ioctl request

struct serial_param {
    unsigned long baud;
    unsigned int length;
    unsigned int parity;
    unsigned int stop;

baud is the required baud rate in Hz. The driver will set the baud rate based on this value and before returning will set baud to the actual baud rate that the driver is set to.

Possible stop bit settings, assign to stop field of serial_param.

Provide 2 stop bits
Provide 1 stop bit

Possible parity values, assign to parity field of serial_param.

Set to parity None
Set to parity Odd
Set to parity Even
Set to parity stuck on
Set to parity stuck off

Possible word length values, assign to length field of serial_param.

5 bit serial byte.
6 bit serial byte.
7 bit serial byte.
8 bit serial byte.

Interrupt Control*

The assembly include file provides support for writing short interrupt shells in assembly. Two macros are provided, one (InterruptEntry) to setup the beginning of the interrupt service routine and one (InterruptExit) to exit from the ISR cleanly. Note that these macros do not provide the acknowledgement needed to finish a routine.



Assembly macro. Called at the beginning of an ISR it saves the working registers r0, r1, r2, r3, r12 and r14 and the saved processor status register. It does not re-enable any interrupts or change stacks so the stack on exit from the macro will be the stack appropriate to the exception. It is the responsibility of the user to ensure that the stack is large enough. The registers saved are sufficient that a call to a C function can be made safely after exit from the macro. If the user is writing code that makes use of other registers these additional registers must be preserved as is normal for the ARM procedure call standard. Use the companion macro InterruptExit at the end of the ISR.



Assembly macro. Called at the end of the ISR to restore the registers saved by InterruptEntry. It disables all interrupts before restoring the processor to the state it was in before the interrupt.

Example Interrupt Shell

An example of the use of the interrupt macros taken from "uart0_ishell.c".

The include of "" provides the macros for entry and exit.

Two external references are made. One to "VicVectAddrRead" is used to signal the end of the ISR to the VIC hardware. The other (to "Uart0Service") is the C routine that does most of the actual work responding to the interrupt.

Finally the shell itself ("Uart0InterruptShell") is made public so that it can be used to setup the VIC.

The shell is very straightforward.

  • The macro InterruptEntry starts the ISR, saving the critical registers.
  • A standard call is then made to Uart0Service to deal with the interrupt (this will be safe since any registers used in addition to the ones saved in the InterruptEntry macro will be saved by the standard C code).
  • A write to the VIC signal that the interrupt has been dealt with and frees it up for the next interrupt.
  • Finally the saved state from the entry is restored and the routine exits using the macro InterruptExit.
.include ""

	.extern	Uart0Service
	.extern	VICVectAddrRead
	.global Uart0InterruptShell


	bl 	Uart0Service		/*  Call routine that does the 	*/
					/* actual work			*/

	ldr	r0, =VICVectAddrRead	/*  Let VIC know we are done.	*/
	str	r0,[r0]


Linker Files and Startup*

The startup file "crt0.s" makes use of some information defined in the linker control file to set the environment for the compiled code. This allows for straightforward customization of the environment as needed while keeping a common startup for multiple projects.

The values most likely to be modified are the following:

The amount of ram available. This is used to determine where to place the various stacks. The stacks for the exceptions modes are placed at the top of memory, immediately below them is placed the main stack.
The size of the stack for FIQ exception mode. Make sure there is enough room here for all the stack space needed by the interrupt including room for any saved registers.
The size of the stack for IRQ exception mode. Make sure there is enough room here for all the stack space needed by the interrupt including room for any saved registers.
The size of the stack for supervisor mode.
The size of the stack for the abort exception mode. This can be quite small if the abort exception does something simple like loop infinitely.
The size of the stack for the undefined exception mode. This can be quite small if the undefined exception does something simple like loop infinitely.

The linker files have a number of exception defaults provided like the following:

PROVIDE( undefined_instruction_exception = endless_loop);
PROVIDE( software_interrupt_exception = endless_loop);
PROVIDE( interrupt_exception = endless_loop);

These can be overridden in the main code simple by providing a routine of the same public name. In addition they can be overridden in the startup code (interrupt_exception often is to make efficient use of the VIC).

The linker file also provides definitions for the variant specific registers, using sequences similar to:

/*  Provide address definitions for any peripheral registers */
/* used.       */

/* WD */
PROVIDE( WDMOD = 0xE0000000);

Again, if desired, these can be overridden in the main code. That is probably not useful though. The main purpose for this is to (with help from the variant's header file) provide a simple transparent way to use the peripheral registers on the micro.

Linking to the Library

If you use the -llibrary option to request the use of a library in linking remember that GNU expands that to liblibrary.a. So -lnewlib-lpc becomes a search for libnewlib-lpc.a.


  • Add a few more device drivers.
  • Expand documentation.