C串口读取没有发现回车return "\r"

C serial read does not find carriage return "\r"

我现在正在努力通过串行初始化 ELM237 OBD2 适配器几天。 我正在连接的设备发送一个总是以回车符 return 结尾的响应。我可以毫无问题地发送命令。设备也按预期发送响应。我正在尝试从串行端口读取,直到我读取了回车 return。 我可以在我的串行监视器中看到该命令已正确发送,并且我收到了一条完整的消息,其中包括 carriage return back 。但是我的代码无法识别回车 return.

这是我在串行监视器上收到的信息:

[19/07/2018 21:08:43] 
Written data 

41 54 5a 0d                                                             

ATZ. 

[19/07/2018 21:08:43] Read data

41 54 5a 0d

ATZ.             

[19/07/2018 21:08:44] Read data  

0d 0d 45 4c 4d 33 32 37 20 76 31 2e 35 0d 0d 3e   

..ELM327 v1.5..> 

这是我程序的输出:

Serial Port Open Succesfully

Request ATZ Reset ELM Adapter

current buffer ATZ

current buffer ATZ

current buffer ATZ

current buffer ATZ

ELM327 v1.5

current buffer ATZ

ELM327 v1.5

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h> 
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>

int SerialConnection();
int serial_fd;



int main(int argc, char* argv[])
{
    char buffer[255];       /* Input buffer */
    char *bufptr;           /* Current char in buffer */
    int  nbytes;            /* Number of bytes read */

    if(argc != 2)
    {
        printf( "Usage: %s <Serial Port>\n",argv[0]);
        exit(1);
    }

    serial_fd = SerialConnection(argv[1]);//Open serial port
    if(serial_fd < 1)
    {
        printf( "Serial Port Open Failure\n");
        exit(1);
    }
    printf( "Serial Port Open Succesfully\n");


    while(1)
    {
        memset(buffer, 0, 255); //clear buffer
        int ELMInit = 1;
        switch(ELMInit)
        {
        case 1:
            printf( "Request ATZ Reset ELM Adapter\n");
            write(serial_fd, "ATZ\r", 4);
            bufptr = buffer;
            while ((nbytes = read(serial_fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0)
            {
                printf("current buffer %s \n",buffer);
                bufptr += nbytes;
                if (bufptr[-1] == '\r')
                {
                    printf("carriage return found %s \n",buffer);
                    if(strstr(buffer, "ELM327") != NULL)
                    {
                        printf("success %s\n",buffer);
                    }
                    break;
                }
                *bufptr = '[=13=]';
            }
            break;

            default:
            break;


        }

    }

}   

int SerialConnection(char *serial_port)
{
    int fd;
    struct termios options;

    fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)
    {
        return -1;
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
        tcgetattr(fd, &options);
        cfsetispeed(&options, B38400);
        cfsetospeed(&options, B38400);
        options.c_cflag &= ~PARENB;                     /* Mask the character size to 8 bits, no parity */
        options.c_cflag &= ~CSTOPB;                     /*1 Stop bit */
        options.c_cflag &= ~CSIZE;
        options.c_cflag |= CS8;                         /* Select 8 data bits */
        options.c_cflag &= ~CRTSCTS;                    /* Disable hardware flow control */ 
        //options.c_lflag |= ICANON;                        /* Canonical mode*/
        //options.c_lflag |= ~(ICANON | ECHO | ECHOE);  /* Enable data to be processed as Canonical input */
        options.c_cflag |= (CLOCAL | CREAD);            /* Enable the receiver and set local mode */
        tcsetattr(fd, TCSANOW, &options);               /* Set the new options for the port */
    }
    return (fd);
}

I can See in my Serial Monitor that the command is sent properly and i get a full Message including carriage return back . But my code does not recognize the carriage return.

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

您的程序使用首选方法配置 termios 属性。
但是,如果您需要规范模式(即读取行),那么您的程序应该明确配置该模式,而不是让它碰碰运气(就像现在一样)。

options.c_lflag |= ICANON;                        /* Canonical mode*/
options.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

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

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

options.c_cc[VEOL] = '\r';
options.c_iflag &= ~(INLCR | IGNCR | ICRNL);

禁用回车 return 和换行符翻译,绝对不要忽略回车 returns。


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

options.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
options.c_iflag &= ~(INPCK| IUCLC | IMAXBEL);
options.c_oflag &= ~OPOST;

信号生成也没有配置。


附录

I only have one weird issue left . My Message is 20 bytes Long when i do a printf on nbytes it tells me that i have received all 20 bytes . but when i do a printf on buffer it cuts off the first characters instead of printing ATZ...ELM327 v1.5 it only prints M327 v1.5

(1) Echo 已经关闭,所以 read() 不会接收发送的 "ATZ\r" unless 连接的设备回显命令。这不太可能,因为此类设备通常配置为在由计算机程序而不是人控制时不回声。

(2) printf() 正在显示 buffer 的全部内容,其中包括 EOL 字符,例如回车 return。输出这个接收到的 \r 无关的 空格(在字符串说明符和换行符之间)是破坏早期 printf() 输出的原因.

printf("current buffer %s \n",buffer);
                         ^

很少使用回车 return 本身(即没有换行符)(在 Linux 中),除非您打算覆盖现有行。显示单车时需要注意后果 returns.

一个简单的解决方案是切换到 Linux 行终止,并将回车符 return 转换为换行符(即启用 ICRNL 而不是禁用它)。并将 \r 的搜索更改为 \n

实际上,当(阻塞)规范输入被正确配置时,连接到缓冲区的读取和搜索回车 return 都是多余的。不需要读取循环,因为单个规范的 read() 请求可以确保 returning 与一行输入。