Blocking Blues: TWI, Serial and SPI together

I'm interfacing GPS (using New Soft Serial), Compass (I2C), and WiFi (SPI), along with miscellaneous simpler sensors.

The blocking behavior of the Wire (TWI) library and New Soft Serial strike me as probably incompatible, asking for all kinds of strange problems if I try to use them together. I'm creating an interrupt-driven, callback, version of Wire, but it looks to me like New Soft Serial is still going to spend all it's time blocking, while its reading the GPS.

Is there a library that can use arbitrary IO pins for Serial IO (as New Soft Serial does), that uses the built-in timers to generate interrupts, rather than blocking for the receipt of each bit on the serial line?

but it looks to me like New Soft Serial is still going to spend all it's time blocking, while its reading the GPS.

Newsoftserial uses interrupts on received data, so not blocking.

Lefty

Well, sort of. The interrupt does occur at the beginning of the start bit, but then it stays in the interrupt service routine, doing a series of "tunedDelay"s until the whole character is received!

from void NewSoftSerial::recv()
:
for (uint8_t i=0x1; i; i <<= 1)

{

tunedDelay(_rx_delay_intrabit);

DebugPulse(_DEBUG_PIN2, 1);

uint8_t noti = ~i;

if (rx_pin_read())

d |= i;

else // else clause added to ensure function timing is ~balanced

d &= noti;

}

The ISR returns when the stop bit is received, but then the whole process starts again a few microseconds later, with another start bit.

Unless I'm missing something important, it seems to me that NewSoftSerial spends 98% of the Arduino's resources doing tunedDelay.

Unless I'm missing something important, it seems to me that NewSoftSerial spends 98% of the Arduino's resources doing tunedDelay.

I suppose that depends on how much data the Arduino is receiving. If there is that much data, you won't have resources left to do anything else, anyway.

To answer your original question, no, there is no better software serial implementation available for the Arduino.

Perhaps you just need to spend a few bucks and get a Mega.

What I'm thinking is that every second a GPS sends a torrent of data. During this time, the controller is basically swamped if it's using NewSoftSerial.

These are not low-processing-power devices! National defense hung on processors of less power not that long ago.

If the device isn't spending all its time in some silly delay loop, it can easily pack away the characters received, and do other useful stuff.

Is it possible to get an interrupt for both falling and rising edges on a digital pin? I believe it is. I'm going to try my hand at responding to those interrupts and assembling received characters that way. We'll see!

Is it possible to get an interrupt for both falling and rising edges on a digital pin? I believe it is.

Yes, use CHANGE as the 2nd argument to attachInterrupt.

gknight4:
I'm interfacing GPS (using New Soft Serial), Compass (I2C), and WiFi (SPI), along with miscellaneous simpler sensors.

The blocking behavior of the Wire (TWI) library and New Soft Serial strike me as probably incompatible ...

You could use the hardware serial, couldn't you? Failing that, for around $6 add a second Atmega328 processor and have it handle the GPS (using hardware serial) and send the results via I2C to the main processor. Or use a Mega.

I don't know why you guys have so little faith in the chips we've got!

20 Mips is a lot to work with. The problem is these stupid blocking libraries that we have. I have similar issues with the TWI Wire library. I'm not familiar yet with the SPI library, but I'll bet it has the same issues. All of these are fixable.

I have an interrupt service routine written that takes Serial RX input from an arbitrary pin (like NewSoftSerial), finds the start bits, and packs the bits away properly as characters, with basically zero overhead (a few C lines for each interrupt). Tomorrow, I'll "normalize" it so that it can be used as the serial input to the TinyGPS library, and put it where others can use it. At some point I'll do a Tx part, and make it work as a replacement for NSS.

To answer some of the questions:

I don't want to use Rx and Tx, because it makes the programming and debugging significantly more difficult. I don't want to use the Mega, because I'm using LinkSprite's Diamondback, for the WiFi functionality.

The Arduino is a way powerful chip, but it's spending all it's time in wait loops!

No Blocking! Free the Arduino!

20 Mips is a lot to work with.

16 Mips in the case of an Arduino board.

Tomorrow, I'll "normalize" it so that it can be used as the serial input to the TinyGPS library, and put it where others can use it. At some point I'll do a Tx part, and make it work as a replacement for NSS.

That would be a nice contribution, thanks in advance.

Lefty

If you haven't already purchased the GPS you could try this DssCircuits.com is for sale | HugeDomains

It only does receive and doesn't actually put the last character into the buffer until the next one is received, but it doesn't block!

http://telobot.com/downloads/nbserial.zip

For GPS, I'm using the SkyNav SKM53 ($30 on ebay, including shipping!) on a prototyping board from Radio Shack, cut down to Arduino size. It accommodates the GPS, tilt-compensated LSM303 Compass (SparkFun, with added 1.8v to 5v level shifters), L293D Motor Controller, as well as connections for several distance sensors and a quadrature rotary encoder. And, of course, the SPI WiFi.

The GPS works great, easily acquiring a signal at my indoor workbench, next to the EMI-noisy computer.

It is to make all these things work smoothly together that I'm interested in non-blocking libraries.

Next: a master-only, non-blocking TWI Wire library, and then I'll come back and pretty-up NBSerial.

What I'm thinking is that every second a GPS sends a torrent of data.

If that's part of your problem, shut off the constant data from the GPS and only request the sentence when you need a fix (so to speak). i.e.

// send the GPS these strings on init . . .
#define GGA_OFF  "$PSRF103,00,00,00,01*24\r\n"
#define GLL_OFF  "$PSRF103,01,00,00,01*27\r\n"
#define GSA_OFF  "$PSRF103,02,00,00,01*26\r\n"
#define GSV_OFF  "$PSRF103,03,00,00,01*27\r\n"
#define RMC_OFF  "$PSRF103,04,00,00,01*20\r\n"

// send this just before you check for a read . . .
#define RMC_READ  "$PSRF103,04,01,00,01*21\r\n" //  used to REQUEST a RMC sentance

It only does receive and doesn't actually put the last character into the buffer until the next one is received, but it doesn't block!

Well that could be a show stopper depending on the protocol being used?

Lefty

The point is that the net processing power of the controller goes way down when its receiving data through NSS. If there's other stuff that it should be paying attention to, it's probably going to do funny things. True interrupt-driven code is definitely the way to go. Everything else is worse.

Indeed, holding onto that last character until another one comes along is definitely not good behavior! It's OK for my application, because there's always more GPS data coming to move it along, but that's not true for others. I'll fix it soon.

I got the transmit portion of TWI over to full-interrupt-driven, wait-for-semaphore mode. When the receive portion works, the hard part there will be done, and I'll fix the receive portion of NBSerial, and maybe add the Tx.

Sounds great! Look forwards to seeing it. Bear in mind many people are not comfortable with asynchronous programming models so some clear examples will help.

On this forum, for example, it is hard to explain the "blink without delay" technique. Doing something like starting a TWI transmit, and having it complete later, will require more coding complexity.

I agree. I will endeavor to provide some comprehensible sample code.

I have TWI working on both the transmit and receive sides. I'll get that cleaned up a little and post it. It should work right out of the box as a replacement for Wire, but then allow for the substitution of non-blocking equivalents. The library will be NBWire.

I've married the Tx side of NSS to the receive side of NBSerial, so that it can talk to itself through a bridge between two pins. It is sending and receiving simultaneously (which would be way impossible with just NSS), but it's dropping bits right now. Soon! When that works, I can replace NSS Tx with a non-blocking version and be done.