Go Down

Topic: NewSoftSerial Library: An AFSoftSerial update (Read 18 times) previous topic - next topic

mikalhart

Jan 30, 2009, 08:00 am Last Edit: Jan 31, 2009, 01:15 am by mikalhart Reason: 1
Hi all--

As an optional companion to TinyGPS, I'm also releasing NewSoftSerial, a library that updates AFSoftSerial in several ways:


  • It corrects a bug in recv() that would sometimes drop received characters
  • It inherits from built-in class Print, eliminating some 4-600 bytes of duplicate code
  • It implements a circular buffering scheme that makes processing RX data more efficient
  • It extends support to all Arduino pins 0-19 (0-21 on Arduino Mini), not just 0-13
  • It supports multiple simultaneous soft serial devices.*

    *But make sure to read the disclaimers in the documentation. :)

    Mikal

    PS Feedback welcome.

    M

mellis

Awesome.  I'll look into making this the new default SoftwareSerial library for Arduino.

etracer

#2
Feb 01, 2009, 03:56 am Last Edit: Feb 01, 2009, 03:58 am by etracer Reason: 1
I'm having some trouble with the NewSoftSerial library.  Serial receives are hanging the program.  It seems like single characters received very slowly will work fine.  But as soon as multiple characters are received quickly, the program freezes.  Sending seems to work without any problems.

Here's some sample code.  It's basically a baud rate converter.  The hardware UART is configured for 9600 baud and the NewSoftSerial instance is set for 2400 baud (I've tried other speeds and it made no difference on the locking).  Any data received by the hardware UART is sent out through NewSoftSerial and vice versa.

Code: [Select]

#include <NewSoftSerial.h>
#define rxPin 2
#define txPin 3
#define ledPin 13

boolean ledState = false;
int counter = 255;            // To slow down the LED blinking
byte incomingByte = 0;

NewSoftSerial nss(rxPin, txPin);

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);
 nss.begin(2400);
 pinMode(ledPin, OUTPUT);      // sets the digital pin as output
 digitalWrite(ledPin,ledState);
 delay(2000);
}

void loop()                     // run over and over again
{
 
// Blink the LED on pin 13 just to show we're alive

 if (counter > 0) {
   counter-=1;
 } else {
   ledState = ! ledState;
   digitalWrite(ledPin,ledState);
   counter=255;
 }

// Read from the hardware UART.
// If any data is available, write it out through the software serial

 if (Serial.available() > 0) {
   incomingByte = Serial.read();
   nss.print(incomingByte);
 }

// Read from software serial.
// If any data is available, write it out through the hardware UART
 
 if (nss.available() > 0) {
   incomingByte = nss.read();
   Serial.print(incomingByte);
 }

 delay(1);                  
}


I thought there might be an interrupt conflict between the hardware UART and the NewSoftSerial but commenting out the hardware Serial code doesn't prevent lockups.  Even this simplified code will freeze after a few characters are received rapidly by NewSoftSerial.

Code: [Select]

#include <NewSoftSerial.h>
#define rxPin 2
#define txPin 3
#define ledPin 13

boolean ledState = false;
int counter = 255;            // To slow down the LED blinking

NewSoftSerial nss(rxPin, txPin);

void setup()                    // run once, when the sketch starts
{
 nss.begin(2400);
 pinMode(ledPin, OUTPUT);      // sets the digital pin as output
 digitalWrite(ledPin,ledState);
 delay(2000);
}

void loop()                     // run over and over again
{
 
// Blink the LED on pin 13 just to show we're alive

 if (counter > 0) {
   counter-=1;
 } else {
   ledState = ! ledState;
   digitalWrite(ledPin,ledState);
   counter=255;
 }
 
 delay(1);                  
}


Notice that I'm not even calling any NewSoftSerial functions (except for the begin) or even using the hardware Serial at all.  Actually the program isn't doing anything but initializing the NewSoftSerial instance and blinking a LED.

Hopefully this is enough info to help find the problem as I'd really like to use this library.

I forgot to add that this is with Arduino 0012 running on a G5 (PPC) Mac.  I downloaded the latest NewSoftSerial library today.

mikalhart

#3
Feb 01, 2009, 04:48 am Last Edit: Feb 01, 2009, 05:09 am by mikalhart Reason: 1
etracer--

Thanks very much for testing out NewSoftSerial.  I tried your second example with both 0012 and the unreleased 0013 and do not see the lockup you are experiencing.  I do notice that when I connect my GPS to the software serial port that the rate of flicker in the LED becomes slightly uneven, but I guess I would expect this.  We are getting hundreds of interrupts per second after all.

What device is connected to your software serial port?

As a test, would you please replace NewSoftSerial with AFSoftSerial?  Since NewSoftSerial inherits much of its interrupt code from AFSoftSerial, this will be a very interesting test.  It should be an exact replacement in this case.

In your baud rate converter -- excellent project by the way -- you need to change

Code: [Select]
nss.print(incomingByte);
to

Code: [Select]
nss.print(incomingByte, BYTE);

and similarly with the Serial.print.

EDIT: I just tried your baud rate converter.  Running the NewSoftSerial at 4800 and the Serial console at 9600, I get my GPS data in the console beautifully.  If I ratchet Serial and the console down to 2400 I still get valid data, but some of it is lost -- exactly as you'd expect.


Mikal

etracer

I've tried a variety of devices with the software serial.  Originally I started testing with connecting to a APC SmartUPS at 2400 baud through a MAX232.  When I was getting the unexplained lockups, I connected next to a PC serial port (again through the MAX232) so I could manually send data to the software serial.  Trying to eliminate the MAX232 as the possible problem, I next connected directly to a ID-12 RFID module which outputs TTL-level serial (at 9600 baud).  In all cases the results were the same and the software serial locks up after a few rapid characters.  It's random as to how many characters make it before the lock.

I tried AFSoftSerial and get the same lockups.

I did some digging in the source and was able to confirm the problem is somewhere in the recv() called by the interrupts.  Commenting out the code (but leaving the definition) in NewSoftSerial::recv() stops the lockups - but obviously also stops any data.  In playing with the code I find that if I leave any more code then the initial digitalRead if, I get lockups.  So this code doesn't lock up (but also obviously doesn't return any data).

Code: [Select]

void NewSoftSerial::recv()
{

 char i, d = 0;

 if (digitalRead(_receivePin))
   return;       // not ready!
/*

 tunedDelay(_bitDelay - 8);

 for (i=0; i<8; i++) {
   //PORTB |= _BV(5);
   tunedDelay(_bitDelay*2 - 6);  // digitalread takes some time
   //PORTB &= ~_BV(5);
   if (digitalRead(_receivePin))
     d |= (1 << i);
 }
 tunedDelay(_bitDelay*2);

 // buffer full?
 if ((_receive_buffer_tail + 1) % _NewSS_MAX_RX_BUFF == _receive_buffer_head)
   return;

 _receive_buffer[_receive_buffer_tail++] = d; // save new byte
 if (_receive_buffer_tail == _NewSS_MAX_RX_BUFF)
   _receive_buffer_tail = 0;

*/
}


I think the problem might either have something to do with calling the digitalRead function or simply having the code running during the interrupt taking too long.  As a trivial case, I replaced the recv() function with:

Code: [Select]

void NewSoftSerial::recv()
{

 if (digitalRead(_receivePin))
   return;       // not ready!
}


While the code will obviously return no data, I also get no lockups.  If I add one more digitalRead() like this:

Code: [Select]

void NewSoftSerial::recv()
{

 if (digitalRead(_receivePin))
   return;       // not ready!

 if (digitalRead(_receivePin))
   return;       // not ready!
}


Then I get the lockups.

Another probably unrelated weirdness that I stumbled across is with the tunedDelay() assembly.  While commenting out different bits of code to test, I found that if I had two calls to tunedDelay() one after another, I would get the following compile errors:

Code: [Select]

/var/tmp//cc0RgyXU.s: Assembler messages:
/var/tmp//cc0RgyXU.s:940: Error: register r24, r26, r28 or r30 required


I don't know AVR assembly so I can't really debug further.  Maybe there's a word alignment problem when the two assembly blocks are placed one after another?

I can't explain why your tests work fine and every test I try fails with lockups.  I've tried an Arduino NG, an Adafruit Boardurino, and a bare ATmega 168 on a breadboard with the Diecimila bootloader and get the same results with each (all at 16MHz).  I've even tried different pins for the software serial with no change.

Hopefully this info helps.

Thanks

Go Up