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

我正在使用用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字节。 如果有任何forms的错误,请原谅我。

我的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); 

}

对于SIZE是#define SIZE 64而帧是char帧[64];

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

同时更新我在终端以及程序上获得的帧图片。 可能它会更清楚。

通过程序从UART接收数据

minicom复活了

看着那个男人

返回值

成功时,返回读取的字节数(零表示文件结束),文件位置按此编号前进。 如果此数字小于请求的字节数,则不是错误; 这可能发生在例如因为现在实际可用的字节数较少 (可能是因为我们接近文件结尾,或者因为我们正在从管道或终端读取),或者因为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: "); } } 

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

Canonical模式如手册中所述

在规范模式中:

输入逐行提供。 键入其中一个行分隔符(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或字节为换行符'\ n'。

您的原始代码有许多问题导致它“只获得前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 64 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); } 

试试吧

  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;