Channels ▼

Al Williams

Dr. Dobb's Bloggers

Red-Headed Stepchild

December 14, 2012

I don't know if everyone knows the expression "like a red-headed stepchild." Since I actually have two red headed stepchildren, I probably shouldn't use the phrase, which is a tricky way of saying something is not as favored as something else. I ported lwos (the lightweight OS I've been writing) to an NXP ARM chip and I couldn't help but think that Linux is the red-headed stepchild in this equation.

I really enjoy programming ARM chips, and NXP generally makes getting started painless when compared to some other ARM suppliers. But I can't help but think that it would have been easier if I were using Windows.

I picked up an NXP LPCXpresso board for the upcoming LPC812 chip. The LPCXpresso boards are nice. For a very reasonable price you get a long skinny board with an NXP processor and some small amount of I/O (this one has a 3-color LED and a potentiometer on board). You also get an LPC-Link USB debugging interface. The board is made to snap in two so you can use the debugging interface with your own target boards. Or, I suppose, you could detach the target and use it somewhere without the debugging interface.

The LPCXpresso software is a version of Code Red which is based on Eclipse. I have mixed feelings about Eclipse that I mentioned last time, but it is certainly feature-packed and well-supported. There is a "quick start panel" that allows you do certain common operations (see the figure below; more on that in a bit).

Getting started is fairly easy. You download the LPCXpresso software and then ask it to download the example code from the Internet. You'd think the "quick access panel" would be a shortcut for things you can do through the normal Eclipse interface. Apparently, though, some NXP-specific features are better handled from the quick panel. Trying to download the examples and use the standard Import command won't do what you need it to do unless you know how to manually set up the projects. The quick panel knows how to download from NXP and sets things up correctly.

This is important because the examples contain the CMSIS and driver libraries to interact with the chip. Of course, you don't actually need these, but it sure makes life easier if you want to interact with the chip's clock, I/O, and other features. This constitutes what amounts to a board support package for the LPCXpresso.

The prototypical example is Blinky, the NXP program that blinks whatever LEDs are on the board. I was surprised to find out I couldn't build Blinky! A little troubleshooting found that the example code named header files in uppercase (LPC8xx.h and LPC8xx_gpio.h). On Windows this would be a non-event but on Linux, LPC8xx.h and lpc8xx.h are two different files! Easy enough to fix, of course, but red-headed stepchild, indeed. I also found that the debug version of Blinky referred to the release version of CMSIS_CORE and that version was not built, so I had to fix that as well to get Blinky to work.

Once I had the kinks out of the toolchain, I didn't expect it to be very hard to port lwos over. In fact, I didn't need to make any changes to get it to compile. I did tell the compiler to not build the default LWOS main (using LWOS_NOMAIN) because the LPC812 needs some special setup for the timer.

However, there was one glitch. By default the quick start new project wizard let me set the CMSIS and driver library to use. It also included the "none" flavor of the Redlib C runtime library. This is an efficient library that is not totally up to the C standard. You can substitute the standard newlib, if you prefer, but that wasn't my problem. The real problem was the "none" flavor.

Both libraries come in three flavors: none, nohost, and semihost. The semihost library allows you to do file I/O through the debugging interface on your host computer. So, for example, printf will write to the debugging console. The nohost library allows you to make file I/O calls, but they don't do anything. The none library has no file I/O at all, which makes sense for a little embedded system like this. However, what I didn't realize was that malloc and its associated calls are only included with the nohost and semihost libraries. Luckily, it was easy to just flip the setting on the quick start panel.

I didn't want to wire up any extra hardware, so I decided to just make a few tasks that blink the multi-colored LED at different rates. The LED is connected to port 0's pins 7, 16, and 17. There is a multi-rate timer that the Blinky code appears to set up for 1mS, so I decided to use it as the basis of a 100mS LWOS tick timer in task 0:

extern uint32_t mrt_counter;
int task0(Task *tcb)
	if (mrt_counter>=100)

Although NXP has a nice user's guide for the chip features itself, I couldn't find anything directly about the driver library, so I had to go crack into the source code to find out about things like mrt_counter (that their Blinky example uses, but isn't in the driver's header files).

The two tasks are identical except for which output line they pulse and the length of time they sleep. Here's the first task:

int task1(Task *tcb)
	int *p=task_storage(int);
	int v=*p;
	GPIOSetBitValue( 0, 7, v );

Things worked perfectly at a casual glance. The problem arose when I put an oscilloscope on the LED pin. I would expect the task_sleep call to wait at least one second (10 times 100 mS). A little more wouldn't surprise me since LWOS is cooperative in nature, but I wouldn't expect less. But that's just what I got — consistently somewhere around 900mS.

Digging more into the NXP driver code, I discovered that although the timer offers to do millisecond delays for you, it is entirely dependent on you setting the right initial counter value. Blinky used 0x8000 and so did I. That number is wrong for the default clock, which both examples use.

More digging in the code showed a variable called SystemCoreClock. That told me the frequency was 36MHz. The Blinky code had a part of its initialization that routed the external clock out for debugging. I put that back in and a scope on pin 7 of the board showed a clock at 35.97MHz so that was close enough to agreeing with the variable that I believed it.

Blinky's timer number, 32768 or 0x8000 multiplied by the period time of 36 MHz (the reciprocal of 36,000,000) results in about 910uS — 90uS too low and that explained the result I was seeing on the scope. Instead of computing a new number, I elected to change the call to init_mrt to SystemCoreClock/1000 so that the program will adapt to a different clock speed. This gives me an LWOS tick at 100mS and the scope started showing what I expected (see figure below). You can find all of the code in the online listings.

Although I didn't have to make any changes to LWOS, I did anyway (in the git repo). It occurred to me that if I really wanted things to be lightweight, you might not appreciate me forcing you to link in malloc. So I added an LWOS_NOMALLOC define and an alternate way to define task table entries. Using TASK_DEF_MEM you can provide statically allocated memory for each task. Be aware, however, that if you turn off malloc you are responsible for filling in the task memory either with TASK_DEF_MEM or some other mechanism or you will get a null pointer back from the task_storage call.

Next time I'll talk more about the LPC812 and why I'm interested in it. I'm glad NXP offers Linux tools, but it would be nicer if I didn't have to clean up file name cases and things like that. I am also hoping I just missed the lpc800_driver_lib documentation. I don't mind reading the code to see how things work, but it would have been nicer to have clean documentation to explain all the calls in detail, maybe linked with the corresponding feature in the user's guide.

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags


<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags


<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

