Montreal
Offline
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« on: October 06, 2008, 04:24:30 pm » |
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: #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
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #1 on: October 06, 2008, 05:02:53 pm » |
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
Full Member
Karma: 0
Posts: 239
Arduino rocks
|
 |
« Reply #2 on: October 06, 2008, 06:45:55 pm » |
if someone has the time to implement all these changes, they will be wrapped into the project
|
|
|
|
|
Logged
|
|
|
|
|
Montreal
Offline
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #3 on: October 06, 2008, 06:59:41 pm » |
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
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #4 on: October 06, 2008, 10:13:32 pm » |
Here are the patches: --- 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:
--- 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
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #5 on: October 06, 2008, 10:14:50 pm » |
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
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #6 on: October 07, 2008, 04:38:48 am » |
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
God Member
Karma: 0
Posts: 507
|
 |
« Reply #7 on: October 07, 2008, 11:46:57 am » |
I like that  Very clever if it works.
|
|
|
|
|
Logged
|
|
|
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #9 on: October 08, 2008, 01:07:37 am » |
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
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #10 on: October 08, 2008, 01:13:32 am » |
I just did exactly that (removed the #if) and added the following line: // 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
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #11 on: October 08, 2008, 01:20:57 am » |
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
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #12 on: October 08, 2008, 10:12:19 am » |
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
Faraday Member
Karma: 8
Posts: 3532
|
 |
« Reply #13 on: October 25, 2008, 09:24:34 am » |
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
Newbie
Karma: 0
Posts: 22
Arduino rocks
|
 |
« Reply #14 on: November 01, 2008, 03:46:30 am » |
I have put the full version online attached to this post: http://absences.sofianaudry.com/en/node/18Coming soon: I will put the project on a SVN.
|
|
|
|
|
Logged
|
|
|
|
|
|