New fast digital I/O and Software SPI libraries

I posted new fast I/O libraries as DigitalPinBeta20120804.zip Google Code Archive - Long-term storage for Google Code Project Hosting..

The libraries support standard 168/328 Arduino, Mega, Leonardo, Teensy, Teensy++, and Sanguino.

The DigitalPin class provides very fast inline functions. DigitalPin is a template class and pin numbers must be specified at compile time.

For 328 pins and low address Mega pins read(), toggle(), and write() execute in two cycles or 125 ns for a 16 MHz CPU. This is about thirty times faster than digitalWrite() which executes in about 4 usec.

The main member functions for the DigitalPin class are:

void config (bool mode, bool level);
void high ();
void low ();
void mode (bool pinMode);
bool read ();
void toggle ();
void write (bool value);

The library also contains these static inline functions similar to digitalRead()/digitalWrite(). Pin number must be a constant.

static bool fastDigitalRead (uint8_t pin);
static void fastDigitalToggle (uint8_t pin);
static void fastDigitalWrite (uint8_t pin, bool level);
static void fastPinConfig (uint8_t pin, bool mode, bool level);
static void fastPinMode (uint8_t pin, bool mode);

There is also a Software SPI class that runs at about 2 MHz. It is a template class with compile time pin numbers and SPI mode. Modes 0 - 3 are supported MSB first. LSB first would be easy to implement.

The member functions are:

void begin ();
uint8_t receive ();
void send (uint8_t data);
uint8_t transfer (uint8_t txData);

There is also a class, DigitalIO, with run-time pin numbers. It is much slower than the above DigitalPin class but is 3 - 4 times faster than the Arduino digitalRead()/digitalWrite() functions.

Looks good, I have been using your previous version in one of my libraries for quite some time now.

Is the main difference ( apart from your new SPI compatibility ) the fact that the underlying implementation no longer uses a template interface. It looks a lot less complex without the non-type template declarations, however at first glance it seems mostly the same code within.

Its interesting to see we both separated the port manipulators into non-member functions and left the class as a simple wrapper. My motivation for this was to have a macro which uses __builtin_constant_p to decide weather to use Arduino standard digitalWrite or its static counterpart. The macro I tried failed, so being quite unfamiliar with __builtin_constant_p and why it failed rather than returning a value has left me with more reading to do. Maybe you have a definitive answer for this.

The DigitalIO class is nice. A clear speed / size trade-off, something I would be happy to pay in a dynamic but critical routine.

I don't like macro implementations and macros are not needed to use to use digitalWrite() or another function when the pin number is not a constant.

I produce an error when pin number is not a constant because most professional embedded programming standards consider having a function change execution speed by a factor of 30 when an argument is a constant to be poor practice.

You could do this to have a fast/slow digital write:

static inline __attribute__((always_inline))
void fastSlowWrite(uint8_t pin, bool value) {
  if (__builtin_constant_p(pin)) {
    fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, value);
  } else {
    digitalWrite(pin, value);
  }
}

This compiles as 692 bytes and write is fast.

#include <DigitalPin.h>
const uint8_t PIN = 13;
void setup() {
  pinMode(PIN, OUTPUT);
  fastSlowWrite(PIN, 1);
}
void loop() {}

This compiles as 872 bytes and write is slow.

#include <DigitalPin.h>
uint8_t var = 13;
void setup() {
  pinMode(var, OUTPUT);
  fastSlowWrite(var, 1);
}
void loop() {}

I made DigitalPin a wrapper since it insures the static and class functions behave the same. I almost used separate implementations since the template insures the pin number is a constant an much of the static function stuff is not needed.

Yes, I agree macros shouldn't be provided for client use, I just used one to do a quick test.

Looking at your function I realise my error was not due to __builtin_constant_p, but the value passed to it. My fast digitalWrite is a template function and the compiler cannot instantiate it when using a variable pin.

As for combining the two being bad practice, would it not be suitable if users of the code are made fully aware of the behaviour. As already existing functionality is being combined I see it more of an extension or more flexible to users preference. On the other hand, some things I prefer a more explicit approach.

If distributed code is written using the function you posted above, it would not tie the end user into one particular mode.

Embedded programming standards don't have statements like:

would it not be suitable if users of the code are made fully aware of the behavior.

To often one user's code is another user's library and stuff like this gets lost and tragedies happen.

Nothing really matters with Arduino since it is a case study in bad practice and I often follow with bad practice in my Arduino libraries and programs since they are just toys. I have some limits with Arduino and a factor of 30 in execution speed was too much.

Here is a typical coding standard for a critical system, The F35 Joint Strike Fighter http://www.jsf.mil/downloads/documents/JSF_AV_C++_Coding_Standards_Rev_C.doc.

