通过RS485进行通信

Communication via RS485

本文关键字:通信 RS485 通过      更新时间:2023-10-16

我有一台单板计算机通过RS485连接到另一台设备。计算机应向设备发送请求并接收响应(使用与设备相关的协议)。我可以毫无问题地发送消息,设备也可以接收消息(例如,我可以更改设备的参数)。当我想从设备中读取参数时,就会出现问题。在这种情况下,我得到了错误的回复(错误的字符,转移的消息,不完整的消息,…)

这是我的初始化代码:

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;
}

发送代码:

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;
}

接收代码:

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;
}

功能aux_gpio_write_settings()aux_gpio_read_settings()用于设置UART(通过GPIO)。RS485可以发送或接收数据。

如果我在linux台式机上使用该代码,它会正常工作,因为USB/RS485适配器会自动在发送和接收模式之间切换。在我的单板电脑上,我必须手动操作。正因为如此,我认为设置GPIO和接收响应会导致时间问题。我该如何处理这个问题?

可能是由于receiveFrame读取循环中的一个错误。当接收到的数据量小于所需的数据量时,会减少循环中下次接收的数据量,但当使用与上一个循环相同的指针时,会覆盖读取的第一个数据。你需要增加指针以及减少大小:

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

发送数据时也会遇到类似的问题,你一遍又一遍地发送相同的数据,只是每次发送的数据更少。

我还建议您实际检查错误(当bytesReceived < 0时),并适当地处理这些情况。