SoftwareSerial 8-o-1 problem

Hi!

I'm trying to use Arduino's SoftwareSerial to communicate with a RS-422 device.
I use SN75179BP to convert 422 to TTL.

The device is 8bit - odd parity - 1 stop bit.
I've tried to mod the softwareserial library using:
http://arduino.cc/forum/index.php/topic,108097.0.html

Changed the parity bit calculation in the write function to odd instead of even.

Couple of problems:
First, if I use the exact mod suggested by pylon [the 4th comment] on the recv function, while trying to upload the sketch I get:

/var/folders/st/vmhkh5t57592ln_zhkyfq2q80000gn/T//cct5EjNP.s: Assembler messages:
/var/folders/st/vmhkh5t57592ln_zhkyfq2q80000gn/T//cct5EjNP.s:243: Error: register r24, r26, r28 or r30 required
/var/folders/st/vmhkh5t57592ln_zhkyfq2q80000gn/T//cct5EjNP.s:263: Error: register r24, r26, r28 or r30 required

Have no idea why.
but if I leave the function as is, and only changing this:

  // skip the parity bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    // skip the stop bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

to this:

 // skip the 2 stop bits
    tunedDelay(_rx_delay_stopbit*2);
    DebugPulse(_DEBUG_PIN2, 1);

I can upload the sketch and even start receiving true and correct data on my serial.

The second problem, is that no matter what I do - I can't get the write function to work.
The device doesn't respond to any input except that in the second I try to send something to that device over the serial, I stop getting nice and clean data on my recieve channel, instead I get clutter...

[BTW, in that situation - if send some "new line" 0D, it brings back the normal data on the recieve channel...]

*-I'm using Arduino 1.0.1

EDIT: I don't know if it's important or not, but I'm using inverse_logic for the receive function to work.

Any ideas will be more than welcomed,
Liad.

Changed the parity bit calculation in the write function to odd instead of even.

Show us your changed code to check the implementation.

First, if I use the exact mod suggested by pylon [the 4th comment] on the recv function, while trying to upload the sketch I get:

For what board type are you compiling this?

but if I leave the function as is, and only changing this:

Your change is just skipping the parity bit and the stop bit. This works for the receiving part but it's clear that the transmitting part is not working that way.

pylon:
Show us your changed code to check the implementation.

Just changed

  uint8_t p = 0;

to

  uint8_t p = 1;

For what board type are you compiling this?

Arduino UNO.

Your change is just skipping the parity bit and the stop bit. This works for the receiving part but it's clear that the transmitting part is not working that way.

Yea, I know...I just wanted the receiving part to work to see if my device will show any changes while playing with the write function.

Here's the code for the 8o1 variant.

//
// The receive routine called by the interrupt handler
//
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 parity bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    // skip the 2 stop bits
    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
}

and the write part:

size_t SoftwareSerial::write(uint8_t b)
{
  if (_tx_delay == 0) {
    setWriteError();
    return 0;
  }

  uint8_t oldSREG = SREG;
  cli();  // turn off interrupts for a clean txmit

  // Write the start bit
  tx_pin_write(_inverse_logic ? HIGH : LOW);
  tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
  uint8_t p = 0;
  uint8_t t;
  for (t = 0x80; t; t >>= 1)
    if (b & t) p++;
  

  // Write each of the 8 bits
  if (_inverse_logic)
  {
    for (byte mask = 0x01; mask; mask <<= 1)
    {
      if (b & mask) // choose bit
        tx_pin_write(LOW); // send 1
      else
        tx_pin_write(HIGH); // send 0
    
      tunedDelay(_tx_delay);
    }
    // parity
    if (p & 0x01)
      tx_pin_write(LOW); // send 1
    else 
      tx_pin_write(HIGH); // send 0
    tunedDelay(_tx_delay);

    tx_pin_write(LOW); // restore pin to natural state
  }
  else
  {
    for (byte mask = 0x01; mask; mask <<= 1)
    {
      if (b & mask) // choose bit
        tx_pin_write(HIGH); // send 1
      else
        tx_pin_write(LOW); // send 0
    
      tunedDelay(_tx_delay);
    }
    // parity
    if (p & 0x01)
      tx_pin_write(HIGH); // send 1
    else 
      tx_pin_write(LOW); // send 0
    tunedDelay(_tx_delay);

    tx_pin_write(HIGH); // restore pin to natural state
  }

  SREG = oldSREG; // turn interrupts back on
  tunedDelay(_tx_delay);
  
  return 1;
}

The compiler errors you get indicate that you're working with MacOS X as the compilation machine. Correct?

I got the compilation error away on my machine by changing the following code too:

inline void SoftwareSerial8o1::tunedDelay(volatile uint16_t delay) { 
  volatile 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)
    );
    
}

Note the inserted volatile statements to keep the compiler from optimizing the variables away.

Hey,

Thank you for your quick response.
I compiled it with the new code but I still can't transmit normally.

The device is constantly sending data. [ASCII formated messegs] I can read it clearly.
Whey I send 1 character using the serial terminal. I stop getting normal data from the device and it doesn't do what it suppose to do if he got the correct data from me.

I took an oscilloscope to check the arduino's output and it outputs exactly what it should [I think]
For example, sending 0x01 - I get 1011111110 [inverse logic]

is it possible that there is a problem with the timing? bit size?

Thanks,
Liad.

Hey,

Problem solved!

I don't completely get it, but the minute I've changed the write function to non-inverse logic [With of course setting up the parity+stop bit correctly] - everything started working...

I'll keep investigate it to see exactly what's going on but it's nice to see it working.

Pylon, thanks a lot for that OSx workaround!

Liad.

Hello,

I'm not sure that my problems with SoftwareSerial has anything to do with this thread, but it sounds likely. I'm using MacOS 10.6.8 with Arduino 1.0.1.

When I make a bluetooth connection to my Sparkfun Bluetooth Mate Gold I can query the device correctly, and both sending commands and receiving chars works fine. However, when I try to make a small echo sketch the chars come back garbled.

Question and code is here: arduino - Making an echo sketch using SoftwareSerial and Bluetooth - Electrical Engineering Stack Exchange.

Help is needed!

Thanks.

I'm not sure that my problems with SoftwareSerial has anything to do with this thread, but it sounds likely

No to me, it doesn't. Stick to your own thread.

Hello, I need to use 8O1 using SoftwareSerial. I did like you wrote:

pylon:
Here's the code for the 8o1 variant.

//

// The receive routine called by the interrupt handler
//
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 parity bit
   tunedDelay(_rx_delay_stopbit);
   DebugPulse(_DEBUG_PIN2, 1);

// skip the 2 stop bits
   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
}




and the write part:



size_t SoftwareSerial::write(uint8_t b)
{
 if (_tx_delay == 0) {
   setWriteError();
   return 0;
 }

uint8_t oldSREG = SREG;
 cli();  // turn off interrupts for a clean txmit

// Write the start bit
 tx_pin_write(_inverse_logic ? HIGH : LOW);
 tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
 uint8_t p = 0;
 uint8_t t;
 for (t = 0x80; t; t >>= 1)
   if (b & t) p++;

// Write each of the 8 bits
 if (_inverse_logic)
 {
   for (byte mask = 0x01; mask; mask <<= 1)
   {
     if (b & mask) // choose bit
       tx_pin_write(LOW); // send 1
     else
       tx_pin_write(HIGH); // send 0
   
     tunedDelay(_tx_delay);
   }
   // parity
   if (p & 0x01)
     tx_pin_write(LOW); // send 1
   else
     tx_pin_write(HIGH); // send 0
   tunedDelay(_tx_delay);

tx_pin_write(LOW); // restore pin to natural state
 }
 else
 {
   for (byte mask = 0x01; mask; mask <<= 1)
   {
     if (b & mask) // choose bit
       tx_pin_write(HIGH); // send 1
     else
       tx_pin_write(LOW); // send 0
   
     tunedDelay(_tx_delay);
   }
   // parity
   if (p & 0x01)
     tx_pin_write(HIGH); // send 1
   else
     tx_pin_write(LOW); // send 0
   tunedDelay(_tx_delay);

tx_pin_write(HIGH); // restore pin to natural state
 }

SREG = oldSREG; // turn interrupts back on
 tunedDelay(_tx_delay);
 
 return 1;
}




The compiler errors you get indicate that you're working with MacOS X as the compilation machine. Correct?

I got the compilation error away on my machine by changing the following code too:



inline void SoftwareSerial8o1::tunedDelay(volatile uint16_t delay) {
 volatile 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)
   );
   
}




Note the inserted volatile statements to keep the compiler from optimizing the variables away.

But unfortunately I can't compile my project:

Arduino: 1.6.5 (Windows 7), Board: "Arduino Leonardo"

Build options changed, rebuilding all

Using library SoftwareSerial in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial 



C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10605 -DARDUINO_AVR_LEONARDO -DARDUINO_ARCH_AVR -DUSB_VID=0x2341 -DUSB_PID=0x8036 -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Leonardo" -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\leonardo -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial C:\Users\MateuszS\AppData\Local\Temp\build7824102489584515010.tmp\soft8e1.cpp -o C:\Users\MateuszS\AppData\Local\Temp\build7824102489584515010.tmp\soft8e1.cpp.o 

C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10605 -DARDUINO_AVR_LEONARDO -DARDUINO_ARCH_AVR -DUSB_VID=0x2341 -DUSB_PID=0x8036 -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Leonardo" -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\leonardo -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\utility C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.cpp -o C:\Users\MateuszS\AppData\Local\Temp\build7824102489584515010.tmp\SoftwareSerial\SoftwareSerial.cpp.o 

C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.cpp: In member function 'virtual size_t SoftwareSerial::write(uint8_t)':
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.cpp:431:26: error: 'XMIT_START_ADJUSTMENT' was not declared in this scope
   tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
                          ^
Error compiling.

I erased XMIT_START_ADJUSTMENT,but afterwards I have a lot of errors in compilation:

Enclosed please find my SoftwareSerial.cpp and .h. What should I write more to this file?

SoftwareSerial.cpp (13.5 KB)

SoftwareSerial.h (3.95 KB)