I've not used the Arduino, but have written efficient soft UARTs on quite a number of platforms. Which approach is best on a given platform will depend upon what types of bit-munging operations it can perform most efficiently. I would offer a few suggestions, though:
- Assign maximum priority to the serial-polling ISR, and run it at preferably 3x the desired data rate. Sample all your inputs and write all your outputs at the start of this interrupt routine, and then figure out what the outputs should be for the next pass. This will help to minimize any timing skew that might otherwise be caused by variable interrupt processing time.
- For the receiver, instead of using a state machine as such, it may be helpful to effectively shift the incoming data into a big shift register. If the pattern of bits indicates a byte was received, grab the data and clear the appropriate bytes.
... near start of interrupt (for consistent timing)
shiftreg >>= 1;
if (IN_PORT)
shiftreg |= 0x20000000;
... other interrupt logic, then...
if ((shiftreg & 0x20000007) == 0x20000001)
{
int result = 0;
if (shiftreg & 0000000040) result |= 1; // Note: constants are OCTAL!
if (shiftreg & 0000000400) result |= 2;
if (shiftreg & 0000004000) result |= 4;
if (shiftreg & 0000040000) result |= 8;
if (shiftreg & 0000400000) result |= 16;
if (shiftreg & 0004000000) result |= 32;
if (shiftreg & 0040000000) result |= 64;
if (shiftreg & 0400000000) result |= 128;
// Do something appropriate with result, then...
shiftreg |= 0x3FFFFFFFF;
}
else if (shiftreg = 1)
{
... Do something with long-break (will be detected exactly once)
}
Note that while the worst-case time may be significant, the normal-case time will be quite fast. Further, when an incoming byte is detected, one could copy it to another word of memory and do the bit-munging on a later interrupt. Since serial-transmit will only need to do something every third interrupt, the bit-munging could be done on interrupts where the serial-transmit routine doesn't run.
I tried a voltmeter over GND and DI10 and while measuring it showed
0V.
That doesn't sound right.
When a UART is not transmitting any data, it stays in the idle "1" state.
I expected the wires connected to the Arduino to be so-called RS232TTL levels of +5V in the "1" state and close to GND in the "0" state.
(d)
When the UART is transmitting lots of data, a multimeter typically shows some sort of average voltage between the "1" state and the "0" state, bouncing around 2 V to 4 V.
Perhaps a power or data line got disconnected or wired up wrong?
As a side note, if I remove the pinMode(rxPin, INPUT) line then
nothing is ever received (and rf.available() is always 0).
That's very unexpected.
Most Arduino documentation says things like
"Arduino (Atmega) pins default to inputs, so they don't need to be explicitly declared as inputs with pinMode()." (a)
Some tutorials for SoftwareSerial suggest explicitly declaring the TX pins to be output.
(b)
Perhaps whatever is listening to "txPin" is picking up noise, making it do something unexpected?
Most Arduino tutorials seem to use 9600 bps for the hardware Serial uart.
(c)
rf.available() is always > 0 (and also becomes 63)
How could you possibly know that?
I'm beginning to suspect the code in your Arduino is some program other than what you posted.
What happens when you test exactly the same program, but with a known serial source?
For example,
rather than connect Arduino D10 (your SoftwareSerial rxPin) to the radio, instead connect D10 to Arduino D0 (the data you type in your PC's serial monitor), and type a few words.
What happens then?
Maybe the SoftwareSerial works fine with normal UART data,
but it can't handle the high-frequency glitches common in low-cost radio receivers.
In that case, maybe it would work better to
- (a) connect the Arduino hardware UART (D0 Rx and D1 Tx) to the radio, and the SoftwareSerial to your debugging serial monitor. Or
- (b) use more sophisticated hardware that does clock recovery, etc. like the HopeRF RFM12B as used in the JeeNode and the Moteino, or
- (c) use more sophisticated software, such as the protocol Roman Black invented and describes in "RF modules made easy" or the frequency-shift keying system developed by Tom Boyd in "Pulse train detection with an Arduino".
Some test code:
#include <SoftwareSerial.h>
#define rxPin 10
#define txPin 11
SoftwareSerial rf(rxPin, txPin);
void setup() {
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
Serial.begin(9600);
rf.begin(2400);
Serial.println("Hello, I was compiled " __DATE__ );
}
void loop() {
if( rf.available() ){
int incomingByte = rf.read();
Serial.print(incomingByte, DEC);
Serial.print(" ");
Serial.println( rf.available(), DEC);
}
}
Best Answer
The reason behind the discrepancy in documentation is that the documentation refers to two different versions of the software and it is out of date.
In Arduino version 0023 and below, SoftSerial was a very basic library written by David Mellis. It just uses millis(), digitalRead/Write and blocks when sending or receiving. This means it can work on any pin. However, it works badly and slowly. I would strongly recommend avoiding this.
In Arduino version 1 and above, SoftSerial was replaced with NewSoftSerial. This uses interrupts and timers, and is far more efficient. This limits the pins that it can work on though to ones that have pin change interrupts.
With respect to the contradictory pins; there are two types of interrupt pin on the ATmega2560 - external interrupts (INT7 - INT0) and pin change interrupts (PCINT23 - PCINT0). External interrupts are more complex and can be configured to trigger on rising and/or falling edges. Pin change interrupts trigger as long as any change occurs.
The long list of:
are pin change interrupts which are what are required for NewSoftSerial (called SoftSerial in Arduino 1.0 and above).
The short list of:
are external interrupts.
You can see these on the pin mapping diagram for the ATmega2560 (which is correct, AFAIK). Function of the interrupt pins is described on page 105 onwards of the datasheet.
There are 4 hardware USARTs on the ATmega2560. These are far easier to use and far more efficient than any software serial library. They are on pins 0/1, 14/15, 16/17, 18/19.
There is also AltSoftSerial which is better than NewSoftSerial in terms of performance, but is even more limited in pin choice.
At this stage, I have to ask "why those pins?". Could the design not change?