DBPST
DBPST

Reputation: 23

Communication via RS485

I have a single-board computer connected to another device via RS485. The computer should send a request to the device and receive a response (using a device dependent protocol). I can send messages without any problems and the device receives them (e.g. I can change parameters of the device). The problem occurs when I want to read parameters from the device. In this case I'm getting wrong responses (wrong characters, shifted messaged, incomplete messages, ...).

Here is my initalization code:

Bool
SerialCommunicator::initPort()
{
if (isInitialized_)
    return true;

if (!paramSet())
    return false;

bzero( &termIO_, sizeof ( struct termios ));
termIO_.c_iflag    |= IGNBRK | IGNPAR;
termIO_.c_cflag    |= CREAD | CLOCAL;
termIO_.c_cflag    |= CS8;
termIO_.c_oflag    |= 0;
termIO_.c_lflag    |= 0;

termIO_.c_cc[VTIME]    = 0;
termIO_.c_cc[VMIN]     = 13; // number of frame characters

String path("/dev/tty" + portSuffix_);
serHandle_ = open(path.c_str(), O_RDWR /*| O_NOCTTY*/);
if (serHandle_ > -1)
{
    isInitialized_ = (cfsetispeed(&termIO_, B19200) == 0)
                        && (cfsetospeed(&termIO_, B19200) == 0);
    isInitialized_ = isInitialized_ && (tcsetattr(serHandle_, TCSANOW, &termIO_) == 0);


    return isInitialized_;
}
else
    return false;
}

Send code:

Bool
SerialCommunicator::sendFrame(UByte *_frame, UInt _size)
{
FD_ZERO( &wrFd_ );
FD_ZERO( &rdFd_ );
FD_SET( serHandle_, &wrFd_);
FD_SET( serHandle_, &rdFd_);

Int retVal;
aux_gpio_write_settings();
retVal = select(serHandle_+1, &rdFd_, &wrFd_, NULL, &timeval_);
if (retVal > 0)
{
  if( FD_ISSET(serHandle_, &wrFd_) )
  {

    UInt    bytesToSend     = _size;
    UInt    bytesSent       = 0;
    UInt    bytesSentTotal  = 0;
    while ( bytesToSend > 0 )
    {
        bytesSent   = write( serHandle_, _frame + bytesSentTotal, bytesToSend );
        if (bytesSent > 0)
        {
            bytesToSend     -= bytesSent;
            bytesSentTotal  += bytesSent;
        }

    }
    aux_gpio_read_settings();
    tcflush(serHandle_, TCIOFLUSH);
    return true;
  }
}
usleep(SLEEPTIME);
return false;
}

Receive code:

Bool
SerialCommunicator::receiveFrame(UByte *_frame, UInt _size)
{
FD_ZERO( &rdFd_ );
FD_ZERO( &wrFd_ );
FD_SET( serHandle_, &rdFd_ );
FD_SET( serHandle_, &wrFd_ );

Bool retVal;
aux_gpio_read_settings();
retVal = select(serHandle_+1, &rdFd_, &wrFd_, NULL, &timeval_);
if (retVal > 0)
{
  if( FD_ISSET(serHandle_, &rdFd_) )
  {

    UInt    bytesToReceive      = _size;
    UInt    bytesReceived       = 0;
    UInt    bytesReceivedTotal  = 0;
    while ( bytesToReceive > 0 )
    {
        bytesReceived   = read( serHandle_, _frame + bytesReceivedTotal, bytesToReceive );
        if (bytesReceived > 0)
        {
            bytesToReceive      -= bytesReceived;
            bytesReceivedTotal  += bytesReceived;
        }
    }
    return true;
  }
}
return false;
}

The functions aux_gpio_write_settings() and aux_gpio_read_settings() are used to set the UART(via GPIOs) s.t. the RS485 can send or receive data.

If I use the code on my linux desktop computer it works fine since the USB/RS485 adapter switches automatically between sending and receiving mode. On my single-board computer I have to do it manually. Because of that, I think that setting the GPIOs and receiving the response cause a timing problem. How can I handle this problem?

Upvotes: 2

Views: 5289

Answers (1)

Some programmer dude
Some programmer dude

Reputation: 409136

It might be with a bug you have in the receiveFrame read-loop. When you receive less than the wanted amount of data, you decrease the amount of data to receive the next time in the loop, but then when using the same pointer as the previous loop you overwrite the first data you read. You need to increase the pointer as well as decrease the size:

bytesReceived   = read( serHandle_, _frame, bytesToReceive );
if (bytesReceived > 0)
{
    bytesToReceive -= bytesReceived;
    _frame += bytesReceived;
}

You have a similar problem when sending data, you send the same data over and over again, just less of it each time.

I also recommend you to actually check for errors (when bytesReceived < 0), and handle those cases appropriately.

Upvotes: 1

Related Questions