Arduino Uno with a 32-bit ARM Cortex-M0 in 28 pin DIL package

Thank you for the explanation!

Mouser had 3907 chips LPC1114FN28/102 DIP on 15 Nov. 2012. Now, 4 days later they have 3899 in stock.
This microcontroller does not seem to have many clients.

It's an interesting chip, but the wide package and "only" 32k of flash make it less than compelling. A PIC32MX150F128 in narrow 28-dip with 128k of flash is more interesting, even if it's $2 more.

Sigh. It's really clear that NXP is serious about trying to displace 8bit CPUs. I wish they were more obviously "on target" with their attempts. 16pin micoBGAs, 28pin wide DIPs, ... nice tries, but not quite what I was hoping for. I can't quite tell whether they're clueless, or whether it really IS that hard to figure out what is really wanted in this nebulous "hobbyist/small manufacturer/mindshare" market space.

whether it really IS that hard to figure out what is really wanted in this nebulous "hobbyist/small manufacturer/mindshare" market space.

It is quite possible that nxp has studied the market and come to the conclusion that this super-duper red-hot "hobbyist" niche is not where they can make sufficient profit to float their boat. Instead, they want to focus instead on this much larger non-sexy industrial applications market for this chip.

Just try to a get a board with through-hole-parts made today and you will understand.

This microcontroller does not seem to have many clients.

It is probably true that large customer orders don't show up on their web inventory.

I have not studied it too much but the LPC Xpresso IDE, designed for ARMs produced by NXP, seems quite easy to work with.

The IDE hides the internal registers and you work mainly with easy to understand functions that set timers, IO ports, etc..
However, there may be traps, complications. LPC Xpresso IDE does not have a simulator, for instance, like Keil. You need the real hardware to test your code.

LPC Xpresso code example:

/****************************************************************************
 *   $Id:: PWM16_32test.c 3635 2010-06-02 00:31:46Z usb00423                $
 *   Project: NXP LPC11xx 16-bit/32-bit PWM example
 *
 *   Description:
 *     This file contains PWM test modules, main entry, to test PWM APIs.
 *
 ****************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * NXP Semiconductors assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. NXP Semiconductors
 * reserves the right to make changes in the software without
 * notification. NXP Semiconductors also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
****************************************************************************/

#include "driver_config.h"
#include "target_config.h"

#include "timer32.h"
#include "timer16.h"
#include "gpio.h"

extern volatile uint32_t timer32_0_counter;

volatile uint32_t period = 1000;  //48Khz PWM frequency

/* Main Program */

int main (void) {
	/* Initialize 32-bits timer 0 */
	init_timer32(0, TIME_INTERVAL);
	enable_timer32(0);
	
	/* Initialize the PWM in timer32_1 enabling match0 output */
	init_timer32PWM(1, period, MATCH0);
	setMatch_timer32PWM (1, 0, period/4);
	enable_timer32(1);

	/* Initialize the PWM in timer16_1 enabling match1 output */
	init_timer16PWM(1, period, MATCH1, 0);
	setMatch_timer16PWM (1, 1, period/8);
	enable_timer16(1);

	/* Enable AHB clock to the GPIO domain. */
	LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);
	
	/* Set port 2_0 to output */
	GPIOSetDir( 2, 0, 1 );
	
	while (1)                                /* Loop forever */
	{
		/* I/O configuration and LED setting pending. */
		if ( (timer32_0_counter > 0) && (timer32_0_counter <= 50) )
		{
			GPIOSetValue( 2, 0, 0 );
		}
		if ( (timer32_0_counter > 50) && (timer32_0_counter <= 100) )
		{
			GPIOSetValue( 2, 0, 1 );
		}
		else 
			while ( timer32_0_counter < 100 ); //wait for 
	  	timer32_0_counter = 0;
	}
}

A nice feature I like is that LPC Xpresso displays explanations about a function, ex. init_timer32 (the meaning of parameters, etc.) if you glide the mouse cursor over its name.

The IDE hides the internal registers

The ide does not hide anything: you can always code the mcu by writing to the registers.

However, in this case, they use a set of libraries from nxp to operate the peripherals. That's not unusual: ST has a set of its own libraries.

dhenry:
The ide does not hide anything: you can always code the mcu by writing to the registers.
However, in this case, they use a set of libraries from nxp to operate the peripherals. That's not unusual: ST has a set of its own libraries.

Without those libraries working with ARM peripherals is really not practical.

If indeed one have to write all these lines of code to initialize a timer I imagine that not many people would dare to attack ARMs.

/******************************************************************************
** Function name:		init_timer
**
** Descriptions:		Initialize timer, set timer interval, reset timer,
**						install timer interrupt handler
**
** parameters:			timer number and timer interval
** Returned value:		None
** 
******************************************************************************/
void init_timer32(uint8_t timer_num, uint32_t TimerInterval) 
{
  if ( timer_num == 0 )
  {
    /* Some of the I/O pins need to be carefully planned if
    you use below module because JTAG and TIMER CAP/MAT pins are muxed. */
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9);
    LPC_IOCON->PIO1_5 &= ~0x07;	/*  Timer0_32 I/O config */
    LPC_IOCON->PIO1_5 |= 0x02;	/* Timer0_32 CAP0 */
    LPC_IOCON->PIO1_6 &= ~0x07;
    LPC_IOCON->PIO1_6 |= 0x02;	/* Timer0_32 MAT0 */
    LPC_IOCON->PIO1_7 &= ~0x07;
    LPC_IOCON->PIO1_7 |= 0x02;	/* Timer0_32 MAT1 */
    LPC_IOCON->PIO0_1 &= ~0x07;	
    LPC_IOCON->PIO0_1 |= 0x02;	/* Timer0_32 MAT2 */
#ifdef __JTAG_DISABLED
    LPC_IOCON->JTAG_TDI_PIO0_11 &= ~0x07;	
    LPC_IOCON->JTAG_TDI_PIO0_11 |= 0x03;	/* Timer0_32 MAT3 */
#endif
#if CONFIG_TIMER32_DEFAULT_TIMER32_0_IRQHANDLER==1
    timer32_0_counter = 0;
    timer32_0_capture = 0;
#endif //TIMER32_0_DEFAULT_HANDLER
    LPC_TMR32B0->MR0 = TimerInterval;
#if TIMER_MATCH
	LPC_TMR32B0->EMR &= ~(0xFF<<4);
	LPC_TMR32B0->EMR |= ((0x3<<4)|(0x3<<6)|(0x3<<8)|(0x3<<10));	/* MR0/1/2/3 Toggle */
#else
	/* Capture 0 on rising edge, interrupt enable. */
	LPC_TMR32B0->CCR = (0x1<<0)|(0x1<<2);
#endif
    LPC_TMR32B0->MCR = 3;			/* Interrupt and Reset on MR0 */

#if CONFIG_TIMER32_DEFAULT_TIMER32_0_IRQHANDLER==1
    /* Enable the TIMER0 Interrupt */
    NVIC_EnableIRQ(TIMER_32_0_IRQn);
#endif
  }
  else if ( timer_num == 1 )
  {
    /* Some of the I/O pins need to be clearfully planned if
    you use below module because JTAG and TIMER CAP/MAT pins are muxed. */
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<10);
#ifdef __JTAG_DISABLED
    LPC_IOCON->JTAG_TMS_PIO1_0  &= ~0x07;	/*  Timer1_32 I/O config */
    LPC_IOCON->JTAG_TMS_PIO1_0  |= 0x03;	/* Timer1_32 CAP0 */
    LPC_IOCON->JTAG_TDO_PIO1_1  &= ~0x07;	
    LPC_IOCON->JTAG_TDO_PIO1_1  |= 0x03;	/* Timer1_32 MAT0 */
    LPC_IOCON->JTAG_nTRST_PIO1_2 &= ~0x07;
    LPC_IOCON->JTAG_nTRST_PIO1_2 |= 0x03;	/* Timer1_32 MAT1 */
    LPC_IOCON->ARM_SWDIO_PIO1_3  &= ~0x07;
    LPC_IOCON->ARM_SWDIO_PIO1_3  |= 0x03;	/* Timer1_32 MAT2 */
#endif
    LPC_IOCON->PIO1_4 &= ~0x07;
    LPC_IOCON->PIO1_4 |= 0x02;		/* Timer0_32 MAT3 */

#if CONFIG_TIMER32_DEFAULT_TIMER32_1_IRQHANDLER==1
    timer32_1_counter = 0;
    timer32_1_capture = 0;
#endif //TIMER32_1_DEFAULT_HANDLER

    LPC_TMR32B1->MR0 = TimerInterval;
#if TIMER_MATCH
	LPC_TMR32B1->EMR &= ~(0xFF<<4);
	LPC_TMR32B1->EMR |= ((0x3<<4)|(0x3<<6)|(0x3<<8)|(0x3<<10));	/* MR0/1/2 Toggle */
#else
	/* Capture 0 on rising edge, interrupt enable. */
	LPC_TMR32B1->CCR = (0x1<<0)|(0x1<<2);
#endif
    LPC_TMR32B1->MCR = 3;			/* Interrupt and Reset on MR0 */

#if CONFIG_TIMER32_DEFAULT_TIMER32_1_IRQHANDLER==1
    /* Enable the TIMER1 Interrupt */
    NVIC_EnableIRQ(TIMER_32_1_IRQn);
#endif
  }
  return;
}

Maybe not all the code above is necessary for a particular application. I do not know.

There is another problem, sometimes you have to change the parameters of a timer on the run as fast as possible between two events that are 10-20 clock cycles apart.
It seems like functions as "init_timer32" need more than 20 clk. ticks. Is there workarounds?

the LPC Xpresso IDE, designed for ARMs produced by NXP, seems quite easy to work with.

The LPC Xpresso IDE is fantastic to work with :slight_smile:

When I first downloaded it I had a simple test program running in minutes. It's not as turnkey as Arduino but close.

Here's a version of blink that runs on the LPC1227

#include "LARD.h"

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */
 
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 7;    // different pin on the LPC Xpresso board

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop(void) {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
}

Look familiar? :slight_smile:


Rob

Without those libraries working with ARM peripherals is really not practical.

If indeed one have to write all these lines of code to initialize a timer I imagine that not many people would dare to attack ARMs.

I guess I and folks I work with are one of those people, :slight_smile:

Once you build up your own blocks / functional modules by operating on registers, you have built up your own library: once you have written tmr1_set(1000);, you can call it next time to set a period of 2000 or 50199, without knowing much about how it did it, just that you know with confidence that it will do it right.

The beauty of modular programming.

Graynomad:
Personally I never use DIPs these days, but people still like them for prototyping.

Surprising. Does that mean you don't do any hand assembly yourself these days, or do you do all your hand assembly with SMD?

My experience is that life's just too short to hand assemble SMD. (But I've never claimed to be the quickest person with a soldering iron.)

Truth is I hate prototyping so I put my faith in data sheets and go straight to a PCB.

That said I don't actually make a lot these days but my current project is a dual processor board, I see no reason the design shouldn't be 99% correct as most hardware is quite simple these days. 99% is close enough and at $65 for 10 PCBs it's cheaper than the SMD breakout boards I'd have to buy as many of the components aren't available in PTH.

I'm not that experienced with soldering SMDs but with a hot-air station I haven't found it that hard so far.


Rob

possible that nxp has studied the market and come to the conclusion that this ... "hobbyist" niche is not where they can make sufficient profit to float their boat.

Oh, certainly. Just like most of the major chip vendors. But if they had decided to ignore it entirely, I don't think you'd see products like the 28-pin DIP, or even the more-recently announced 20-pin SOIC. They seem to be aiming at some non-traditional customers of SOME kind.

I think it's because "real engineers" of the mega-profit sort don't actually get to spend a lot of time "evaluating" new CPUs. You get handed a decision, or pick your old favorite, or base a decision on non-interesting criteria like raw speed, supplier reliability, or price. An professional engineer who is actually "evaluating" a chip on more artistic technical merits is closer to a hobbyist than most of them will care to admit (admittedly, of the techno-hobbyist type, rather than the less-technical arduino user. But it still sucks to struggle through that first "blink" attempt...)

I am seeing that MikroElektronika site has: C, Basic and Pascal compilers for ARM, PIC, PIC32, AVR, 8051.
see: http://www.mikroe.com/forum/
The question is, are these people serious? They seem to have all major compilers possible for all major microcontrollers on the market.

Example:
Code written for mikroBasic ARM compiler

' * Project name:
'     Pwm_Demo (Demo on using mikroE's PWM library functions)
' * Copyright:
'     (c) Mikroelektronika, 2011.
' * Revision History:
'     20110105:
'       - initial release;
' * Description:
'     This is a simple demonstration of PWM library, which is being used for
'     control of the ARM's PWM module. The module is initialized and started,
'     after which the PWM's Duty Ratios can be adjusted by means of 4 buttons
'     connected to pins PA0, PA1, PA2 and PA3. The changes can be monitored on the PWM
'     output pins (PB0 and PB2).
' * Test configuration:
'     MCU:             LM3S9B95
'                      http://www.ti.com/lit/ds/symlink/lm3s9b95.pdf
'     Dev.Board:       EasyMx v7 for STELLARIS(R) ARM(R)
'                      http://www.mikroe.com/eng/products/view/792/easymx-pro-v7-for-stellaris-arm/
'     Oscillator:      XT-PLL, 80.000MHz
'     Ext. Modules:    None.
'     SW:              mikroBasic PRO for ARM
'                      http://www.mikroe.com/eng/products/view/753/mikrobasic-pro-for-arm/
' * NOTES:
'   - Turn off all PORT LEDs except PORTB at SW15. (board specific)
'   - Pull-down PORTA at SW1, and put SW16.1 in VCC position (board specific)

program Pwm_Demo
dim current_duty, old_duty, current_duty1, old_duty1 as word
    pwm_period1, pwm_period2 as word

sub procedure InitMain()
  GPIO_Digital_Input (@GPIO_PORTA, _GPIO_PINMASK_ALL) ' configure PORTA pins as input
end sub

main:
  InitMain()
  current_duty  = 100                         ' initial value for current_duty
  current_duty1 = 100                         ' initial value for current_duty1

  pwm_period1 = PWM_CCP0_Init(5000, @_GPIO_MODULE_CCP0_B0)
  pwm_period2 = PWM_CCP3_Init(5000, @_GPIO_MODULE_CCP3_B2)

  PWM_CCP0_Start()
  PWM_CCP3_Start()

  PWM_CCP0_Set_Duty(current_duty,  _PWM_INVERTED_DISABLE)  ' Set current duty for PWM_CCP0
  PWM_CCP3_Set_Duty(current_duty1, _PWM_INVERTED_DISABLE)  ' Set current duty for PWM_CCP3

  while (TRUE)                               ' endless loop
    if GPIO_PORTA_DATA.B0 = 1 then           ' button on RA0 pressed
      Delay_ms(1)
      current_duty = current_duty + 5        ' increment current_duty
      if (current_duty > pwm_period1) then   ' if we increase current_duty greater then possible pwm_period1 value
        current_duty = 0                     ' reset current_duty value to zero
      end if
      PWM_CCP0_Set_Duty(current_duty,  _PWM_INVERTED_DISABLE) ' set newly acquired duty ratio
    end if

    if GPIO_PORTA_DATA.B1 = 1 then           ' button on RA1 pressed
      Delay_ms(1)
      current_duty = current_duty - 5        ' decrement current_duty
      if (current_duty > pwm_period1) then   ' if we decrease current_duty greater then possible pwm_period1 value (overflow)
        current_duty = pwm_period1           ' set current_duty to max possible value
      end if
      PWM_CCP0_Set_Duty(current_duty,  _PWM_INVERTED_DISABLE) ' set newly acquired duty ratio
    end if

    if GPIO_PORTA_DATA.B2 = 1 then           ' button on RA2 pressed
      Delay_ms(1)
      current_duty1 = current_duty1 + 5      ' increment current_duty1
      if (current_duty1 > pwm_period2) then  ' if we increase current_duty1 greater then possible pwm_period2 value
        current_duty1 = 0                    ' reset current_duty1 value to zero
      end if
      PWM_CCP3_Set_Duty(current_duty1, _PWM_INVERTED_DISABLE)       ' set newly acquired duty ratio
    end if

    if GPIO_PORTA_DATA.B3 = 1 then           ' button on RA3 pressed
      Delay_ms(1)
      current_duty1 = current_duty1 - 5      ' decrement current_duty1
      if (current_duty1 > pwm_period2) then  ' if we decrease current_duty1 greater then possible pwm_period1 value (overflow)
        current_duty1 = pwm_period2          ' set current_duty to max possible value
      end if
      PWM_CCP3_Set_Duty(current_duty1, _PWM_INVERTED_DISABLE)
    end if

    Delay_ms(1)                              ' slow down change pace a little
  wend
end.

westfw:
non-interesting criteria like raw speed, supplier reliability, or price.

Call me non-interesting, but I think supplier reliability and price actually tend to concentrate the mind over and above other more "artistic" features, at least when when a "real" product is involved. Particularly supplier reliability.

Graynomad:
Truth is I hate prototyping so I put my faith in data sheets and go straight to a PCB.

Fair enough, but PCBs don't assemble themselves!

Even with a hot-air station, are you putting all your boards together by hand, or are you sending them out to a service for assembly? I'm wondering at what volume this becomes economical these days...

They've been around for a long time. $249 for a C compiler is not cheap these days, but maybe it's worth it if you get a stack of working libraries for the hardware that port between platforms.

Are those function calls the same in the C version?

Does the same code work on an LPC if you just change the target or something simple?


Rob

are you putting all your boards together by hand,

I've only done a few in this life as an EE. Previously I worked for a company that had a production line and in those days there was no SMDs anyway. I had to load 1 maybe 2 prototype boards but after that I got the assembly people to do them.

In this life I'm retired and only do a few boards for myself every year or so. However my current project may get serious and require a lot of boards. I do have an associate in the US that is happy to load boards but I would think after 10 or 20 he'd rather get a life as well.

So if it gets that far I'll have to find an assembling company.

I'm wondering at what volume this becomes economical these days...

A friend of mine in NZ did a run of about 10 large boards with a lot of SMDs. Is was worth getting a local firm to load the SMDs for him and he finished off. I can't remember the prices though.

I have also in the past talked with both US and Chinese companies that sell boards. They are often prepared to make and sell them on a royalty basis. For that matter Sparkfun will do that. But this of course means that they have to buy into you product and you in turn lose a lot of control.

That's not necessarily a bad thing though, in fact to me that would be a good thing, I can think of nothing worse than making and testing 100 boards.


Rob

A different question,
Can a source code (including the libraries) written in LPC Xpresso be ported, preferably in an easy manner, to Keil uVision4 for ARM?
Reason: LPC Xpresso does not have a simulator while Keil has so the code can be debugged without using the real hardware.

Can a source code (including the libraries) written in LPC Xpresso be ported, preferably in an easy manner, to Keil uVision4 for ARM?

The answer is a little bit difficult.

An ide (uVision or EWARM or Code Red / eclipse) can have different compilers, particularly for an open source ide like eclipse: you can hang actually mdk or iar arm compilers now to eclipse.

IDE is simply an interface to integrate all little pieces together for better productivity.

Libraries are compiler-specific, as a result, not ide-aware.

So if the libraries in question are written for a compiler to be ported to, your code utilizing that library can be easily ported, short of your usual struggle with some syntax issues.

Now, most of the oem libraries on the arm are written for gcc, mdk and iar. The issue is likely cmsis: they may have been built on different versions of cmsis that your target platform may not support. In those cases, you can either sort it out or migrate with the host cmsis.