0

So I'm trying to understand a read method. In this method,

// Read data from buffer
int SoftwareSerial::read()
{
if (!isListening())
 return -1;
// Empty buffer?
if (_receive_buffer_head == _receive_buffer_tail)
 return -1;
// Read from "head"
uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
_receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF;
return d;
}

it looks it receives buffer head. But in the write only a 1 or 0 is being sent as the data packet's header. Nothing else. So how does the arduino know how big of an buffer to create? also, there is no stop bit being transmitted, so how does it look at a buffer tail? Or is this all created regardless of the transmitter? and then, all the values just get read into the buffer, and then read again from there? Also I don't see a delay in this read method. Wouldn't that cause errors in the data flow? How is that being handled?

asked Oct 2, 2015 at 20:37

1 Answer 1

1

That read method just retrieves bytes that have already been received from an internal buffer.

The buffer is pre-allocated to a specific size (64 bytes) and that is how many bytes can be received before you start to lose data as it gets overwritten. You have to be sure to read your data from the buffer before it overflows.

The function that actually receives each UART bit from the IO port, builds them into bytes, and stores them in the internal buffer, is this one:

void SoftwareSerial::recv()
{
#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Preserve the registers that the compiler misses
// (courtesy of Arduino forum user *etracer*)
 asm volatile(
 "push r18 \n\t"
 "push r19 \n\t"
 "push r20 \n\t"
 "push r21 \n\t"
 "push r22 \n\t"
 "push r23 \n\t"
 "push r26 \n\t"
 "push r27 \n\t"
 ::);
#endif
 uint8_t d = 0;
 // If RX line is high, then we don't see any start bit
 // so interrupt is probably not for us
 if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
 {
 // Wait approximately 1/2 of a bit width to "center" the sample
 tunedDelay(_rx_delay_centering);
 DebugPulse(_DEBUG_PIN2, 1);
 // Read each of the 8 bits
 for (uint8_t i=0x1; i; i <<= 1)
 {
 tunedDelay(_rx_delay_intrabit);
 DebugPulse(_DEBUG_PIN2, 1);
 uint8_t noti = ~i;
 if (rx_pin_read())
 d |= i;
 else // else clause added to ensure function timing is ~balanced
 d &= noti;
 }
 // skip the stop bit
 tunedDelay(_rx_delay_stopbit);
 DebugPulse(_DEBUG_PIN2, 1);
 if (_inverse_logic)
 d = ~d;
 // if buffer full, set the overflow flag and return
 if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head)
 {
 // save new data in buffer: tail points to where byte goes
 _receive_buffer[_receive_buffer_tail] = d; // save new byte
 _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
 }
 else
 {
#if _DEBUG // for scope: pulse pin as overflow indictator
 DebugPulse(_DEBUG_PIN1, 1);
#endif
 _buffer_overflow = true;
 } 
 } 
#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Restore the registers that the compiler misses
 asm volatile(
 "pop r27 \n\t"
 "pop r26 \n\t"
 "pop r23 \n\t"
 "pop r22 \n\t"
 "pop r21 \n\t"
 "pop r20 \n\t"
 "pop r19 \n\t"
 "pop r18 \n\t"
 ::);
#endif
}

To show you how the timing of the routine works, here is a little diagram:

enter image description here

answered Oct 2, 2015 at 20:43
5
  • Hello, i had a couple questions about that above code. So, when it says, " If RX line is high, then we don't see any start bit, so interrupt is probably not for us" in the comments, does that mean that if there is no startbit being detected, interrupts are turned off and if there is a startbit, then it is turned on? Also, what does 1/2 of a bit width refer to? Is that a time reference? Commented Oct 7, 2015 at 0:33
  • I know this is a old post, but can you clarify here? Commented Mar 31, 2016 at 19:48
  • 1
    The recv() function is triggered by the change interrupt (PCI) detecting the RX line has changed state - that is gone HIGH to LOW, or LOW to HIGH. It responds to both changes, and also to other pins as well as the RX pin (if others are configured for PCI). So it checks to see if the RX pin is LOW (or HIGH with inverse logic) so it knows if it's an active start bit or not. If it is then it goes on to receive the entire byte, which it does by the use of tuned delay times. Commented Mar 31, 2016 at 19:54
  • 1
    Each bit has a specific time of 10/baud seconds (9600 baud is 10/9600 = 1.042ms) and that is how long each bit takes to transmit or receive. By waiting for half that time first the delay of 1.042ms falls in the middle of a received bit so it can reliably detect the level of that bit. If it didn't do that it would be reading the state on the border between two bits and it would be anyone's guess what the value would be that it would read, Commented Mar 31, 2016 at 19:54
  • I have added a diagram to my answer to make it even clearer. Commented Mar 31, 2016 at 20:04

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.