【问题标题】:Serial port in C++ on Linux. Can read and write on serial port happen simultaneously..?Linux 上 C++ 中的串行端口。串行端口上的读写可以同时发生..?
【发布时间】:2014-10-01 12:46:03
【问题描述】:

我正在做一个项目,该项目需要一台 Linux PC 从 UART 上的微控制器获取数据,为此我使用了一个已经可用的开源代码,用于 Linux 的 C++ 串行端口。 (基于Ros(机器人操作系统)的代码)

代码如下:

#define DEFAULT_BAUDRATE 115200
#define DEFAULT_SERIALPORT "/dev/ttyUSB0"

//Global data
FILE *fpSerial = NULL;   //serial port file pointer
ros::Publisher ucResponseMsg;
ros::Subscriber ucCommandMsg;
int ucIndex;          //ucontroller index number

int FileDesc;

unsigned char crc_sum=0;

//Initialize serial port, return file descriptor
FILE *serialInit(char * port, int baud)
{
  int BAUD = 0;
  int fd = -1;
  struct termios newtio, oldtio;
  FILE *fp = NULL;

 //Open the serial port as a file descriptor for low level configuration
 // read/write, not controlling terminal for process,
  fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

  ROS_INFO("FileDesc : %d",fd);

 if ( fd<0 )
  {
    ROS_ERROR("serialInit: Could not open serial device %s",port);
   return fp;
  }

  // save current serial port settings
  tcgetattr(fd,&oldtio);

  // clear the struct for new port settings
  bzero(&newtio, sizeof(newtio));

  //Look up appropriate baud rate constant
  switch (baud)
  {
     case 38400:
     default:
        BAUD = B38400;
        break;
     case 19200:
        BAUD  = B19200;
        break;
    case 115200:
        BAUD  = B115200;
        break;
     case 9600:
       BAUD  = B9600;
        break;
     case 4800:
        BAUD  = B4800;
        break;
     case 2400:
        BAUD  = B2400;
        break;
     case 1800:
        BAUD  = B1800;
        break;
     case 1200:
        BAUD  = B1200;
        break;
  }  //end of switch baud_rate

  if (cfsetispeed(&newtio, BAUD) < 0 || cfsetospeed(&newtio, BAUD) < 0)
  {
    ROS_ERROR("serialInit: Failed to set serial baud rate: %d", baud);
    close(fd);
    return NULL;
  }

  // set baud rate, (8bit,noparity, 1 stopbit), local control, enable receiving characters.
  newtio.c_cflag  = BAUD | CRTSCTS | CS8 | CLOCAL | CREAD;

  // ignore bytes with parity errors
  newtio.c_iflag =  IGNPAR;

  // raw output
  newtio.c_oflag = 0;

  // set input mode to non - canonical
  newtio.c_lflag = 0;

  // inter-charcter timer 
  newtio.c_cc[VTIME] = 0;

  // blocking read (blocks the read until the no.of charcters are read
  newtio.c_cc[VMIN] = 0;

  // clean the line and activate the settings for the port
  tcflush(fd, TCIFLUSH);
  tcsetattr (fd, TCSANOW,&newtio);

  //Open file as a standard I/O stream
  fp = fdopen(fd, "r+");

 if (!fp) {
    ROS_ERROR("serialInit: Failed to open serial stream %s", port);
    fp = NULL;
  }

ROS_INFO("FileStandard I/O stream: %d",fp);

  return fp;
} //serialInit


//Process ROS command message, send to uController
void ucCommandCallback(const geometry_msgs::TwistConstPtr& cmd_vel)
{
  unsigned char msg[14];
  float test1,test2;
  unsigned long i;

 // build the message packet to be sent
 msg = packet to be sent;
 msg[13] = crc_sum;

   for (i=0;i<14;i++)
   {
     fprintf(fpSerial, "%c", msg[i]);
   }

tcflush(FileDesc, TCOFLUSH); 

} //ucCommandCallback


//Receive command responses from robot uController
//and publish as a ROS message
void *rcvThread(void *arg)
{
  int rcvBufSize = 200;
  char ucResponse[10];//[rcvBufSize];   //response string from uController
  char *bufPos;
  std_msgs::String msg;
  std::stringstream ss;
  int BufPos,i;
  unsigned char crc_rx_sum =0;

  while (ros::ok()) {

     BufPos = fread((void*)ucResponse,1,10,fpSerial);

for (i=0;i<10;i++)
{
 ROS_INFO("T: %x ",(unsigned char)ucResponse[i]);
 ROS_INFO("NT: %x ",ucResponse[i]);
}

          msg.data = ucResponse;
          ucResponseMsg.publish(msg);
}

  return NULL;
} //rcvThread


int main(int argc, char **argv)
{
  char port[20];    //port name
  int baud;     //baud rate 

  char topicSubscribe[20];
  char topicPublish[20];

  pthread_t rcvThrID;   //receive thread ID
  int err;

  //Initialize ROS
  ros::init(argc, argv, "r2SerialDriver");
  ros::NodeHandle rosNode;
  ROS_INFO("r2Serial starting");

  //Open and initialize the serial port to the uController
  if (argc > 1) {
    if(sscanf(argv[1],"%d", &ucIndex)==1) {
      sprintf(topicSubscribe, "uc%dCommand",ucIndex);
      sprintf(topicPublish, "uc%dResponse",ucIndex);
    }
    else {
      ROS_ERROR("ucontroller index parameter invalid");
      return 1;
    }
  }
  else {
    strcpy(topicSubscribe, "uc0Command");
    strcpy(topicPublish, "uc0Response");
  }

  strcpy(port, DEFAULT_SERIALPORT);
  if (argc > 2)
     strcpy(port, argv[2]);

  baud = DEFAULT_BAUDRATE;
  if (argc > 3) {
    if(sscanf(argv[3],"%d", &baud)!=1) {
      ROS_ERROR("ucontroller baud rate parameter invalid");
      return 1;
    }
  }

  ROS_INFO("connection initializing (%s) at %d baud", port, baud);

   fpSerial = serialInit(port, baud);

 if (!fpSerial )
  {
    ROS_ERROR("unable to create a new serial port");
    return 1;
  }
  ROS_INFO("serial connection successful");

  //Subscribe to ROS messages
  ucCommandMsg = rosNode.subscribe("cmd_vel" /*topicSubscribe*/, 100, ucCommandCallback);

  //Setup to publish ROS messages
  ucResponseMsg = rosNode.advertise<std_msgs::String>(topicPublish, 100);

  //Create receive thread
  err = pthread_create(&rcvThrID, NULL, rcvThread, NULL);

  if (err != 0) {
    ROS_ERROR("unable to create receive thread");
    return 1;
  }

  //Process ROS messages and send serial commands to uController
  ros::spin();

  fclose(fpSerial);
  ROS_INFO("r2Serial stopping");
  return 0;
}

你可以把ROS部分放在一边,但问题出在串口代码上。

当我运行此代码时,我会正确接收来自控制器的数据,但即使控制器停止发送数据,我也会看到printfs 上不断出现相同的数据。这是不刷新输入缓冲区的问题吗?

但是我无法将数据从Linux PC发送到控制器,不知道发生了什么,Linux中的串口可以同时读写吗?

奇怪的观察,当我在 H-term(类似于超级终端的 uART 可视化器)中打开端口并且我的串行端口代码在后端运行时,H-term 仍然没有给出任何错误,但是理想情况下,H-term 应该给出一个错误,说“端口无法打开它被锁定”,但这不会发生,我的代码是否没有在串行端口上获得锁定?

当我使用 H-term 连接端口并运行 mu 串行端口代码时,我可以看到 UART 上从 linux-PC 到微控制器的数据?

任何人都可以对我在这里面临的问题有任何见解吗?

提前致谢。

【问题讨论】:

  • 我也遇到过类似的问题。确保数据被刷新等等。除此之外,我已经看到 UART 通信的稳定性可以与波特率相关联。另外,请查看github.com/freemed/tty0tty,它对于解决此类问题非常有用。
  • Data Flushed 的意思,请您详细说明一下。
  • 当您使用 fprintf 时,数据不会被写入,直到您打印换行符或执行 fflush。
  • 还有其他问题吗?
  • 不,我现在正在使用 fwrite(跟随@laune 评论)。

标签: c++ linux serialization


【解决方案1】:

这里有个问题:

BufPos = fread((void*)ucResponse,1,10,fpSerial);

因为没有检查 BufPos 是零还是小于 10

而不是 ros::ok,使用 feof()(收到 0 字节后)检查关闭的连接,使用 ferror() 检查错误。或者当您知道(根据协议)已收到数据包时停止调用 fread。

可以在全双工模式(即读取和写入)下使用串行端口,而不是完全“同时”,而是交替使用。合作伙伴必须遵守协议以避免误解。

不要混合使用 fprintf 和 fread/fwrite。发送时,表示fwrite。

【讨论】:

  • 我用 fwrite 代替了 fprintf,还是一样的行为。
猜你喜欢
  • 2012-08-09
  • 1970-01-01
  • 2019-07-03
  • 1970-01-01
  • 2017-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多