Tuesday, February 19, 2008

Arduino Software Serial Correction

Looking through the Arduino libraries, I found "Software Serial" to be very useful. Serial communications in software (bit banging) should be checked for correct bit timing especially if multiple baud rates are used. Optimizations in the compiler, 16 bit numbers, and interrupts can create bit timing errors that will reek havok in communications.

So I decided to check out the Arduino's (receive) bit timing on a scope.

The picture below shows the receive routine (incorrect) timing,

When receiving, you try and read the pin at the middle of the bit. Which is, once you see the start bit, wait 50% of the bit time, then take a reading. Notice the middle of the scope pic above how the pin is being read incorrectly. It sampled off the edge of the bit resulting in an incorrect read on the 3rd bit.

With the corrected (fixed) bit time,


Notice the reads much more closer to the center?

That was for 9600 baud. The following code should receive correctly for bauds up to and including 14400

int SoftwareSerial::read()
{
int val = 0;
int bitDelay = _bitPeriod;// - clockCyclesToMicroseconds(50);

// one byte of serial data (LSB first)
// ...--\ /--\/--\/--\/--\/--\/--\/--\/--\/--...
// \--/\--/\--/\--/\--/\--/\--/\--/\--/
// start 0 1 2 3 4 5 6 7 stop


while (digitalRead(_receivePin));

// confirm that this is a real start bit, not line noise
if (digitalRead(_receivePin) == LOW) {

// frame start indicated by a falling edge and low start bit
// jump to the middle of the low start bit

delayMicroseconds((bitDelay/2)-25);

//digitalWrite(3,LOW);
//digitalWrite(3,HIGH);

// offset of the bit in the byte: from 0 (LSB) to 7 (MSB)
for (int offset = 0; offset < 8; offset++) {
// jump to middle of next bit
delayMicroseconds(bitDelay-14);

//digitalWrite(3,LOW);
//digitalWrite(3,HIGH);

// read bit
val |= digitalRead(_receivePin) << offset;
}

delayMicroseconds(_bitPeriod-8);

return val;
}

return -1;
}

No comments: