Ubuntu串行通信:读取失败然后立即进入
我正在编写一个在运行Ubuntu服务器14.04的MIO-3260单板计算机上运行的程序,并与AMC DPRANIE C100A400驱动器通信。 程序向驱动器发送一串hex代码,并且应该为它发送的每条消息接收响应。 当我在Windows上的realTerm中尝试它时效果很好所以我不认为这是驱动器的问题。 但是,当我尝试从串口读取时,read()几乎一直都会返回-1,直到突然在一个看似随机的点上,我立刻得到了大量的消息。
我正在使用termios来设置串口。 这是我的代码。 我已经尝试在阻塞配置中进行设置,但如果我这样做,代码只会在第一次读取()时无限期挂起。
int fd; fd = open("/dev/ttyS0",O_RDWR | O_NOCTTY | O_NDELAY); struct termios SerialPortSettings; tcgetattr(fd, &SerialPortSettings); //get current settings of serial port cfsetispeed(&SerialPortSettings,B115200);//set input baud rate cfsetospeed(&SerialPortSettings,B115200);//set output baud rate SerialPortSettings.c_cflag &= ~PARENB;//clear parity bit (no parity) SerialPortSettings.c_cflag &= ~CSTOPB;//Stop bits = 1 SerialPortSettings.c_cflag &= ~CSIZE;//clears the mask SerialPortSettings.c_cflag |= CS8; //set data bits = 8 SerialPortSettings.c_cflag &= ~CRTSCTS; //turn off hardwar based flow ctrl SerialPortSettings.c_cflag |= CREAD | CLOCAL;//Turn on the reciever SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); //Turn off software //based flow control SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Non-canonical mode SerialPortSettings.c_oflag &= ~OPOST;//no output processing //set the termios struct now if(tcsetattr(fd,TCSANOW,&SerialPortSettings) != 0) printf("\n ERROR: setting attributes"); else printf("\n Baudrate = 115200 \t Stopbits = 1 \t Parity = none");
要从串口读取,我的代码如下:
uint8_t buf[1024];//Rx buffer int bytes_read; bytes_read = read(fd,&buf,1024); if(bytes_read != -1){ for(int i=0;i<bytes_read;i++) /*printing only the received characters*/ printf("%02X\t",buf[i]); puts("\n"); }
这是我应该接收的消息类型的示例{0xA5 0xFF 0x10 0x01 0x00 0x00 0xD4 0x11}。 它应该是大约8-14个字节长。 相反,我同时收到一个巨大的数字而在其他时间没有收到(例如我在发送946个没有响应的命令后立即收到810个字节)。
我过去几天一直在排除故障,不知道发生了什么。 有时候我跑它并且它大部分时间都会响应,然后神秘地它会停止然后间歇性地回来。
如果我能提供更多信息,请告诉我。
任何帮助将非常感激!
更新:我已将笔记本电脑连接到串口,因此我可以使用RealTerm监视传输,并且我已经validationMIO-3260正在正确发送命令并且驱动器正确响应。 所以问题似乎是当我尝试从端口读取时。
我已经尝试在阻塞配置中进行设置,但如果我这样做,代码只会在第一次读取()时无限期挂起。
您描述的结果类似于阻止规范模式(当您真正想要(阻止)原始模式时)。
read()应该阻塞,直到收到EOL(行尾)字符。
但是,当我尝试从串口读取时,read()几乎一直都会返回-1,直到突然在一个看似随机的点上,我立刻得到了大量的消息。
您描述的结果类似于非阻塞规范模式(当您真正想要(阻止)原始模式时)。
当缓冲区中没有可用的数据(即完整的行)时, read()将返回-1,并且errno (您无需检查)将设置为-EAGAIN。
但是当二进制数据巧合地匹配EOL(行尾)字符时,满足规范read()的条件,并返回缓冲的数据。
串行终端实际上没有配置为非规范模式的原因是因为ICANON和相关标志是从错误的成员中清除的。
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Non-canonical mode
ICANON位于c_lflag
成员中,而不是c_iflag
。
所以声明应该是
SerialPortSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Non-canonical mode
据推测,串行终端默认为规范模式,并且您的程序从未成功地将模式更改为任何不同的模式。
此外,对于原始模式,需要定义VMIN和VTIME成员。
例如:
SerialPortSettings.c_cc[VMIN] = 1; SerialPortSettings.c_cc[VTIME] = 1;
您的代码中的另一个错误是当数组地址足够时使用指向数组地址的指针(即地址地址)。
bytes_read = read(fd,&buf,1024);
应该只是
bytes_read = read(fd, buf, 1024);
附录
OP的代码与这个问题非常相似,它具有相同的坏termios语句。
那张海报最终解决了他自己的问题,但错误地将修复归因于添加一个(不相关的)ECHONL标志,而没有意识到他实际上是在纠正结构成员的名字。
附录2
这个c_iflag和ICANON bug的起源似乎是xanthium.in的这个串口教程 。 两年前,作者被告知该错误,但尚未修复。