Arduino Forum upgrade scheduled for Monday, October 20th, 11am-4pm (CEST). Sorry for the inconvenience!
Pages: [1] 2   Go Down
Author Topic: AFSoftwareSerial w/ internal clock 8MHz  (Read 5459 times)
0 Members and 1 Guest are viewing this topic.
Montreal
Offline Offline
Newbie
*
Karma: 0
Posts: 22
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm working on a project with low-voltage devices to be put in nature:
http://absences.sofianaudry.com/

I would like to implement a network of objects able to share information with several input/output. So I need software serial. However, I find the SoftwareSerial library from Arduino not really interesting as it doesn't have any buffer/interrupt.

I discovered and tested the AFSoftwareSerial from ladyada and it suits my needs perfectly except for one thing: I run with low voltage, so I use ATMega168V10 chips with internal clock so it runs at 8 MHz (same as lilypad). The AFSoftwareSerial is hardcoded very specifically to run at 16MHz.

In one of her replies, ladyada suggests it's possible to implement it.

Looking at AFSoftwareSerial.cpp I found out the critical section:
Code:
#if (F_CPU == 16000000)
void whackDelay(uint16_t delay) {
  uint8_t tmp=0;

  asm volatile("sbiw    %0, 0x01 \n\t"
           "ldi %1, 0xFF \n\t"
           "cpi %A0, 0xFF \n\t"
           "cpc %B0, %1 \n\t"
           "brne .-10 \n\t"
           : "+r" (delay), "+a" (tmp)
           : "0" (delay)
           );
}
#endif

I decided to create this post to address this specific issue. I don't know much about ASM code so I don't know how to code it for F_CPU=8000000. Any suggestions?
Logged

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

There's actually another issue I just found out about regarding AFSoftwareSerial, which is that the receive buffer is static. Which means that if I got several AFSoftSerial objects they will all have the same buffer.

This is another issue that would need to get addressed IMHO since one of the main objectives behind SoftwareSerial is to be able to have several serial communication channels.
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 239
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

if someone has the time to implement all these changes, they will be wrapped into the project
Logged

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

I'm currently working on a patch for separate buffers for each receive pin. I'll send it here once done.

I would need help wrt whackDelay(uint16_t delay) however, to get it to work with 8MHz. I'm just not familiar with assembly so I don't know what you intended to do in that function (and why you needed to use assembly code, for that matter).

Thanks for the great code.
Logged

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

Here are the patches:
Code:
--- AFSoftSerial.h.orig.h      2008-04-05 14:37:46.000000000 -0400
+++ AFSoftSerial.h      2008-10-06 20:26:17.000000000 -0400
@@ -24,12 +24,21 @@
 
 uint16_t whackDelay2(uint16_t delay);
 
-static void recv(void);
-
 class AFSoftSerial
 {
+  public:
+    static char **_receive_buffers;
+    static uint8_t _receive_buffers_index[14];
+    static int _bitDelay;
+    static long _baudRate;
+    static void recv(int pin);
+
   private:
-    long _baudRate;
+    uint8_t _receivePin;
+    uint8_t _transmitPin;
+
+    char *_receive_buffer;
+//    char _receive_buffer[AFSS_MAX_RX_BUFF];
     void printNumber(unsigned long, uint8_t);
    
   public:

Code:
--- AFSoftSerial.cpp.orig.cpp      2008-04-05 15:25:50.000000000 -0400
+++ AFSoftSerial.cpp      2008-10-06 22:55:26.000000000 -0400
@@ -21,6 +21,8 @@
  * Includes
  ******************************************************************************/
 #include <avr/interrupt.h>
+#include <stdlib.h>
+#include <string.h>
 #include "WConstants.h"
 #include "AFSoftSerial.h"
 
@@ -33,13 +35,6 @@
 /******************************************************************************
  * Statics
  ******************************************************************************/
-static uint8_t _receivePin;
-static uint8_t _transmitPin;
-static int _bitDelay;
-
-static char _receive_buffer[AFSS_MAX_RX_BUFF];
-static uint8_t _receive_buffer_index;
-
 #if (F_CPU == 16000000)
 void whackDelay(uint16_t delay) {
   uint8_t tmp=0;
@@ -55,43 +50,45 @@
 }
 #endif
 
-/******************************************************************************
- * Interrupts
- ******************************************************************************/
+char **AFSoftSerial::_receive_buffers = 0;
+uint8_t AFSoftSerial::_receive_buffers_index[14];
+int AFSoftSerial::_bitDelay = 0;
+long AFSoftSerial::_baudRate = 0;
 
-SIGNAL(SIG_PIN_CHANGE0) {
-  if ((_receivePin >=8) && (_receivePin <= 13)) {
-    recv();
-  }
-}
-SIGNAL(SIG_PIN_CHANGE2)
-{
-  if (_receivePin <8) {
-    recv();
-  }
-}
-
-
-void recv(void) {
+void AFSoftSerial::recv(int pin) {
   char i, d = 0;
-  if (digitalRead(_receivePin))
+  if (digitalRead(pin))
     return;       // not ready!
   whackDelay(_bitDelay - 8);
   for (i=0; i<8; i++) {
     //PORTB |= _BV(5);
     whackDelay(_bitDelay*2 - 6);  // digitalread takes some time
     //PORTB &= ~_BV(5);
-    if (digitalRead(_receivePin))
+    if (digitalRead(pin))
       d |= (1 << i);
    }
   whackDelay(_bitDelay*2);
-  if (_receive_buffer_index >=  AFSS_MAX_RX_BUFF)
+  if (AFSoftSerial::_receive_buffers_index[pin] >= AFSS_MAX_RX_BUFF)
     return;
-  _receive_buffer[_receive_buffer_index] = d; // save data
-  _receive_buffer_index++;  // got a byte
+  AFSoftSerial::_receive_buffers[pin][AFSoftSerial::_receive_buffers_index[pin]] = d; // save data
+  AFSoftSerial::_receive_buffers_index[pin]++;  // got a byte
 }
-  
 
+/******************************************************************************
+ * Interrupts
+ ******************************************************************************/
+
+SIGNAL(SIG_PIN_CHANGE0) {
+  for (int pin=8; pin<=13; pin++)
+    if (AFSoftSerial::_receive_buffers[pin])
+      AFSoftSerial::recv(pin);
+}
+SIGNAL(SIG_PIN_CHANGE2)
+{
+  for (int pin=0; pin<8; pin++)
+    if (AFSoftSerial::_receive_buffers[pin])
+      AFSoftSerial::recv(pin);
+}  
 
 /******************************************************************************
  * Constructors
@@ -99,16 +96,39 @@
 
 AFSoftSerial::AFSoftSerial(uint8_t receivePin, uint8_t transmitPin)
 {
-  _receivePin = receivePin;
-  _transmitPin = transmitPin;
-  _baudRate = 0;
+  setRX(receivePin);
+  setTX(transmitPin);
 }
 
 void AFSoftSerial::setTX(uint8_t tx) {
   _transmitPin = tx;
+  pinMode(_transmitPin, OUTPUT);
+  digitalWrite(_transmitPin, HIGH);
 }
+
 void AFSoftSerial::setRX(uint8_t rx) {
   _receivePin = rx;
+  if (!_receive_buffers) {
+    _receive_buffers = (char**)malloc(14 * sizeof(char*));
+    memset(_receive_buffers, 0, 14*sizeof(char*));
+  }
+  if (!_receive_buffers[_receivePin])
+    _receive_buffers[_receivePin] = (char*)malloc(AFSS_MAX_RX_BUFF * sizeof(char));
+  _receive_buffer = _receive_buffers[_receivePin];
+  _receive_buffers_index[_receivePin] = 0;
+
+  pinMode(_receivePin, INPUT);
+  digitalWrite(_receivePin, HIGH);  // pullup!
+
+   if (_receivePin < 8) {
+     // a PIND pin, PCINT16-23
+     PCMSK2 |= _BV(_receivePin);
+     PCICR |= _BV(2);
+  } else if (_receivePin <= 13) {
+    // a PINB pin, PCINT0-5
+    PCICR |= _BV(0);    
+    PCMSK0 |= _BV(_receivePin-8);
+  }
 }
 
 /******************************************************************************
@@ -117,12 +137,6 @@
 
 void AFSoftSerial::begin(long speed)
 {
-  pinMode(_transmitPin, OUTPUT);
-  digitalWrite(_transmitPin, HIGH);
-
-  pinMode(_receivePin, INPUT);
-  digitalWrite(_receivePin, HIGH);  // pullup!
-
   _baudRate = speed;
   switch (_baudRate) {
   case 115200: // For xmit -only-!
@@ -143,17 +157,7 @@
     _bitDelay = 470; break;
   default:
     _bitDelay = 0;
-  }    
-
-   if (_receivePin < 8) {
-     // a PIND pin, PCINT16-23
-     PCMSK2 |= _BV(_receivePin);
-     PCICR |= _BV(2);
-  } else if (_receivePin <= 13) {
-    // a PINB pin, PCINT0-5
-    PCICR |= _BV(0);    
-    PCMSK0 |= _BV(_receivePin-8);
-  }
+  }
 
   whackDelay(_bitDelay*2); // if we were low this establishes the end
 }
@@ -162,22 +166,22 @@
 {
   uint8_t d,i;
 
-  if (! _receive_buffer_index)
+  if (! _receive_buffers_index[_receivePin])
     return -1;
 
   d = _receive_buffer[0]; // grab first byte
   // if we were awesome we would do some nifty queue action
   // sadly, i dont care
-  for (i=0; i<_receive_buffer_index; i++) {
+  for (i=0; i<_receive_buffers_index[_receivePin]; i++) {
     _receive_buffer[i] = _receive_buffer[i+1];
   }
-  _receive_buffer_index--;
+  _receive_buffers_index[_receivePin]--;
   return d;
 }
 
 uint8_t AFSoftSerial::available(void)
 {
-  return _receive_buffer_index;
+  return _receive_buffers_index[_receivePin];
 }
 
 void AFSoftSerial::print(uint8_t b)
@@ -319,3 +323,4 @@
   for (; i > 0; i--)
     print((char) (buf[i - 1] < 10 ? '0' + buf[i - 1] : 'A' + buf[i - 1] - 10));
 }
+
Logged

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

Notice that these patches will only patch the "separate buffers" problem (which goes beyond the original scope of this topic).

The rest still needs to be addressed.
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is a hack you can try, why not call softwareSerial.begin  with double the baud rate, this will be pretty close to the correct speed at the slower baud rates.

If that doesn't work at the baud rate you need, or you want something a little less hacky, you could tweek the values for  _bitDelay in the case statement in the begin() method of AFSoftwareSerial.cpp.
« Last Edit: October 07, 2008, 04:41:04 am by mem » Logged

0
Offline Offline
God Member
*****
Karma: 1
Posts: 513
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I like that smiley  Very clever if it works.
Logged

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

This is also what is suggested here:
http://forums.ladyada.net/viewtopic.php?t=6793&sid=5c6c655ebd8e259c3407f25b58edc1b9

The post also contains a nice analysis of the assembly code.

I'm giving it a try right now, I'll keep you posted.
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Unfortunately that link doesn't have a suggestion for changing the software so the hack isn't necessary.

If someone here wants to tinker, I suggest that the #if is removed from the asm code and put in case statement in the method that calculates _bitdelay. At the slower baud rates, _bitdelay will be half its current value on an 8mhz clock. As the baud rate increases it will be increasingly less than half (as the time delay of digitalWrite becomes more significant).

This could be a fun project for someone with an oscilloscope and an 8mhz board.
Logged

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

I just did exactly that (removed the #if) and added the following line:
Code:

  // Adjust the _bitDelay.
  _bitDelay = ((unsigned long)_bitDelay * F_CPU) / 16000000L;

in method begin.

It works fine with an internal clock @ 8 MHz! I am even able to communicate without any problem with Arduino Diecimila boards (that run at 16 MHz).
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Good to hear you have it working, but I expect that it will fail at high baud rates because the delay will need to be reduced to compensate for the 4us or more it takes digitalWrite when running on an 8mhz clock.  I think some of the higher baud case statements may need a #if statment to compensate for this, but you may need a scope to find the best values.

How high a baud rate have you tested it?
« Last Edit: October 08, 2008, 01:25:23 am by mem » Logged

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

You seem to be right in the problem you mention, I believe I'm having problems of that kind now. I need to run some more tests. I'll keep you posted.
Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 12
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

tatien: can you post a patched version of the library (with multiple buffer support)?  The patches you posted didn't seem to apply cleanly to the the version of the library I downloaded.
Logged

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

I have put the full version online attached to this post:

http://absences.sofianaudry.com/en/node/18

Coming soon: I will put the project on a SVN.
Logged

Pages: [1] 2   Go Up
Arduino Forum upgrade scheduled for Monday, October 20th, 11am-4pm (CEST). Sorry for the inconvenience!
Jump to: