C串行读取未找到回车符“\r";

C串行读取未找到回车符“\r";,c,serial-port,carriage-return,C,Serial Port,Carriage Return,我现在要花几天时间通过串口初始化ELM237 OBD2适配器。 我连接的设备发送的响应总是以回车结束。我可以毫无问题地发送命令。响应也按预期由设备发送。我试图从串行端口读取数据,直到读取回车符。 我可以在我的串行监视器中看到命令被正确发送,我得到一条完整的消息,包括回车。但是我的代码不识别回车 以下是我在串行监视器上接收到的信息: 这是我的程序的输出: 代码: #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括 int SerialConnection();

我现在要花几天时间通过串口初始化ELM237 OBD2适配器。 我连接的设备发送的响应总是以回车结束。我可以毫无问题地发送命令。响应也按预期由设备发送。我试图从串行端口读取数据,直到读取回车符。 我可以在我的串行监视器中看到命令被正确发送,我得到一条完整的消息,包括回车。但是我的代码不识别回车

以下是我在串行监视器上接收到的信息: 这是我的程序的输出: 代码:

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
int SerialConnection();
int serial_fd;
int main(int argc,char*argv[])
{
字符缓冲区[255];/*输入缓冲区*/
char*bufptr;/*缓冲区中的当前字符*/
int nbytes;/*读取的字节数*/
如果(argc!=2)
{
printf(“用法:%s\n”,argv[0]);
出口(1);
}
serial_fd=SerialConnection(argv[1]);//打开串行端口
如果(串行_fd<1)
{
printf(“串行端口打开故障\n”);
出口(1);
}
printf(“串行端口成功打开\n”);
而(1)
{
memset(缓冲区,0255);//清除缓冲区
int-ELMInit=1;
开关(ELMInit)
{
案例1:
printf(“请求ATZ重置ELM适配器\n”);
写入(串行_fd,“ATZ\r”,4);
bufptr=缓冲区;
而((nbytes=read(serial_fd,bufptr,buffer+sizeof(buffer)-bufptr-1))>0)
{
printf(“当前缓冲区%s\n”,缓冲区);
bufptr+=N字节;
如果(bufptr[-1]='\r')
{
printf(“找到%s\n的回车符”,缓冲区);
if(strstr(缓冲区,“ELM327”)!=NULL)
{
printf(“成功%s\n”,缓冲区);
}
打破
}
*bufptr='\0';
}
打破
违约:
打破
}
}
}   
int串行连接(字符*串行端口)
{
int-fd;
结构termios选项;
fd=打开(串行端口,O|RDWR | O|NOCTTY | O|NDELAY);
如果(fd==-1)
{
返回-1;
}
其他的
{
fcntl(fd,F_SETFL,0);
tcgetattr(fd和选项);
cfsetispeed(和选项,B38400);
cfsetospeed(和选项,B38400);
options.c_cflag&=~PARENB;/*将字符大小屏蔽为8位,无奇偶校验*/
options.c_cflag&=~CSTOPB;/*1停止位*/
options.c_cflag&=~CSIZE;
options.c_cflag |=CS8;/*选择8个数据位*/
options.c_cflag&=~CRTSCTS;/*禁用硬件流控制*/
//options.c_lflag |=ICANON;/*规范模式*/
//options.c|lflag |=~(ICANON | ECHO | ECHO);/*允许将数据作为规范输入进行处理*/
options.c|u cflag |=(CLOCAL | CREAD);/*启用接收器并设置本地模式*/
tcsetattr(fd、TCSANOW和options);/*为端口设置新选项*/
}
返回(fd);
}
我可以在我的串行监视器中看到命令被正确发送,我得到一条完整的消息,包括回车。但是我的代码不识别回车

您程序的输出确实证实了@JonathanLeffler的评论。接收到的回车正在转换为换行符,换行符将终止规范的read()请求。尽管程序的termios配置不完整,但您看到的结果还是有些合理

您的程序使用首选方法配置termios属性。
但是,如果您需要规范模式(即读取行),那么您的程序应该显式地配置该模式,而不是将其留给偶然(就像现在这样)

您可能应该禁用回音,这样您就不会收到您发送的内容

由于您希望该行以
\r
而不是传统的
\n
终止,因此还必须指定该行

options.c_cc[VEOL] = '\r';
options.c_iflag &= ~(INLCR | IGNCR | ICRNL);
禁用回车和换行转换,并且绝对不要忽略回车


请注意,您的代码确实禁用了硬件流控制,但将软件流控制(Xon/Xoff)留给了偶然

options.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
options.c_iflag &= ~(INPCK| IUCLC | IMAXBEL);
options.c_oflag &= ~OPOST;
信号生成也不是以任何方式配置的


附录

我只剩下一个奇怪的问题了。我的消息是20字节长,当我在nbytes上进行printf时,它告诉我我已经收到了全部20字节。但是当我在缓冲区上执行printf时,它会切断第一个字符,而不是打印ATZ…elm327v1.5它只打印m327v1.5

(1) Echo已关闭,因此read()将不会拾取发送的“ATZ\r”,除非连接的设备回显该命令。这是不可能的,因为这种设备通常被配置成在由计算机程序而不是由人控制时不回声

(2) printf()显示
缓冲区的全部内容,其中包括下线字符,如回车符。输出这个接收到的
\r
和额外的空格(在字符串说明符和换行符之间)是对早期的printf()输出的阻碍

printf("current buffer %s \n",buffer);
                         ^
除非您打算覆盖现有的行,否则很少(在Linux中)使用回车本身(即没有换行符)。在显示单独的回车时,您需要注意其后果

一个简单的解决方案是切换到Linux行终止,并将回车转换为换行符(i)
options.c_lflag |= ICANON;                        /* Canonical mode*/
options.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
options.c_cc[VEOL] = '\r';
options.c_iflag &= ~(INLCR | IGNCR | ICRNL);
options.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
options.c_iflag &= ~(INPCK| IUCLC | IMAXBEL);
options.c_oflag &= ~OPOST;
printf("current buffer %s \n",buffer);
                         ^