在C中使用linux串口,无法获取完整数据

问题描述 投票:0回答:4

我正在使用用 C 编写的 Linux 串行端口。下面是我的 UART 设置

 int fd;
 struct termios tty_attributes;
 fd = open(comport, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK );

if(fd < 0)
{
    perror("open comport error.\n");
    exit(EXIT_FAILURE);
}
else
 {

    if(tcgetattr(fd, &tty_attributes) == -1)
    {
        perror("tcgetattr termios function error.\n");
        exit(EXIT_FAILURE);
    }

    tty_attributes.c_lflag = 0;
    tty_attributes.c_oflag = 0;
    tty_attributes.c_iflag = 0;
    tty_attributes.c_cflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
    tty_attributes.c_cflag |= CS8;
    tty_attributes.c_cflag |= CLOCAL;
    tty_attributes.c_cflag &= ~CREAD;
    tty_attributes.c_oflag &= ~OPOST;
    tty_attributes.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
    tty_attributes.c_cc[VMIN] = SIZE_STR_FRAME;
    cfsetospeed(&tty_attributes, BAUDRATE);       //setting communication speed and other attributes
    cfsetispeed(&tty_attributes, BAUDRATE);
    tcflush(fd, TCIOFLUSH);
    tcsetattr(fd, TCSANOW, &tty_attributes);     //change immediately
    return fd;
}

}

下面是我的读取框架的代码

char* frame_read(int fd)
{
    char *ret = NULL;
    int read_ret_val;
    struct timeval time_val;
    if (fd < 0)
    {
        printf("Before read over comm channel, channel must be initialize\n");
        exit(EXIT_FAILURE);
    }
    memset(frame, 0, SIZE);
    fd_set rfds;        //read file discriptors
    int return_val;
    FD_SET(fd, &rfds);

    setReceiveMode(fd, TRUE);
    tcflush(fd, TCIFLUSH);
    tcflush(fd, TCOFLUSH);    //flush previous values
    return_val = select((fd) + 1, &rfds, NULL, NULL, &time_val);
    if (return_val == -1)
    {
        perror("select");
        exit(EXIT_FAILURE);
    }

    else if (return_val)
    {
        usleep(100 * 1000);
        read_ret_val = read(fd, frame, SIZE);
        if (read_ret_val < 0)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }
        ret = frame;
        //printf("inside else if of read\n");
    }
}

我有一个 GPS 模块与 UART 连接,当我检查 minicom 时,我得到了全帧,但是当我通过 uart 接收时(使用此代码),我只得到前 16 个字节。 谁能指出我的错误。? 这里波特率是9600,帧是64字节,SIZE是64字节。,我占用的缓冲区也是64字节。如有格式错误,请原谅。

我的main.c文件

int main(int argc, char *argv[])
{
  int i=0,j=0;
  char *readb;
  unsigned char data[34];
  static int fd = -1;
  struct struct_base_gps *gps;
  int command=0;
  char COMM_PORTNAME[13];
  strcpy( COMM_PORTNAME, argv[1] );// give the first port number for GPS receiving
  if((fd = init_comm_channel(COMM_PORTNAME)) < 0 )
  {
       exit(EXIT_FAILURE);
       printf("port is not opened\n");
  }
  else
  {

     printf("port is open for communication:\n");
     readb = frame_read(fd);
     for (i=0;i<=34;i++)
      {
          data[i] = *(readb +j);
         printf("the data is %x\n",data[i]);
         j++;
     }
  }
  close (fd);

}

尺寸为 #定义尺寸 64 框架是 字符帧[64];

感谢您的反馈,我已经更新了代码。

还更新我在终端和程序上获得的框架图片。也许它会更清晰。

程序从UART接收数据

迷你电脑已收到

c linux serial-port
4个回答
1
投票

看着那个男人

返回值

成功时,返回读取的字节数(零表示结束 文件的位置),并且文件位置提前该数字。 是 如果该数字小于字节数则不会出错 要求;例如,这可能会发生,因为字节数较少 现在实际上可用(也许是因为我们已经接近尾声了 文件,或者因为我们正在从管道或终端读取),或者 因为 read() 被信号中断了。 另请参阅注释。

强调我的

所以你不能指望一次读取就能检索到整个帧。 您应该循环直到收到所有预期的字符,例如:

int total_rec = 0;
char temp[SIZE];
while( total_rec < SIZE )
{
    read_ret_val = read(fd, temp, SIZE);
    if (read_ret_val != -1)
    {
       if ( (total_rec + read_ret_val) >= SIZE)
       { 
           read_ret_val = SIZE - total_rec;
       }
       memcpy(&frame[total_rec], temp, read_ret_val);
       total_rec += read_ret_val;
    }
    else
    {
       perror("error reading serial line: ");
    }
}

1
投票

您的原始代码有许多问题,导致它“仅获取前 16 个字节”

  • 代码(如发布的)仅执行单个 read() 系统调用(而不是连续循环以从设备读取数据)。

  • 输入显然是 ASCII 文本,分隔为以回车符和换行符结尾的行,但您的程序使用非规范模式而不是规范模式来读取。 @pbn 的假设得到了 minicom 输出的证实。

  • 您的程序以非阻塞模式而不是阻塞模式使用串行终端,并使用 select()usleep() 调用来等待数据的到来。

  • termios初始化(除了不符合POSIX标准)有几个错误,包括应用于cflag成员的不正确的iflag符号,未使用~CSIZE清除字符大小位,以及未启用CREAD。

  • 您的读取例程不必要地在 select() 调用之前刷新(即丢弃)所有已接收但未读的数据。

用于打开和配置串行终端的修订例程(用于阻止规范模式):

#define BAUDRATE    B9600

