首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C中的串行读取不返回数据

C中的串行读取不返回数据
EN

Stack Overflow用户
提问于 2018-06-14 18:01:11
回答 2查看 1.6K关注 0票数 2

我使用C语言中的termios编写一个简单的程序,将其写入串口并读取返回的数据。与串行线路上的设备的通信以回车结束。该程序很简单,目前看起来如下:

代码语言:javascript
复制
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{

  struct termios s_alicat;
  int tty_fd, count;

  // Ports connected in USB as sudo
  if ((tty_fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
  {
    printf("Line failed to open with %d\n", tty_fd);
    return -1;
  }
  else
  {
    printf("fd is %d\n", tty_fd);
  }

  s_alicat.c_cflag = B19200 | CS8 | CREAD | CLOCAL;

  //No parity 8N1:
  s_alicat.c_cflag &= ~PARENB;
  s_alicat.c_cflag &= ~CSTOPB;
  s_alicat.c_cflag &= ~CSIZE;

  s_alicat.c_iflag = IGNPAR | ICRNL; // Ignore parity errors

  //Disable hardware flow control
  s_alicat.c_cflag &= ~CRTSCTS;

  //Disable software flow control
  s_alicat.c_iflag &= ~(IXON | IXOFF | IXANY);

  //Raw output
  s_alicat.c_oflag &= ~OPOST;

  //Raw input
  s_alicat.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

  tcflush(tty_fd, TCIFLUSH);
  tcsetattr(tty_fd, TCSANOW, &s_alicat);


  unsigned char transmit[2] = "C\r";
  if ((count = write(tty_fd, &transmit, 2)) < 0)
  {
    printf("Failed to write to device");
  }

  printf("Transmited %d characters\n", count);

  usleep(500000);

  unsigned char receive[255];

  if ((count = read(tty_fd, &receive, 255) < 0))
  {
    printf("Error receiving text %d", count);
  }
  else
  {
    if (count == 0)
    {
      printf("No data read in...\n");
    }
    else
    {
      printf("%s", receive);
    }
  }
  printf("Closting port...\n");
  close(tty_fd);

  return 0;
}

所以:

  • 端口正确打开。
  • 我能够写数据(物理上可以看到它通过发光二极管穿过线,当数据被发送和接收时点亮)。
  • 读取返回0个字符。

如果我通过使用19.2,8N1设置的另一个程序(没有流控制)发送相同的命令(C\r),我将得到以下字符串(或类似的内容)

C\s+012.05\s+031.73\s+000.01\s+000.01\s010.24\s\s\s\s\sAir\r

我在这里做错什么了?这是否与IO被终止回车这一事实有关?还是我的配置不正确?

编辑:所以,如果我看字符设备(/dev/ttyUSB0),我实际上可以看到数据回来-见下面的快照。所以,看起来我的问题是从读缓冲区中读取和获取信息。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-06-14 21:07:51

还是我的配置不正确?

是。

问题是您的程序使用非阻塞模式。

代码语言:javascript
复制
  open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY)

并与非规范模式相结合。

代码语言:javascript
复制
  s_alicat.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

即使您声明输入行以“带(a)回车”结尾。

“ReadReportwith0个字符”对于非阻塞的原始读取(就像您的配置)来说是可预测的和正常的,只要没有可用的数据。详细信息请参见这个答案

若要更正程序,请使用阻塞模式。

在获得文件描述符后插入以下语句:

代码语言:javascript
复制
fcntl(tty_fd, F_SETFL, 0);    /* set blocking mode */

有关解释,请参见

至于termios配置,您的程序有一个严重的错误:它使用未经初始化的termios结构s_alicat

正确的方法是使用tcgetattr()

请参阅正确设置终端模式POSIX操作系统串行编程指南

代码语言:javascript
复制
#include <errno.h>
#include <string.h>

...

  if (tcgetattr(tty_fd, &s_alicat) < 0) {
      printf("Error from tcgetattr: %s\n", strerror(errno));
      return -1;
  }
  cfsetospeed(&s_alicat, B19200);
  cfsetispeed(&s_alicat, B19200);

  s_alicat.c_cflag |= (CLOCAL | CREAD);
  s_alicat.c_cflag &= ~CSIZE;
  s_alicat.c_cflag |= CS8;         /* 8-bit characters */
  s_alicat.c_cflag &= ~PARENB;     /* no parity bit */
  s_alicat.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */

  s_alicat.c_iflag |= ICRNL;       /* CR is a line terminator */
  s_alicat.c_iflag |= IGNPAR;      // Ignore parity errors

  // no flow control
  s_alicat.c_cflag &= ~CRTSCTS;
  s_alicat.c_iflag &= ~(IXON | IXOFF | IXANY);

  // canonical input & output
  s_alicat.c_lflag |= ICANON;
  s_alicat.c_lflag &= ~(ECHO | ECHOE | ISIG);
  s_alicat.c_oflag |= OPOST;

  if (tcsetattr(tty_fd, TCSANOW, &s_alicat) != 0) {
      printf("Error from tcsetattr: %s\n", strerror(errno));
      return -1;
  }

代码中的其他错误包括在数组地址足够时使用指向数组地址的指针(即地址地址)。

代码语言:javascript
复制
write(tty_fd, &transmit, 2)
read(tty_fd, &receive, 255)

应该是分别的

代码语言:javascript
复制
write(tty_fd, transmit, 2)
read(tty_fd, receive, 255)

read() syscall不返回或存储字符串,但是您的程序假定它返回或存储字符串。

代码(更正了优先级错误)应该是:

代码语言:javascript
复制
if ((count = read(tty_fd, receive, sizeof(receive) - 1)) < 0) {
    printf("Error receiving text %s\n", strerror(errno));
} else {
    receive[count] = 0;  /* terminate string */
    printf("Received %d: \"%s\"\n", count, receive);
}

请注意,读取请求长度小于缓冲区大小,以便为终止空字节保留空间。

增编

您的代码有一个优先级/括号错误,这个错误一直传递到我的代码中。冒犯的说法是:

代码语言:javascript
复制
if ((count = read(tty_fd, &receive, 255) < 0))

变量count的赋值应该是来自read() syscall的返回代码,而不是对逻辑表达式read() < 0的计算。

如果没有适当的括号,则首先执行比较,因为小于运算符的优先级高于赋值运算符。

这个错误导致count,当有一个好的读取(即一个正的、非零的返回代码)时,总是将值指定为0(即为false的整数值)。

从这个答案中修改的代码与您的代码合并后进行了测试,并确认其功能与预期的功能相同,文本以回车结束。

票数 3
EN

Stack Overflow用户

发布于 2018-06-15 23:00:19

@锯末-谢谢你的帮助。我将在这里张贴我的工作代码与一些初步意见。

问题是您的程序使用非阻塞模式。

这其实不是问题,这正是我想要的。我不希望读取阻塞,因为这可能导致程序挂起,如果设备没有响应。这段代码只是用来测试我的串行接口是否良好。因此,使用fcntl(tty_fd, F_SETFL, 0)将标志设置为0是我不想做的。

变量计数的赋值应该是来自read() syscall的返回代码。

我想我是跟着你来的,但措辞很奇怪。是的,括号放得不对--谢谢你指出这一点。但是,关于“返回代码”,我假设您的意思是-1还是接收到的字节数?根据您最新的答复,我认为是这样的。

这是最后的代码。我相信我把你提供的反馈意见纳入其中。如果你看到任何奇怪的东西,可以自由地提供更多。此运行时函数的返回如下所示

代码语言:javascript
复制
root@cirrus /h/m/D/s/F/C/c/src# ./main
fd is 3
count is 49
String is C +012.17 +030.85 +000.00 +000.00 010.24     Air
Closing port...

守则:

代码语言:javascript
复制
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#define PORT "/dev/ttyUSB0"

int main(void)
{

  struct termios s_alicat;
  int tty_fd, count;
  char receive[255], transmit[2];

  // Ports connected in USB as sudo
  if ((tty_fd = open(PORT, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
  {
    printf("Line failed to open with %d\n", tty_fd);
    return -1;
  }
  else
  {
    printf("fd is %d\n", tty_fd);
  }

  if (tcgetattr(tty_fd, &s_alicat) < 0)
  {
    printf("Error from tcgetattr: %s\n", strerror(errno));
    return -1;
  }

  cfsetospeed(&s_alicat, B19200);
  cfsetispeed(&s_alicat, B19200);

  // Set up receiver and set to local mode
  s_alicat.c_cflag |= (CLOCAL | CREAD | CS8);

  s_alicat.c_iflag |= IGNPAR | ICRNL; // Ignore parity errorss

  tcflush(tty_fd, TCIFLUSH); //discard file information not transmitted
  if (tcsetattr(tty_fd, TCSANOW, &s_alicat) != 0)
  {
    printf("Error from tcsetattr: %s\n", strerror(errno));
    return -1;
  }

  // Clear the port before kicking off communications
  strcpy(transmit, "\r\r");
  write(tty_fd, transmit, 2);

  strcpy(transmit, "C\r");

  if ((count = write(tty_fd, transmit, 2)) < 0)
  {
     printf("Failed to write to device");
  }

  int j = 0;
  count = 0;

  /* Attempt to read data at most 3 times if there is no data 
   * coming back.
   */

  while (count == 0 && j < 3)
  {

    usleep(100000);
    if ((count = read(tty_fd, receive, sizeof(receive) - 1)) < 0)
    {
      printf("Error receiving text %d", count);
    }
    else
    {
      printf("count is %d\n",  count);
      receive[count] = 0;
      printf("String is %s", receive);
    }
    j++;
  }

  printf("Closing port...\n");
  int p = 0;
  if ((p = close(tty_fd)) < 0)
  {
    printf("Port failed to close %d\n", p);
    return -1;
  }

  return 0;
}

编辑

通过cfsetospeedcfsetispeed增加了波特率的显式设置。

另一位编辑

摆脱了多余的tcgetattr呼叫。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50863379

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档