Embedded programming standards don't have statements like:
Quote
would it not be suitable if users of the code are made fully aware of the behavior.

Agreed, they probably don't. But what they should have is technical documentation specifying all the client needs to know. Reading the documentation is hopefully part of most design standards, certainly in mine anyway. I assume you are pointing out that this expectation isn't always met on large scale systems or projects involving many people.

And as much as I want to argue I guess I can't overcome the fact that someone will eventually skip the important information, causing a bad day.

I'll download libre office so I can view that doc. Might be some good insights in there.

Nothing really matters with Arduino since it is a case study in bad practice

:slight_smile:

Here's a PDF of the JSF document for those who don't want to download a DOC

http://www.robgray.com/temp/JSF-AV-rules.pdf

and the similar JPL standard

http://www.robgray.com/temp/JPL_Coding_Standard_C.pdf

As an example of the Arduino code, from digitalWrite()

uint8_t port = digitalPinToPort(pin);
...
if (port == NOT_A_PIN) return;

digitalPinToPort() is just a macro that indexes into an array, there is no bounds checking and any non-zero value satisfies the if test. So

pin = 1000;
...
digitalWrite(pin, HIGH);

Probably actually works (I don't have any hardware to test it right now) but would almost certainly not do what you expect.

To be fair for what the Arduino is this may be a reasonable trade off between reliability and speed, but we often get "Can I use an Arduino in a commercial product?" style questions and while legally you can, in good conscience I don't think we should advise in the affirmative on technical grounds.


Rob

And what's wrong with the technical ground of Arduino?

I'm referring to the lack of defensive coding techniques used in the standard Arduino libraries. My digitalWrite(1000, HIGH); example should not be allowed to execute at run time. The error should be trapped. And IIRC there are many more examples.

I appreciate that error trapping make the program slower and larger and it's not required for the average Arduino user anyway so I'm not having a go at the decisions that were presumably made in this regard when the code was written with the "wearable LED/installation art" audience in mind.

But I don't believe the Arduino libraries should be used in a commercial or otherwise critical application.


Rob

I assure you that there are many commercial projects that use coding standards no more rigid than the Arduino core.

I have no doubt of such :astonished:


Rob

I'm not an expert on the matter, but I thought that the boundary conditions of the Arduino, mainly limited CPU power and limited memory, make it less suitable for very defensive coding and more suitable for (mild) programming by contract.

While it certainly does not meet the requirements of keeping an Joint Strike Fighter in the air and other life-and-death critical applications, think it is usable in may other commercial environments. Besides, there is the enterprise code a programmer would like to write, and there is the code he writes under time constraints, changing requirements etc. As said, compared to some commercial code, the Arduino libraries aren't always that bad :slight_smile:

Of course, this is all pretty off-topic...

fat16lib,
This library is just the idea that I needed for my project as I have been trying to make my own bit-blast SPI protocol, as I need to use pins other than 50,51,52,53 for my SPI calls.
I have my own crude bit-blast working, but I would like to take advantage of other device libraries, for example the MCP23S17 library, that are written for the SPI library, but I want to shift SPI over to pins 32, 34,36,38 on my Mega2560.

However, I'm having problems getting any signal from the GPIO pins on my Mega2560 using the SoftSPI library, or demo.
The below modified pin assigned demo compiles just fine with no errors, but when I probe pins 38 (CLK) or pins 36,34 I see nothing. Any ideas?

Also, I have a basic question about the SS line...
The standard SPI library includes the use of SS pin to select/deselect the slave, but I don't see the use of SS in this library, or example. Should the spi.transfer() calls be bounded calls to pull SS LOW,HIGH?
Why was the use of SS not included?

// scope test for development - assumes 328 processor
#include <DigitalPin.h>
#include <SoftSPI.h>

const uint8_t SOFT_SPI_MISO_PIN = 36;
const uint8_t SOFT_SPI_MOSI_PIN = 34;
const uint8_t SOFT_SPI_SCK_PIN  = 38;
const uint8_t SPI_MODE = 0;

SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, SPI_MODE> spi;

int test;
void setup() {
  Serial.begin(9600);
  spi.begin();
  while(1) {
    Serial.println("Enter:");
    Serial.println("R - Receive");
    Serial.println("S - Send");
    Serial.println("T - Transfer");
    while ((test = Serial.read()) <= 0) {}
    test = toupper(test);
    if (strchr("RST", test)) break;
    Serial.println("Invalid entry");
  };
  Serial.print("Starting test ");
  Serial.println((char)test);
}
void loop() {
  if (test == 'S') spi.send(0X55);
  if (test == 'R') {
    Serial.println(spi.receive(), HEX);
  }
  if (test == 'T') {
    Serial.println(spi.transfer(0XAA), HEX);
  }
  delay(10);
}

How are you looking at the pins?

If you connect pin 34 to pin 36 and run the "T - Transfer" command does "AA" print?

If you set pin 36 to GND, does the "R - Receive" command print 0? If pin 36 is set to 5V, "R - Receive" should print FF.

SS is needed for hardware SPI to work. SS must be set to an output in master mode so it is often used as chip select.

For software SPI in master mode no SS signal is needed. You can use any pin for chip select.

Graynomad:
I'm referring to the lack of defensive coding techniques used in the standard Arduino libraries. My digitalWrite(1000, HIGH); example should not be allowed to execute at run time. The error should be trapped. And IIRC there are many more examples.

I appreciate that error trapping make the program slower and larger and it's not required for the average Arduino user anyway so I'm not having a go at the decisions that were presumably made in this regard when the code was written with the "wearable LED/installation art" audience in mind.

But I don't believe the Arduino libraries should be used in a commercial or otherwise critical application.


Rob

Could you explain that better, as I don't see the case. The function is really digitalWrite(int_variable, HIGH); so the compiler can have no knowlege of what the run time variable value might become. And any run time check would seem to mean some kind of actual 'overhead' or 'supervisory' code running, which is not a C/C++ property as far as I know, and would just suck up more resources from my poor overworked underpaid AVR 8 bitter? Can't it just be on me to not make such stuipid errors in the first case?

OK, on rereading your post, you are pretty clear you aren't saying it's something aimed or needed by me, just commerical or safety critical type applications. Never mind. :wink:

Lefty

Can't it just be on me to not make such stuipid errors in the first case?

Yes, but we all make mistakes or forget to allow for boundary conditions or out of range values.

My example cannot be detected at compile time of course but a simple defensive coding is not about supervisory code, it's just applying sanity tests to incoming data. Here's an example from the framework I'm currently working on

	if (pin >= NATIVE_PINS) {
		SYS_ERROR (ERR_INV_PIN);
		return ERROR;
	}

It adds a single test and if there is a run-time problem it's reported with an error mechanism.

That's better than turning on a semi-random pin, maybe one that has a critical purpose. Here's another example

uint32 stringLoadFromArray(string * s, char * c) {

	VERIFY_OBJECT(s, OBJID_STRING)
        // OK, the struct is good we can use it with a reasonable amount of confidence

The function requires a pointer to a "string" structure and most code will simply take if for granted that "s" does in fact point to the right place. The VERIFY_OBJECT macro tests for two special bytes in the structure, if they don't conform to a particular format this is considered to be a fatal error, it is reported and the processor enters a while(1) loop.

This does add overhead that's for sure, and I see this as being a good reason for using the new Due because the overhead will be easily absorbed in the extra speed and memory.

One more for the road

uint32 serialSetUartPins(serialConnection * s, uint32 location) {

	uint8 txPin;
	uint8 rxPin;

	VERIFY_OBJECT (s, ERR_BAD_OBJECT);

	ASSERT_RETERR (location > 3, ERR_SERIAL_BAD_PORT);

	location <<= 1;
	rxPin = __uart_logical_pins[s->port][location];
	txPin = __uart_logical_pins[s->port][location+1];


	ASSERT_RETERR ((rxPin == ERROR) || (txPin == ERROR), ERR_SERIAL_BAD_LOCATION);

	TRY
		pinFunc (rxPin, (s->port == 0) ? FUNC_RXD0 : FUNC_RXD1);
		pinFunc (txPin, (s->port == 0) ? FUNC_TXD0 : FUNC_TXD1);
	CATCH_RETERR

	s->txPin = txPin;
	s->rxPin = rxPin;

	return NOERROR;
}

This function is mostly error checking code, an equivalent Arduino function would probably just write into some registers without question and you spend 3 hours wondering why the serial port doesn't work because you "know" that "location == 2".

Overkill? Maybe but there won't be much get through that shouldn't :slight_smile:

EDIT: After writing all that I saw your last paragraph :slight_smile:


Rob

Rob,

If you hangout here long enough, you will become one of us and reliably won't matter. I speak from experience, my standards are slipping.

For most programmers reliably is is handled by taking out the debug checks when software is released so the code is smaller and runs faster.

Here is a quote from long ago:

Turning off dynamic semantic checks once you've finished debugging your program is like taking off your seat belt once you've left the driveway.

This quote is wrong, modern programmers don't believe in seat belts or airbags. We are at the level of automobiles 60 years ago, there are no seat belts.

Modern programmers don't diagnose and fix bugs, they turn Bohrbugs into Heisenbugs. One way to make life better for users is to convert Bohrbugs into Heisenbugs.

This is good enough because Bohrbugs are showstoppers for users: every time the user does the same thing, he or she will encounter the same bug. With Heisenbugs, on the other hand, the bugs often go away when you run the program again. This is a perfect match for the way users already behave on the Web. If they go to a Web page and it fails to respond, they just click “refresh” and that usually solves the problem.

Here is a definition:

Jim Gray drew a distinction between two kinds of bugs. The first kind are bugs that behave predictably and repeatedly—that is, they occur every time the program encounters the same inputs and goes through the same sequence of steps. These are Bohrbugs, named for the Bohr atom, by analogy with the classical atomic model where electrons circle around the nucleus in planetary-like orbits. Bohrbugs are great when debugging a program, since they are easier to reproduce and find their root causes.

The second kind of bug is the Heisenbug, named for Heisenberg’s Uncertainty Principle and meant to connote the inherit uncertainty in quantum mechanics, which are unpredictable and cannot be reliably reproduced. The most common Heisenbugs these days are concurrency errors (a.k.a. race conditions), which depend on the order and timing of scheduling events to appear. Heisenbugs are also often sensitive to the observer effect; attempts to find the bug by inserting debugging code or running in a debugger often disrupt the sequence of events that led to the bug, making it go away.

Jim Gray wrote this in 1985 and not much has changed. http://www.hpl.hp.com/techreports/tandem/TR-85.7.pdf

If you hangout here long enough, you will become one of us and reliably won't matter.

:), I hope not.

I speak from experience, my standards are slipping.

Code I suggest on this forum never has any real tests in it because a) I haven't got the time to do it and b) most people asking for help won't understand anyway and I suspect the experienced guys here do similar. But don't let it creep into your own code and libraries.

As I said above I think there will be no excuses when we start writing code for the Due, it has the resources to include more defensive code.

Interesting document, I just read it. Did you know it was written by me? My full name is James Robert Gray, AKA Jim Gray :slight_smile:

From that document

Availability is doing the right thing within the specified response time. Reliability is not doing the wrong thing.

I aim for reliability, I'm content to stop a system rather than continue with bogus information. Availability implies redundancy which is out of the scope of what I'm doing I think.

Make each module fail-fast -- either it does the right thing or stops.

By that definition my code is "fail-fast", meaning that it's better to stop a system entirely and right away rather than let things propagate and probably get worse.

Processes are made fail-fast by defensive programming. They check all their inputs, intermediate results, outputs and data structures as a matter of course. If any error is detected, they signal a failure and stop. In the terminology of [Cristian], fail-fast software has small fault detection latency.

As I am trying to do.

There have been a few threads asking if Arduino can be used in a commercial environment and my answer has always been "No, not in my opinion" (talking from the software point of view here). I haven't looked for a while now but IIRC things like digitalWrite() blindly index into arrays.

The framework I'm writing (examples above are from it) attempts to address these sorts of issues but it's as much an academic project as anything as to be honest nobody seems to care about such things.

Still it keeps me off the streets at night :slight_smile:


Rob

The framework I'm writing (examples above are from it) attempts to address these sorts of issues but it's as much an academic project as anything as to be honest nobody seems to care about such things.

I find most of the things you say to be very interesting, unfortunately, the code you posted above is well above my understanding at this point.

Keep posting, I do pick up pieces at least,
Mark

if (pin >= NATIVE_PINS) {
    SYS_ERROR (ERR_INV_PIN);
    return ERROR;

So you do that in digitalRead, digitalWrite, analogWrite, digitalPinToTimer, digitalPinToBitMask, digitalPinToPort, and some equivalent in portOutputRegister, portModeRegister, portInputRegister, and code in most of the above to CHECK for ERROR on each return, till the error checking is larger than the code that actually is involved in doing something to a pin. (Those are all potentially user-accessable APIs, so they all need to do error checking, right?) And then you'd wonder why everyone is bypassing your code and using direct port IO without putting the error checking back.

Strong error checking seems like a good idea, but when you combine it with other modern programming practices that are also good ideas, you end up executing the same checks "many" times...

Pascal and Ada would have the compiler do this for you invisibly, of course, assuming that you create proper data types (ie tell the compiler that a "pin" is supposed to be from 1 to 19...)

(and that's without the issue of trying to figure out what SYS_ERROR is supposed to do on a deeply embedded system.)

(I guess in C++, the proper course of action is to make everything that's a pin be an object/class, and overload assignment/creation to do the range check so that the other methods don't need to? Except that you lose garbage collection of unused methods, leading to a different version of bloat?)