int init_comm_channel(char *comport)
{   
    struct termios tty_attributes;
    int fd;

    fd = open(comport, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror("open comport error.\n");
        return (-2);
    }
    if (tcgetattr(fd, &tty_attributes) == -1) {
        perror("tcgetattr termios function error.\n");
        return (-3);
    }
    tty_attributes.c_cflag |= CLOCAL | CREAD;
    tty_attributes.c_cflag &= ~CSIZE;
    tty_attributes.c_cflag |= CS8;         /* 8-bit characters */
    tty_attributes.c_cflag &= ~PARENB;     /* no parity bit */
    tty_attributes.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty_attributes.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    tty_attributes.c_lflag |= ICANON | ISIG;  /* canonical input */
    tty_attributes.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
    
    tty_attributes.c_iflag &= ~INPCK;
    tty_attributes.c_iflag |= IGNCR;
    tty_attributes.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
    tty_attributes.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */

    tty_attributes.c_oflag &= ~OPOST;
    
    cfsetospeed(&tty_attributes, BAUDRATE);       //setting communication speed and other attributes
    cfsetispeed(&tty_attributes, BAUDRATE);
    tcflush(fd, TCIOFLUSH);

    if (tcsetattr(fd, TCSANOW, &tty_attributes) < 0) {
        perror("tcsetattr function error.\n");
        return (-4);
    }
    return fd;
}

每个系统调用读取一行的修订例程:

#define SIZE    100
unsigned char frame[SIZE];

char *frame_read(int fd)
{
    int read_ret_val;

    if (fd < 0) {
        printf("Before read over comm channel, channel must be initialize\n");
        exit (EXIT_FAILURE);
    }
    read_ret_val = read(fd, frame, SIZE - 1);
    if (read_ret_val < 0) {
        perror("read");
        exit (EXIT_FAILURE);
    }
    frame[read_ret_val] = 0; /* terminate string */
    return (frame);
}

                                   

修改后的main()例程永远循环:

int main(int argc, char *argv[])
{
    int fd;
    char *readb;
    char com_portname[13] = {0};

    if (argc > 1)
        strcpy(com_portname, argv[1]);  // give the first port number for GPS receiving
    if ((fd = init_comm_channel(com_portname)) < 0) {
        printf("port is not opened\n");
        exit (EXIT_FAILURE);
    }
    printf("port is open for communication:\n");
    do {
        readb = frame_read(fd);
        while (*readb > 0)
            printf("the data is 0x%x\n", *readb++);
        printf("The line is: %s", frame);
    } while (1);  /* loop for another line */
    close(fd);
}

0
投票

尝试使用

    memset(&tty_attributes,0,sizeof(tty_attributes));
    tty_attributes.c_iflag=0;
    tty_attributes.c_oflag=0;
    tty_attributes.c_cflag=CS8|CREAD|CLOCAL;
    tty_attributes.c_lflag=0;
    tty_attributes.c_cc[VMIN]=1;
    tty_attributes.c_cc[VTIME]=5;

0
投票

大多数 GPS 模块和一般设备的串行接口都会逐行向您发送数据。为此,您可以使用已明确禁用的规范模式。

手册

中所述的规范模式

在规范模式下:

输入可逐行使用。 当键入行分隔符之一(NL、EOL、EOL2;或 EOF)时,输入行可用。 行的开头)。 除了 EOF 的情况外,行分隔符包含在 read(2) 返回的缓冲区中。

我发布代码来设置串行接口速度和启用规范模式的奇偶校验:

int set_interface_attribs(int fd, int speed, int parity) 
{
  // setup based on stty < /dev/ttyACM0 (cfg.txt) output which
  // worked for ABSniffer in pyserial implementation
  // otherwise module responded only once for every two prompts
  struct termios tty;
  int rc;
  memset(&tty, 0, sizeof tty);
  if (tcgetattr(fd, &tty) != 0) 
  {
    log_info("error from tcgetattr %s\r\n", strerror(errno));
    return -1;
  }

  rc = cfsetospeed(&tty, speed);
  if (rc == - 1) return -1;
  rc = cfsetispeed(&tty, speed);
  if (rc == - 1) return -1;

  tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;  // 8-bit chars
  // disable IGNBRK for mismatched speed tests; otherwise receive break
  // as \000 chars
  tty.c_cc[VMIN] = 0;   // read doesn't block
  tty.c_cc[VTIME] = 5;  // 0.5 seconds read timeout

  tty.c_cflag |= (CLOCAL | CREAD);  // ignore modem controls,
  // enable reading
  tty.c_cflag &= ~(PARENB | PARODD);  // shut off parity
  tty.c_cflag |= parity;
  tty.c_cflag &= ~CSTOPB;
  //    tty.c_iflag |= ICRNL | BRKINT; //ICRNL
  tty.c_iflag |= IGNCR;
  tty.c_cflag &= ~CRTSCTS;
  //    tty.c_oflag |= OPOST | ONLCR;
  // tty.c_iflag |= ISIG | ICANON | IEXTEN;
  tty.c_lflag |= ISIG | IEXTEN | ICANON;
  tty.c_lflag &= ~ECHO;
  tty.c_cc[VEOF] = 0x0;
  tty.c_cc[VEOL] = 0x0;

  if (tcsetattr(fd, TCSANOW, &tty) != 0) 
  {
    log_info("error from tcsetattr %s\r\n", strerror(errno));
    return -1;
  }
  return 0;
}

使用方法如下:

rc = set_interface_attribs(fd, B9600, 0);

从现在开始,数据应该逐行可用。所有错误和可能的返回值均在阅读手册中进行了解释。假设没有错误,读取任意大小的缓冲区应该返回 EAGAIN(资源暂时不可用)并返回代码 -1 或换行符 ' 的字节 '.

© www.soinside.com 2019 - 2024. All rights reserved.