簡體   English   中英

C串行讀取找不到回車符“ \\ r”

[英]C serial read does not find carriage return “\r”

我現在正在努力通過串行初始化ELM237 OBD2適配器。 我連接的設備發送的響應始終以回車結尾。 我可以毫無問題地發送命令。 響應也由設備按預期發送。 我試圖從串行端口讀取,直到讀取回車。 我可以在串行監視器中看到該命令已正確發送,並且收到完整的消息,包括回車。 但是我的代碼無法識別回車符。

這是我在串行監視器上收到的信息:

[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 = '\0';
            }
            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);
}

我可以在串行監視器中看到該命令已正確發送,並且收到完整的消息,包括回車。 但是我的代碼無法識別回車符。

程序的輸出確實確認了@JonathanLeffler的評論。 收到的回車符將轉換為換行符,從而終止規范的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);

禁用回車和換行翻譯,並且絕對不要忽略回車。


請注意,您的代碼確實禁用了硬件流控制,但是使軟件流控制(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 ... ELM327 v1.5,它只會打印M327 v1.5

(1)回聲已關閉,因此除非連接的設備回顯命令, 否則 read()不會接收已發送的“ ATZ \\ r”。 這不太可能,因為此類設備通常配置為在受計算機程序而非人為控制時不回聲。

(2) printf()正在顯示buffer的全部內容,其中包括EOL字符,例如回車符。 輸出此接收到的\\r並與無關的空格(在字符串說明符和換行符之間)結合起來,是對早期的printf()輸出的一種掩蓋。

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

除非打算覆蓋現有的行,否則很少使用回車符本身(即沒有換行符)(在Linux中)。 在顯示單獨回車時,您需要意識到后果。

一種簡單的解決方案是切換到Linux行終止,並將回車符轉換為換行符(即啟用ICRNL而不是禁用它)。 並將\\r的搜索更改為\\n

實際上,當正確配置(阻塞)規范輸入時,級聯讀取緩沖區和搜索回車符都是多余的。 不需要讀取循環,因為可以確保單個規范的read()請求返回一行輸入。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM