Pages: [1]   Go Down
Author Topic: LARD, Arduino-style abstraction library for the LPC1227 32-bit processor  (Read 1885 times)
0 Members and 1 Guest are viewing this topic.
nr Bundaberg, Australia
Online Online
Tesla Member
***
Karma: 126
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

As I've mentioned a few times I've recently been playing with LPC 32-bit processors, specifically the LPC1227.

I thought that a good way to learn the architecture would be to write something half-useful like a HAL, you already have CMSIS but that still needs a lot of bit-twiddling and in-depth knowledge of the hardware at the register level so LARD (Lpc ARDuino, yeah I know it's not great) was born.

I've only been on this for a few days but so far it looks promising. Here's an example of a "blink without delay" program

Code:
#include "LARD.h"
#define LED_PIN 13

void LEDoff (void);
void LEDon (void);

swTimer * led_timer;

void LEDon (void) {
  digitalWrite (LED_PIN, HIGH);
  swTimerAttachCallback (led_timer, LEDoff);
}

void LEDoff (void) {
  digitalWrite (LED_PIN, LOW);
  swTimerAttachCallback (led_timer, LEDon);
}

void setup (void) {
  pinMode(LED_PIN, OUTPUT);
  led_timer = swTimerCreate(1000, SWTIMER_TYPE_ASTABLE, LEDon);
  swTimerStart(led_timer);
}

void loop (void) {
}

Or a simpler version

Code:
#include "LARD.h"
#define LED_PIN 13

swTimer * led_timer;

void LEDfunc (void) {
  pinToggle (LED_PIN);
}

void setup (void) {
  pinMode(LED_PIN, OUTPUT);
  led_timer = swTimerCreate(1000, SWTIMER_TYPE_ASTABLE, LEDfunc);
  swTimerStart(led_timer);
}

void loop (void) {
}

I really got these chips for another project so this is a sideline, but it may be worth persevering to get it as compatible as possible.

There's more info and a list of the API functions here

lard.robgray.com

As always, any thoughts are appreciated.

EDIT: The LPC1227 is very similar to the Mega1284, 2 UARTs, 8k RAM, 4 timers, 48 or 64 pins etc, but with a 32-bit ARM core at 30/45MHz.

______
Rob
« Last Edit: July 15, 2012, 12:15:08 am by Graynomad » Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I like what you are doing here. "Arduino-like" is a useful goal in itself. It makes porting of libraries from the Arduino world to other architectures relatively straightforward, even if not quite trivial. Expecting the porting process to be *completely* free is probably misguided -- past a certain point, it becomes a case of rapidly diminishing returns very quickly.

Having said that, the case study of the Leaf Maple concentrates my mind a bit. It was also quite "Arduino-like", but never attracted the critical mass of support to get the most useful Arduino libraries ported across. And so it kind of languishes.

I think the question here then needs to become a bit more pointed. What aspects of "Arduino-like" are important, and what aspects less so? I think perhaps too much effort was directed in the Leaf Maple project to making their system "Arduino-like" in ways which turned out to be less critical than aspects which they put less resources into. For example, they went to great length porting the entire Arduino IDE to the Maple, but for intermediate to advanced users, that is probably a complete waste of effort. Unfortunately, the intermediate to advanced users are probabaly their natural target audience for a project of this kind. As yours would be too for this project, I would think.

So I think you are definitely putting the initial efforts into the right place here. If you can make an abstraction layer that significantly reduces the effort to port many of the Arduino 3rd party libs (which I strongly suspect is primarily what makes Arduino interesting to the intermediate to advanced developers), then you may attract the requisite critical mass.

I'd personally like to see more "Arduino-like" projects get off the ground to leverage all that valuable Arduino 3rd party library development onto other platforms.
 



Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

nr Bundaberg, Australia
Online Online
Tesla Member
***
Karma: 126
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks pico, I was beginning to think that nobody cared smiley-cry

Quote
What aspects of "Arduino-like" are important,
Good question, for the moment I'm concentrating on the standard things like reading/writing pins, UARTs etc as well as adding stuff I think people always use, for example yesterday I added input debouncing, this works in the background (or should do, I haven't hooked up any hardware yet smiley) for all 39 IO pins, just call debouncePinRead() to get the current state of the pin.

This should get around the constant dicking around with millis() to see how long the transition has been in place. I'm about to expand that to include a "pushButton" object the raises "onPressed/onReleased" style events so there's no messy "do something when a button is pressed and something else when it's released" code.

I also just added finite state machine support but it was so simple it hardly justifies having it built in so I'm thinking about that.

Quote
they went to great length porting the entire Arduino IDE to the Maple,
I suppose I see where they were coming from but the IDE is the last thing I'd want to port. Apart from the fact that I don't have the resources why on earth would you use it when you get the fantastic Eclipse IDE for free?

The IDE is good for it's intended audience but as you say they aren't the people who need to work with 32-bit processors.

Quote
If you can make an abstraction layer that significantly reduces the effort to port many of the Arduino 3rd party libs
That's the plan, I should grab some 3rd-party library source code and see how it goes. I still have a lot to do before that though. I suspect any such porting efforts by library authors will be aimed at the Due when it arrives and I don't plan to port every known library, but I will probably do things like LCD etc. 

Quote
then you may attract the requisite critical mass.
That will be the day, nothing I do attract anything as you can see by the lack of interest in this thread smiley I suppose I could make an Arduini-compatible board, but I fear there has been too many such boards already and most haven't taken the Arduino world by storm.

I just think that with more resources (on these larger chips) we could do so much better than the messy Arduino API, at least get the function naming more orthogonal, eg delay() and delayMicroseconds(), why isn't it delayMillesecnds()? And what does "interrupts()" do? Yes I know what it does now but it's not descriptive, enableInterrupts() is.

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Greenville, IL
Offline Offline
Edison Member
*
Karma: 15
Posts: 1330
Warning Novice on board! 0 to 1 chance of errors!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


 My interest in 32-bit chips in not for the higher math capabilities, but rather the speed these Arm chips offer. At my level of programming, I can write programs using Analogread, and digitalwrite to make a whole lot of things going on in my sketch. Where I have ran into problems is that at some point, I require too many tasks to be completed in relation to how long the loop() takes to repeat.

 What I like about the faster chips is, that I can add more and more tasks and still not worry about being behind on my timing of reads or writes.

 I have had some success with a Maple clone but, I find it easier to work with my Arduino boards most of the time.
Logged


Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

That's the plan, I should grab some 3rd-party library source code and see how it goes. I still have a lot to do before that though.

Trying to port over some code from a few libraries from time to time will be a good gauge of your progress, I would think. Some are much simpler than others, of course. The Leaf Maple guys had some of the simpler libs ported more or less from day one, but I remember seeing one thread where there were still people struggling with getting a FAT library for an an SD card to work properly over a year later! Discouraging. The lack of ported libs, and the lack of tools to port the more complex ones over (straightforwardly) really limited the usefulness of the Maple board, even though the hardware design was very promising, I thought. But despite all the raw power, the humble Arduino Mega2560 was a far more capable platform, IMHO, simply because of the wealth of ready-to-go 3rd party software for it.

On a side note, my suspicions for the delay of the Due is that doing exactly what we are discussing here is actually much easier to discuss than do! It will interesting to see what has been ported and what hasn't when it (finally) comes out.

One reason I suspect the hardware side has probably been done and locked down for some time now is the Android variant of the Due that came out during the Google IO conference quite a few weeks ago now. So, in a sense, it would appear the Due _hardware_ already has been released, so one might assume the wait is in finalising the software. Probably better late than half-baked in that regard, so it may actually be a very good sign. Or a very bad one!




 
« Last Edit: July 16, 2012, 10:15:31 am by pico » Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

nr Bundaberg, Australia
Online Online
Tesla Member
***
Karma: 126
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I require too many tasks to be completed in relation to how long the loop() takes to repeat.
The LPC1227 that I'm using was chosen because it's perfect for something else I'm working on. This Arduino port is a side effect really. I see the 1227 as a Mega1284 on steroids, and as I think the 1284 is the perfect general-purpose "Arduino" chip I love the 1227 for being the same only better.

Quote
struggling with getting a FAT library for an an SD card to work properly over a year later!
Yikes! I won't spend that much time on a library I think. Actually one issue I have is that I'm stuck with C and no C++, that's because you have to pay for C++ and I'm not sure I want to do that yet. I will have to make that decision soon though as the more I write the harder it will be to port my port smiley

Quote
It will interesting to see what has been ported and what hasn't when it (finally) comes out.
Very much so.

Quote
my suspicions for the delay of the Due is that doing exactly what we are discussing here is actually much easier to discuss than do!
My thoughts as well, so I'm not going to kill myself trying to replicate what the Arduino guys are presumably doing as we speak. As I say on the linked web page "to create a familiar environment for Arduino programmers" (by which I mean the language and function calls, not the IDE), I want something that is familiar and that hides all the register twiddling that it still necessary, for example here's the code I've written to set up a UART and associated structures in memory

Code:
serialConnection * serialCreate (uint8 port, uint32 baudrate, uint8 data_bits, uint8 parity, uint8 stop_bits,
uint8 rx_buff_size, uint8 tx_buff_size) {

LPC_UART0_Type * uart;
uint32 clkDiv;
serialConnection * s;

if (port > MAX_SERIAL_CONNECTIONS) {
SYS_ERROR (ERR_SERIAL_BAD_PORT | 1);
SYS_ERROR (ERR_SERIAL_INIT_FAILED);
return (serialConnection *)ERROR;
}

/////////////////////////////////////////////////////////////////
// Create the serial structure
s = (void*)safeMalloc(sizeof (serialConnection));

if (s == NULL) {
SYS_ERROR (ERR_SERIAL_INIT_FAILED | 2);
return (serialConnection *)ERROR;
}

serialConnections [port] = s;

s->object_id = OBJID_SERIAL_CONNECTION;
s->not_object_id = ~OBJID_SERIAL_CONNECTION;

/////////////////////////////////////////////////////////////////
//
switch (port) {
case SERIAL_UART0:

/////////////////////////////////////////////////////////////////
// Setup Rx and Tx pins
pinFunc (1, FUNC_RXD0);
pinFunc (2, FUNC_TXD0);

s->rxPin = 1;
s->txPin = 2;

/////////////////////////////////////////////////////////////////
// Disable this UART's interrupt for the duration
NVIC_DisableIRQ(UART0_IRQn);

/////////////////////////////////////////////////////////////////
// Set a pointer to the UART structure. Note that UART0 and UART1 have different
// structs but for the purposes of this function they are the same because
// we are not using the RS-485 features of UART0
uart = LPC_UART0;
s->uart = uart;

/////////////////////////////////////////////////////////////////
// Create Rx and Tx buffers
s->RxBuffer = FIFObuffer16Create(rx_buff_size);
if (s->RxBuffer == NULL) {
free (s);
SYS_ERROR (ERR_SERIAL_INIT_FAILED | 3);
return (serialConnection *)ERROR;
};

s->TxBuffer = FIFObuffer16Create(tx_buff_size);
if (s->TxBuffer == NULL) {
free (s);
free (s->RxBuffer);
SYS_ERROR (ERR_SERIAL_INIT_FAILED | 4);
return (serialConnection *)ERROR;
}

/////////////////////////////////////////////////////////////////
// Release the UART's reset
LPC_SYSCON->PRESETCTRL |= (0x1 << 2); // Set UART0_RST_N bit

/////////////////////////////////////////////////////////////////
// Enable the UART's AHB clock
LPC_SYSCON->SYSAHBCLKCTRL |= (0x1 << 12); // Set UART0 bit

/////////////////////////////////////////////////////////////////
// Set the UART's clock divider
LPC_SYSCON->UART0CLKDIV = 0x1;  // divide by 1, may be different later

/////////////////////////////////////////////////////////////////
// Get the clock divider for later use in baudrate calcs
// Could use the constant 1 but maybe this will change later
// so we just re-read the register
clkDiv = LPC_SYSCON->UART0CLKDIV;
break;

case SERIAL_UART1:
// NOTE: Comments as per the above UART0 code

pinFunc (8, FUNC_RXD1);
pinFunc (9, FUNC_TXD1);

s->rxPin = 8;
s->txPin = 9;

NVIC_DisableIRQ(UART1_IRQn);
uart = (LPC_UART0_Type*) LPC_UART1;
s->uart = uart;
s->RxBuffer = FIFObuffer16Create(rx_buff_size);
if (s->RxBuffer == NULL) {
free (s);
SYS_ERROR (ERR_SERIAL_INIT_FAILED | 5);
}

s->TxBuffer = FIFObuffer16Create(tx_buff_size);
if (s->TxBuffer == NULL) {
free (s);
free (s->RxBuffer);
SYS_ERROR (ERR_SERIAL_INIT_FAILED | 6);
}

LPC_SYSCON->PRESETCTRL |= (0x1 << 3); // Set UART1_RST_N bit

LPC_SYSCON->SYSAHBCLKCTRL |= (0x1 << 13); // Set UART1 bit

LPC_SYSCON->UART1CLKDIV = 0x1; // divide by 1, may be different later

clkDiv = LPC_SYSCON->UART1CLKDIV;

break;

default:
free (s);
SYS_ERROR (ERR_SERIAL_INIT_FAILED | (port << 8) | 7);
return (serialConnection *)ERROR;

}

/////////////////////////////////////////////////////////////////
//
// Start with a blank slate in the Line Control Register
//
uart->LCR = 0;

/////////////////////////////////////////////////////////////////
//
// Setup data bits
//
if (data_bits < 5 || data_bits > 8) {
SYS_ERROR (ERR_SERIAL_BAD_DATA_BITS | data_bits);
data_bits = UART_DATA_BITS_8;
}
uart->LCR |= data_bits - 5;
s->data_bits = data_bits;

/////////////////////////////////////////////////////////////////
//
// Setup stop bits
//
if ((stop_bits != UART_STOP_BITS_1) && (stop_bits != UART_STOP_BITS_2)) {
SYS_ERROR (ERR_SERIAL_BAD_STOP_BITS | stop_bits);
stop_bits = UART_STOP_BITS_2;
}
uart->LCR |= stop_bits;
s->stop_bits = stop_bits;

//////////////////////////////////////////////////////////////////
//
// Setup parity
//
if ((parity != UART_PARITY_NONE) &&
(parity != UART_PARITY_ODD) &&
(parity != UART_PARITY_EVEN) &&
(parity != UART_PARITY_FORCE1) &&
(parity != UART_PARITY_FORCE0)) {

SYS_ERROR (ERR_SERIAL_BAD_PARITY);
parity = UART_PARITY_NONE;
}
uart->LCR |= parity;
s->parity = parity;

//////////////////////////////////////////////////////////////////
//
// Setup baudrate
//
uint32_t Fdiv;

Fdiv = ((SystemCoreClock / clkDiv) / 16) / baudrate;

uart->LCR |= 0x80;  // DLAB = 1
uart->DLM = Fdiv / 256;
uart->DLL = Fdiv % 256;
uart->LCR &= ~0x80; // DLAB = 0

s->baudrate = baudrate;

//////////////////////////////////////////////////////////////////
//
// Setup Fractional Divide Register
//
uart->FDR = 0x10; // set to default value, does nothing

//////////////////////////////////////////////////////////////////
//
// Setup FIFOs
//
uart->FCR = (1 << 0) | // Enable both FIFOs
    (1 << 1) | // Reset Rx FIFO
    (1 << 2) | // Reset Tx FIFO
    (3 << 7); // RX FIFO trigger level = 14 chars

//////////////////////////////////////////////////////////////////
//
// Ensure a clean start, no data in either Tx or Rx FIFO.
//
while ((uart->LSR & (LSR_THRE | LSR_TEMT)) != (LSR_THRE | LSR_TEMT)) ;
while (uart->LSR & LSR_RDR) clkDiv = uart->RBR;

//////////////////////////////////////////////////////////////////
//
// Clear any line status bits
//
clkDiv = uart->LSR;

//////////////////////////////////////////////////////////////////
//
// Setup and enable UART interrupts
//
uart->IER = IER_RBR | IER_THRE | IER_RX;

if (port == 0)
NVIC_EnableIRQ(UART0_IRQn);
else
NVIC_EnableIRQ(UART1_IRQn);

return s;

}

Now a lot of that is overhead I want to various reasons, but there's also a lot of knowledge required to write into various registers. Much easier to write

Code:
my_uart = serialCreate(SERIAL_UART0, 115200, UART_DATA_BITS_8, UART_PARITY_ODD, UART_STOP_BITS_1, 10, 10);

or even

Code:
my_uart = serialBegin(SERIAL_UART0, 115200);

Which I guess is the point of a HAL after all.

______
Rob
« Last Edit: July 16, 2012, 10:51:06 am by Graynomad » Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Just a thought: If you keep C++ classes in mind while writing in straight C, you can write C code that is pretty close to C++ classes just by using structures and systematically named functions (just as you are proposing above for SerialBegin() or perhaps Serial_Begin()).

Make any future porting from C to C++ reasonably "mechanical". But backporting a lib from C++ to C would still be a lot of work. So the lack of a free or inexpensive C++ compiler might be a critical roadblock in getting lots of support for porting libs across. smiley-sad

OTOH, "classic" C is still cool, IMHO, and for new lib development would not be an impediment at all! smiley

Keep us posted. Your serial port code example looks like an excellent example of what a good HAL should do. Not too much, not too little...
Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

Ayer, Massachusetts, USA
Offline Offline
Edison Member
*
Karma: 54
Posts: 1848
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yikes! I won't spend that much time on a library I think. Actually one issue I have is that I'm stuck with C and no C++, that's because you have to pay for C++ and I'm not sure I want to do that yet. I will have to make that decision soon though as the more I write the harder it will be to port my port smiley
It may be the compiler you are using you have to pay for C++, but the GNU Compiler Collection (GCC) offers C and C++ (other languages as well, but those are mostly for hosted systems).  In fact on the Arduino, you are using GCC under the covers.  The LPC 1227 chip uses a Cortex Arm-M0, and I believe the GCC port is well supported.  You might need to provide appropriate libraries and ports of libstdc++ and a C library.
Logged

nr Bundaberg, Australia
Online Online
Tesla Member
***
Karma: 126
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I was an early adopter of C++ when it was first released but I'm pretty rusty with it these days, my last 7 years in the job (and now 13 out of the job) I hardly used it, mostly because the product I worked on had to be straight K&R, no fancy bits were allowed because it had to be ported to all systems known to man. From a PC to a Burroughs mainframe (which has 9-bit bytes BTW, it took some work to figure out why the code died on that machine smiley).

So I'm fairly happy to stick with C.

Quote
If you keep C++ classes in mind while writing in straight C
I hope that's what I'm doing, for example variables that would normally be globals are only accessible with system calls and all "object constructors" like the serial one I showed return a pointer to the object.

As you can also see I am paying a lot of attention to error detecting and handling, something I think Arduino barely pays lip service to.

Quote
So the lack of a free or inexpensive C++ compiler might be a critical roadblock in getting lots of support for porting libs across.
Possibly, as mentioned especially for porting of existing libs which I would say are mostly C++.

Quote
OTOH, "classic" C is still cool, IMHO
My sediments exactly, C++ is nice but good C can do almost as much. You can still use the same principles just have to work a bit harder.

Quote
It may be the compiler you are using you have to pay for C++,
The LPC Xpresso environment does in fact use GCC, it appears to be nobbled until you get the paid-for version. I assume that's just a switch somewhere and you don't have to do a total new download so maybe it can be unswitched smiley

I think this is a bit short-sighted of the tool chain people, limit the codes size yes, but not the language.

______
Rob
 
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
God Member
*****
Karma: 32
Posts: 830
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I think this is a bit short-sighted of the tool chain people, limit the codes size yes, but not the language.

Agreed.

Hope the project progresses -- please keep us informed. Hope you find that switch! smiley-wink
Logged

WiFi shields/Yun too expensive? Embeddedcoolness.com is now selling the RFXduino nRF24L01+ <-> TCP/IP Linux gateway: Simpler, more affordable, and even more powerful wireless Internet connectivity for *all* your Arduino projects! (nRF24L01+ shield and dev board kits available too.)

Ayer, Massachusetts, USA
Offline Offline
Edison Member
*
Karma: 54
Posts: 1848
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The LPC Xpresso environment does in fact use GCC, it appears to be nobbled until you get the paid-for version. I assume that's just a switch somewhere and you don't have to do a total new download so maybe it can be unswitched smiley

I think this is a bit short-sighted of the tool chain people, limit the codes size yes, but not the language.
I would imagine the simplest way to disable the C++ bits is just not ship the C++ compiler and libraries.

The GCC bits themselves are covered under the GNU Public License, but not their libraries/debug support, which you would have to recreate.  You might want to check out http://www.linaro.org/ if you want to get past LPC's value add, and just get the free software bits.

Or you can strike off on your own, downloading the toolchain source directly and delving into the builds.  As a long term GCC developer (powerpc right now, but I've done other processors in the past, but never ARM nor AVR), I can tell you it is a large learning curve.  smiley-cool
Logged

nr Bundaberg, Australia
Online Online
Tesla Member
***
Karma: 126
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I would imagine the simplest way to disable the C++ bits is just not ship the C++ compiler and libraries.
Maybe, but that would required a second download which is frowned upon by most people. There are key entry/activation options in the Help menu so I'm assuming it's all there just needs a key.

Quote
I can tell you it is a large learning curve.
I can well imagine, I'd rather be writing code that fighting a new toolchain.

I spent years dicking with search paths, makefiles that piped into SED scripts that begat AWK scripts that piped into...etc etc and I'm well over it. I need a turnkey toolchain and the LPCXpresso seems to be it.

So if that means C and not C++ I guess I can live with that.

_______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Pages: [1]   Go Up
Jump to: