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?

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

  • 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? – Jonathan Oct 7 '15 at 0:33
  • I know this is a old post, but can you clarify here? – Jonathan Mar 31 '16 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. – Majenko Mar 31 '16 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, – Majenko Mar 31 '16 at 19:54
  • Genius.... That's pretty clever. Thank you. – Jonathan Mar 31 '16 at 20:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.