Pages: 1 2 [3] 4 5 ... 7   Go Down
Author Topic: An alternative Serial Library for Arduino 1.0  (Read 20877 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 12
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm having some buffer overflow problems with the 23 code and when I saw the configurable buffer size I setup the 1.0 code base on a windows 7/64 machine.  The 64 byte buffer is way too small when I get back to back messages while writing to the SD card as you have noted in other replies.
So to use this alternative library, I copy the folder SerialPort to the arduino-1.0/libraries folder, if I interpret the readme.txt correctly.  It is a tad unclear where it seems to have dropped something expected after 'to the' and continues on next line with 'your libraries directory.'  I am not sure how this will replace the core coding or override the core coding. Please direct me or hint/link to what I am missing.

quote: " This library provides access to the Arduino serial ports.

To install the this library, copy the SerialPort folder to the
your libraries directory.

If you only use unbuffered TX, edit SerialPort.h and set BUFFERED_TX zero."
endquote:

Logged

Guildford, UK
Offline Offline
Full Member
***
Karma: 0
Posts: 218
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The core Arduino based serial port code is only included in your sketch if you actually use it. For example:
Code:
void setup()
{
    Serial.begin(9600);
    Serial.println("Using Arduino supplied HardwareSerial");
}

void loop()
{
}

So if you've copied the SerialPort folder from the zip file into your Libraries folder (So you have Arduino-1.0\libraries\SerialPort\SerialPort.h and Arduino-1.0\libraries\SerialPort\SerialPort.cpp) you can write the following code instead

Code:
#include <SerialPort.h>

SerialPort<0, 32, 256> port;

void setup()
{
    port.begin(9600);
    port.println("Using SerialPort class");
}

void loop()
{
}

Just remember to do the following and you should be alright:

  • Include <SerialPort.h>
  • Declare new Serial port including its parameters
  • Initialise (call begin()) and use the new serial port (NOT Serial)

The configuration of the serial port looks odd to anyone not familiar with C++ templates, but it's easy to explain. The first parameter inside the angle brackets is the serial port number. Unless you've got a board with more than one serial port (e.g. Arduino Mega) this will always be 0. The second parameter is the size of the receive buffer in bytes and the third is the size of the transmit buffer in bytes.

You can ignore the stuff about editing SerialPort.h. You'll only need this is you're short of flash memory at which point you can come back and ask more questions.

If you do by chance refer to both the new SerialPort and HardwareSerial in the same sketch you'll get some error messages about duplicate interrupt vectors.

Hope that helps.

Iain
Logged

Smithfield, Rhode Island
Offline Offline
God Member
*****
Karma: 3
Posts: 843
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This looks really cool, I'll give it a try this week. By coincidence, I'm working on a project that uses three serial ports, at different baud rates. So I need a big buffer on some and not on others. I'm one of those people that could put a buffer bigger than 256 bytes to use.

Also, on the 9 bit support, its really handy for those who work with RS-485 busses. Not that I'm asking you to add it here, I just wanted to address the comment that its never needed.

Thanks very much!
Logged

North Queensland, Australia
Offline Offline
Edison Member
*
Karma: 65
Posts: 2111
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not sure how this will fair with multiple instances of a class but, If people do not want 16bits for storage this may help.
This change should be fine as the non-type template is specific to each specification of SerialPort, however this may lead to an increase of code with multiple instances.

Code:
struct SerialRingBuffer {
  uint8_t* buf;   /**< Pointer to start of buffer. */
  uint16_t head;  /**< Index to next empty location. */
  uint16_t tail;  /**< Index to last entry if head != tail. */
  uint16_t size;  /**< Size of the buffer. Capacity is size -1. */
};

changed to

Code:
template< const int _MaxSize > struct SerialRingBuffer {
  uint8_t* buf;   /**< Pointer to start of buffer. */
  #if ( _MaxSize > 255 )
  uint16_t head;  /**< Index to next empty location. */
  uint16_t tail;  /**< Index to last entry if head != tail. */
  uint16_t size;  /**< Size of the buffer. Capacity is size -1. */
 #else
  uint8_t head;  /**< Index to next empty location. */
  uint8_t tail;  /**< Index to last entry if head != tail. */
  uint8_t size;  /**< Size of the buffer. Capacity is size -1. */
 #endif
};
Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 12
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you very much and your examples help answer a number of questions.  On the Mega1280 I am able to compile and use the SerialPort alternate library examples but when I include the SD library I'm getting some problems.  Just adding #include <SD.h> brings up vector conflicts in the core HardwareSerial code so I will have to do some more digging for details.

Code:
#include <SerialPort.h>
#include <SD.h>

SerialPort<0, 32, 256> port;

void setup()
{
    port.begin(9600);
    port.println("Using SerialPort class");
}

void loop()
{
}

When the SD.h is compiled here are the compiler errors I'm getting. 

Code:

core.a(HardwareSerial.cpp.o): In function `__vector_25':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:102: multiple definition of `__vector_25'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:37: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_36':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:127: multiple definition of `__vector_36'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:57: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_51':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:140: multiple definition of `__vector_51'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:64: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_54':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:153: multiple definition of `__vector_54'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:71: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_26':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:190: multiple definition of `__vector_26'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:95: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_37':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:221: multiple definition of `__vector_37'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:111: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_52':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:238: multiple definition of `__vector_52'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:118: first defined here
core.a(HardwareSerial.cpp.o): In function `__vector_55':
C:\Arduino\arduino-1.0\hardware\arduino\cores\arduino/HardwareSerial.cpp:255: multiple definition of `__vector_55'
SerialPort\SerialPort.cpp.o:C:\Arduino\arduino-1.0\libraries\SerialPort/SerialPort.cpp:125: first defined here

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 12
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok that didn't take long to find after all.  SD.h includes Arduino.h and Arduino.h includes HardwareSerial.h
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1604
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

SD.h accesses Serial so it can't be used with SerialPort.  Serial uses the same interrupt vector as SerialPort.

I plan to modify SdFat so it can be used with SerialPort.

The problem is use of Serial, not the include of Arduino.h.  SerialPort includes Arduino.h.
« Last Edit: January 02, 2012, 10:35:12 pm by fat16lib » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 12
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the information and plans for SDFat modification but I feel like I'm creating more work and wish to be helping more.  Guess it's time to learn more about the workings of SD and it's interaction with serial. 
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 124
Posts: 6653
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You can probably speed up serial write() quite a bit if you're willing to require that the buffer size be a power of two.  Right now it's calling a 16bit divide function for each byte...
Logged

Guildford, UK
Offline Offline
Full Member
***
Karma: 0
Posts: 218
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That sounds like a reasonable limitation. I've only used powers of 2 so far. And you can enforce this easily using the pre-processor.

Iain
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1604
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is no point in limiting buffers to a power of two.  You don't need a divide to maintain the index for a ring buffer.

A power of two is very coarse for large buffers on a 328.  512 vs 1024 for example.

Also these functions are small and fast so I don't see a point in limiting the ring buffer indices to 8-bits.

Here are my get and put functions for the next version of SerialPort:
Code:
//------------------------------------------------------------------------------
/** get the next byte
 * \param[in] b location for the returned byte
 * \return true if a byte was returned or false if the ring buffer is empty
 */
bool SerialRingBuffer::get(uint8_t* b) {
  size_t t = tail;
  if (head == t) return false;
  *b = buf[t++];
  tail = t < size ? t : 0;
  return true;
}
//------------------------------------------------------------------------------
/** put a byte into the ring buffer
 * \param[in] b the byte
 * \return true if byte was transfered or false if the ring buffer is full
 */
bool SerialRingBuffer::put(uint8_t b) {
  size_t h = head;
  // OK to store here even if ring is full
  buf[h++] = b;
  if (h >= size) h = 0;
  if (h == tail) return false;
  head = h;
  return true;
}
Logged

Guildford, UK
Offline Offline
Full Member
***
Karma: 0
Posts: 218
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is no point in limiting buffers to a power of two.  You don't need a divide to maintain the index for a ring buffer.
Fair enough. If there's no reason for it, don't bother. I seen to remember the old HardwareSerial did do divides but my memory might be failing me.

Iain
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1604
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

For faster writes, it is better to optimize multibyte writes. 

I have been able to make the time to buffer data for write(const char*) and write(uint8_t*, size_t ) about 15 time faster than HardwareSerial.

See this post http://arduino.cc/forum/index.php/topic,85462.0.html

The example in the above post compares times for this statement to execute:
Code:
Serial.write("lkjljlkjlkjljlkjlkjlkjlkjlkjlkjlkj");

The time is over 900 us for HardwareSerial and about 60 us for SerialPort.

I will soon post this version of SerialPort. 

I have also added options for parity, character size, number of stop bits, and RX error checking.

I return four types of RX errors, framing, parity, USART overrun, and ring buffer overflow.
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1604
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I posted a new beta, SerialPortBeta20120103.zip, here http://code.google.com/p/beta-lib/downloads/list

Here is the changes.txt file:
Quote
3 Jan 2012

Almost total rewrite so expect bugs!  Be sure to tell me about bugs.

See html for more details about the following functions.

Changed begin() to have a optional second argument to set parity,
character size, and number of stop bits.

void    begin (long baud, uint8_t options = SP_8_BIT_CHAR)

The following can be ORed together for options:

Choose one stop bit option.
static const uint8_t    SP_1_STOP_BIT = 0 (default)
static const uint8_t    SP_2_STOP_BIT = M_USBS

Choose one character size.
static const uint8_t    SP_5_BIT_CHAR = 0
static const uint8_t    SP_6_BIT_CHAR = M_UCSZ0
static const uint8_t    SP_7_BIT_CHAR = M_UCSZ1
static const uint8_t    SP_8_BIT_CHAR = M_UCSZ0 | M_UCSZ1

Choose one parity option.
static const uint8_t    SP_EVEN_PARITY = M_UPM1
static const uint8_t    SP_NO_PARITY = 0
static const uint8_t    SP_ODD_PARITY = M_UPM0 | M_UPM1

Added RX error checking.

The following error bits are returned:
static const uint8_t    SP_FRAMING_ERROR = M_FE
static const uint8_t    SP_PARITY_ERROR = M_UPE
static const uint8_t    SP_RX_BUF_OVERRUN = 1
static const uint8_t    SP_RX_DATA_OVERRUN = M_DOR

Added the following functions:

void clearRxError()
uint8_t getRxError()
size_t    read (uint8_t *b, size_t n)
size_t    write (const char *s) - overrides version in Stream
size_t    write (uint8_t *b, size_t n) - overrides version in Stream
size_t    writeln (const char *s)
size_t    writeln ()
Logged

Guildford, UK
Offline Offline
Full Member
***
Karma: 0
Posts: 218
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I posted a new beta, SerialPortBeta20120103.zip, here http://code.google.com/p/beta-lib/downloads/list
I've tried it out and it is faster when using RAM strings, but to save space most of my debug messages are coming from flash memory.
Could I request 1 additional function that takes a __FlashStringHelper as an argument can copies it directly into the ring buffer?
At present I'm copying from __FlashStringHelper to a RAM buffer and then passing on to SerialPort. Still faster than before but could be faster still.

Not seen any bugs yet.

Iain
Logged

Pages: 1 2 [3] 4 5 ... 7   Go Up
Jump to: