如何使用C正确接收串口数据?

How to properly received serial data using C?

更新1: 简短版本:当串口设备发送 0x23 0x0 0x3b 0xa 时,我的程序可以读取所有 4 个字节(serial_getBiaststatus 示例)。当串行设备发送第二个字节为 0x3 时,我的设备只接收最后 2 个字节(0x3b 0xaserial_getLedstatus 示例)

长版 我有一个串行设备,可以回答一些要设置的命令和一些选项的 return 状态,其中一个选项是 LED。 要设置选项,我必须发送:

0xFF, 0x55, 0x<ADDR>, 0x<DATA>

(设备 return在 SET 命令后没有任何内容)

要阅读此选项,我必须发送:

0xFF, 0xAA, 0x<ADDR>, 0xFF

设备将 return:

0x23, 0x<VALUE>, 0x3B, 0x0A

除了这些选项之外,设备将不断发送以换行符结尾的数据。所以我需要禁用这些发送特殊寄存器(地址 0x9)的数据以禁用此 log 数据,否则我将无法从 LED 或其他设备选项读取状态。

好的,我的问题是:当我将 LED 状态设置为 3 (0x3) 时,我无法从设备读取状态!我的程序只从设备读取 2 个字节,而不是 4 个字节。如果我使用 01 值,则状态 return 正确! (4字节)。 我确信该设备正在发送正确的值(4 个字节,在这种情况下,0x23, 0x03, 0x3B, 0x0A 因为我已经在 windows 中使用 YAT 终端程序对其进行了测试并且值是正确的.....所以这不是设备问题,而是我的程序问题。

到目前为止,这是我的代码。我正在将与串行设备的连接设置为非规范模式到 set/read 设备选项(特别是因为 'set' 没有 return 任何东西,所以我不能做 read() 和设置超时)...设置选项后,我将设备连接设置为规范模式并获取必要的数据,直到程序结束。 一切正常,除非我需要在设备选项中使用“3”……如果在设备上设置选项本身(我可以直观地验证 LED),但是当我尝试读取这个 LED 值时,只有最后 2 个字节是 returned (0x3b 0xa),而不是值(4 的第二个字节)。


int serial_set_interface_attribs(int fd, int canonical) {    
    pthread_mutex_lock(&m_serial);    
    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        pthread_mutex_unlock(&m_serial);
        return -1;
    }


    switch (serial_speed) {

        case 57600:
            cfsetospeed(&tty, (speed_t) B57600);
            cfsetispeed(&tty, (speed_t) B57600);
            jonis_log_level(2, "Serial device speed set to 57600\n");
            break;

        case 115200:
            cfsetospeed(&tty, (speed_t) B115200);
            cfsetispeed(&tty, (speed_t) B115200);
            jonis_log_level(2, "Serial device speed set to 115200\n");
            break;

        case 230400:
            cfsetospeed(&tty, (speed_t) B230400);
            cfsetispeed(&tty, (speed_t) B230400);
            jonis_log_level(2, "Serial device speed set to 230400\n");
            break;

        case 460800:
            cfsetospeed(&tty, (speed_t) B460800);
            cfsetispeed(&tty, (speed_t) B460800);
            jonis_log_level(2, "Serial device speed set to 460800\n");
            break;

        case 500000:
            cfsetospeed(&tty, (speed_t) B500000);
            cfsetispeed(&tty, (speed_t) B500000);
            jonis_log_level(2, "Serial device speed set to 500000\n");
            break;

        case 576000:
            cfsetospeed(&tty, (speed_t) B576000);
            cfsetispeed(&tty, (speed_t) B576000);
            jonis_log_level(2, "Serial device speed set to 576000\n");
            break;

        case 921600:
            cfsetospeed(&tty, (speed_t) B921600);
            cfsetispeed(&tty, (speed_t) B921600);
            jonis_log_level(2, "Serial device speed set to 921600\n");
            break;


        default:
            cfsetospeed(&tty, (speed_t) B921600);
            cfsetispeed(&tty, (speed_t) B921600);
            jonis_log_level(2, "Serial device speed set to 921600\n");

    }


    tty.c_cflag |= CLOCAL | CREAD;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8; /* 8-bit characters */
    tty.c_cflag &= ~PARENB; /* no parity bit */
    tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */

    if (canonical == 1) {
        jonis_log_level(1,"Interface in canonical mode\n");
        tty.c_lflag |= ICANON | ISIG; /* canonical input */
        tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
    } else {
        jonis_log_level(1,"Interface in non-canonical mode\n");
        //Disable ECHO
        tty.c_lflag &= ~(ICANON);
        tty.c_lflag &= ~(ECHO | ECHOE);        
        tty.c_cc[VMIN] = 0;
        tty.c_cc[VTIME] = 10;

    }
    
    //tty.c_iflag &= ~IGNCR;  /* preserve carriage return - estava comentado */ 
    tty.c_iflag &= ~INPCK;
    tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */

    tty.c_oflag &= ~OPOST;

    tty.c_cc[VEOL] = 0;
    tty.c_cc[VEOL2] = 0;
    tty.c_cc[VEOF] = 0x04;



    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));        
        pthread_mutex_unlock(&m_serial);
        return -1;
    }    
    pthread_mutex_unlock(&m_serial);    
    return 0;
}

void *serial_threadGetSerialData(void *argv) {

    AN_NOTUSED(argv);
    // signal handlers:
    signal(SIGINT, jonisSigintHandler);
    signal(SIGTERM, jonisSigtermHandler);
    
    if (pthread_mutex_init(&m_serial, NULL) != 0) {
        printf("\n mutex init failed\n");
        exit(EXIT_FAILURE);
    }
        
    if (serial_device == NULL) {
        jonis_log("Can't start serial communication. Device is null.\n");
        return NULL;
    }


    fd_serial = open(serial_device, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd_serial < 0) {
        jonis_log("Error opening %s: %s\n", serial_device, strerror(errno));
        return NULL;
    }    
    
    
    // Configue LED and other settings           
    struct sdongle_version *tmp = serial_getDongleVersion();
    if (tmp == NULL) {
        jonis_log_level(1, "Function to get dongle FW version returned NULL!\n");
    } else {        
        jonis_log("Serial dongle FW version: %d.%d\n",tmp->major,tmp->minor);
    }                    

    // Set LED to 3    
    serial_setLedstatus(3); 
    // Read LED status from device
    serial_getLedstatus();

    if (serial_bias_t == 1) {
        // Enable Bias T
        serial_setBiaststatus(1);
        printf("Definido biast como 1!\n");
    } else {
        // Disabl Bias-T
        serial_setBiaststatus(0);
        printf("Definido biast como 0!\n");
    }
    (...)
    
    serial_enableData();
            
    /* simple canonical input */
    do {


        char buf[150];
        //unsigned char *p;
        int rdlen;

        rdlen = read(fd_serial, buf, sizeof (buf) - 1);
        if (rdlen > 0) {
            buf[rdlen] = 0;
            //printf("Read (main) %d bytes: ", rdlen);
            /* first display as hex numbers then ASCII */
            //for (p = buf; rdlen-- > 0; p++) {
            //    printf(" 0x%x", *p);
            //    if (*p < ' ')
            //        *p = '.';   /* replace any control chars */
            //}
            //printf("\n    \"%s\"\n\n", buf);
            //printf("%s", buf);

            // Proccess data
            //decodeRaw(buf);


        } else if (rdlen < 0) {
            jonis_log("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else { /* rdlen == 0 */
            //printf("(main)Nothing read. EOF?\n");            
        }
        /* repeat read */
    } while (!MyProgram.exit);

    jonis_log_level(1,"Reseting Serial Dongle options.\n");    
    if (serial_setLedstatus(0) != 1) {
        jonis_log("Error reseting serial dongle LED status\n");
    }    
    
    if (fd_serial > -1) {
        close(fd_serial);
    }

    pthread_exit(EXIT_SUCCESS);

}

int serial_disableData(void) {

    if (fd_serial < 0) {
        jonis_log("Error disabling serial data: invalid serial connection.\n");
        return -1;
    }

    char disable_data[] = {0xFF, 0x55, 0x09, 0x01};

    serial_set_interface_attribs(fd_serial, 0);    
    jonis_log_level(1, "Canonical mode set to 0\n");    
    pthread_mutex_lock(&m_serial);
    
    int wlen = 0;

    wlen = write(fd_serial, disable_data, 4);
    if (wlen != 4) {
        jonis_log("Error from write: %d, %d\n", wlen, errno);
        pthread_mutex_unlock(&m_serial);
        return -1;
    }
    tcdrain(fd_serial); /* delay for output */
    
    usleep(100000);
    
    // Clear current buffer
    char buf[9000] = {0};
    int total_cleared = 0;

    int rdlen = 0;
    rdlen = read(fd_serial, buf, sizeof (buf) - 1);
    total_cleared = rdlen;
    jonis_log_level(1, "Clearing buffer. Current counter: %d. Returned: %d\n", total_cleared, rdlen);
    while (rdlen != 0) {
        rdlen = read(fd_serial, buf, sizeof (buf) - 1);
        total_cleared = total_cleared + rdlen;
        usleep(100000);
        jonis_log_level(1, "Clearing buffer. Current counter: %d. Returned: %d\n", total_cleared, rdlen);
    }

    jonis_log_level(1, "Finished sending 'disable_log' command do serial device! Data length in buffer: '%d'\n", total_cleared);
    pthread_mutex_unlock(&m_serial);
    return 1;

}

int serial_enableData(void) {

    if (fd_serial < 0) {
        jonis_log("Error enabling serial data: invalid serial connection.\n");
        return -1;
    }

    serial_set_interface_attribs(fd_serial, 1);
    pthread_mutex_lock(&m_serial);
    char disable_data[] = {0xFF, 0x55, 0x09, 0x00};

    int wlen = 0;

    wlen = write(fd_serial, disable_data, 4);
    if (wlen != 4) {
        jonis_log("Error from write: %d, %d\n", wlen, errno);
        pthread_mutex_unlock(&m_serial);
        return -1;
    }
    tcdrain(fd_serial); /* delay for output */

    jonis_log_level(1, "Finished sending 'enable_log' command do serial device!\n");

    pthread_mutex_unlock(&m_serial);
    return 1;

}

struct sdongle_version *serial_getDongleVersion(void) {

    if (fd_serial < 0) {
        jonis_log("Error getting serial data: invalid serial connection.\n");
        return NULL;
    }

    if (serial_disableData() < 0) {
        jonis_log("Could not disable data\n");
        return NULL;
    }

    char disable_data[] = {0xFF, 0xAA, 0x0F, 0xFF};
    int wlen = 0;

    wlen = write(fd_serial, disable_data, 4);
    if (wlen != 4) {
        jonis_log("Error from write: %d, %d\n", wlen, errno);
        return NULL;
    }
    tcdrain(fd_serial); /* delay for output */
    usleep(100000);

    char buf[150];
    char *p;
    int rdlen;

    //rdlen = read(fd_serial, buf, sizeof (buf) - 1);
    //if (rdlen > 0) {
    while ((rdlen = read(fd_serial, buf, sizeof (buf) - 1)) < 4 || !Modes.exit) {    
        buf[rdlen] = 0;
        //printf("(Version) Read '%d' bytes: ", rdlen);
        if (rdlen == 4 && buf[0] == 0x23 && buf[2] == 0x3b && buf[3] == 0xa) {
            jonis_log_level(1, "Version string found!\n");
            int minor = buf[1] << 2 >> 2;
            int major = buf[1] >> 6;
            jonis_log_level(1, "Minor version: %d, Major version: %d\n", minor, major);

            struct sdongle_version *ver = malloc(sizeof (struct sdongle_version));
            ver->major = major;
            ver->minor = minor;
            return ver;
        }

        /* first display as hex numbers then ASCII */
        for (p = buf; rdlen-- > 0; p++) {
            //printf(" 0x%x", *p);            
        }
        //printf("\n    (version)Value: \"%s\"\n\n", buf);


    }

    return NULL;

}


int serial_getLedstatus(void) {

    if (fd_serial < 0) {
        jonis_log("Error getting serial data: invalid serial connection.\n");
        return -1;
    }

    if (serial_disableData() < 0) {
        jonis_log("Could not disable data\n");
        return -1;
    }

    char led_status_data[] = {0xFF, 0xAA, 0x8, 0xFF};

    int wlen = 0;

    wlen = write(fd_serial, led_status_data, 4);
    if (wlen != 4) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd_serial); /* delay for output */
    sleep(1);

    char buf[150] = {0};
    char *p = NULL;
    int rdlen = 0;
    int tries = 0;
    
    while (((rdlen = read(fd_serial, buf, sizeof (buf) - 1)) < 4 && !Modes.exit) && tries <= 5) {
        jonis_log_level(1,"(led) Read '%d' bytes: \n", rdlen);
        tries++;
        // Force rwrite command
        wlen = write(fd_serial, led_status_data, 4);
        tcdrain(fd_serial);
        sleep(1);        
        buf[rdlen] = 0;                
        /* first display as hex numbers then ASCII */
        for (p = buf; rdlen-- > 0; p++) {
            if (debug_level >= 1) { 
                printf(" 0x%x", *p); 
            }
        }
        if (debug_level >= 1) { 
            printf("\n");
            jonis_log_level(1,"    (led)Value: \"%s\"\n\n", buf);
        }
    } 
    
    if (rdlen > 0) {
        jonis_log_level(1,"Debug -> Received chars in LED Status\n");
        jonis_log_level(1,"(led) Read '%d' bytes: ", rdlen);        
        buf[rdlen] = 0;                
        /* first display as hex numbers then ASCII */
        for (p = buf; rdlen-- > 0; p++) {
            if (debug_level >= 1) { 
                printf(" 0x%x", *p);            
            }
        }
        if (debug_level >= 1) { 
            printf("\n");
            jonis_log_level(1,"    (led)Value: \"%s\"\n\n", buf);
        }
    }
    
    jonis_log_level(1,"LED Status (function): %d\n",buf[1]);
    return buf[1];

}


int serial_setLedstatus(char led_status) {

    
    if (fd_serial < 0) {
        jonis_log("Error getting serial data: invalid serial connection.\n");
        return -1;
    }

    if (serial_disableData() < 0) {
        jonis_log("Could not disable data\n");
        return -1;
    }    
    
    char led_data[] = {0xFF, 0x55, 0x08, led_status};
    int wlen = 0;
    wlen = write(fd_serial, led_data, 4);
    if (wlen != 4) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd_serial); /* delay for output */
    //serial_getLedstatus();
    return 1;
}

int serial_getBiaststatus(void) {

    if (fd_serial < 0) {
        jonis_log("Error getting serial data: invalid serial connection.\n");
        return -1;
    }

    if (serial_disableData() < 0) {
        jonis_log("Could not disable data\n");
        return -1;
    }

    char bias_status_data[] = {0xFF, 0xAA, 0x07, 0xFF};

    int wlen = 0;

    wlen = write(fd_serial, bias_status_data, 4);
    if (wlen != 4) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd_serial); /* delay for output */
    usleep(100000);

    char buf[150];
    char *p;
    int rdlen;

    rdlen = read(fd_serial, buf, sizeof (buf) - 1);
    if (rdlen > 0) {
        buf[rdlen] = 0;
        printf("(biast) Read '%d' bytes: ", rdlen);
        
        /* first display as hex numbers then ASCII */
        for (p = buf; rdlen-- > 0; p++) {
            printf(" 0x%x", *p);            
        }
        printf("\n    (biast)Value: \"%s\"\n\n", buf);


    } else if (rdlen < 0) {
        jonis_log("Error from read: %d: %s\n", rdlen, strerror(errno));
    } else { /* rdlen == 0 */
        //printf("(led)Nothing read. EOF?\n");        
    }    
    jonis_log_level(1,"BIAS-T Status (function): %d\n",buf[1]);
    return buf[1];

}


int serial_setBiaststatus(char biast_status) {

    if (fd_serial < 0) {
        jonis_log("Error getting serial data: invalid serial connection.\n");
        return -1;
    }

    if (serial_disableData() < 0) {
        jonis_log("Could not disable data\n");
        return -1;
    }

    char biast_data[] = {0xFF, 0x55, 0x07, biast_status};
    int wlen = 0;
    wlen = write(fd_serial, biast_data, 4);
    if (wlen != 4) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd_serial); /* delay for output */
    usleep(100000);
    
    if (serial_getBiaststatus() == biast_status) {    
        return 1;
    } else {
        return 0;
    }

}

这是 returned 文本(注意仅从 LED 状态接收到 2 个字节):

[2022-04-28 09:22:00] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:00] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:00] [serial_disableData]  Clearing buffer. Current counter: 1. Returned: 1
[2022-04-28 09:22:01] [serial_disableData]  Clearing buffer. Current counter: 1. Returned: 0
[2022-04-28 09:22:01] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '1'
[2022-04-28 09:22:01] [serial_getDongleVersion]  Version string found!
[2022-04-28 09:22:01] [serial_getDongleVersion]  Minor version: 34, Major version: 0
[2022-04-28 09:22:01]  Serial dongle FW version: 0.34
[2022-04-28 09:22:01] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:01] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:02] [serial_disableData]  Clearing buffer. Current counter: 0. Returned: 0
[2022-04-28 09:22:02] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '0'
[2022-04-28 09:22:02] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:02] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:03] [serial_disableData]  Clearing buffer. Current counter: 0. Returned: 0
[2022-04-28 09:22:03] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '0'
[2022-04-28 09:22:04] [serial_getLedstatus]  (led) Read '2' bytes:
 0x3b 0xa
[2022-04-28 09:22:05] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:05] [serial_getLedstatus]  (led) Read '2' bytes:
 0x3b 0xa
[2022-04-28 09:22:06] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:06] [serial_getLedstatus]  (led) Read '2' bytes:
 0x3b 0xa
[2022-04-28 09:22:07] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:07] [serial_getLedstatus]  (led) Read '2' bytes:
 0x3b 0xa
[2022-04-28 09:22:08] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:08] [serial_getLedstatus]  (led) Read '2' bytes:
 0x3b 0xa
[2022-04-28 09:22:09] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:09] [serial_getLedstatus]  (led) Read '2' bytes:
 0x3b 0xa
[2022-04-28 09:22:10] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:10] [serial_getLedstatus]  Debug -> Received chars in LED Status
[2022-04-28 09:22:10] [serial_getLedstatus]  (led) Read '2' bytes:  0x3b 0xa
[2022-04-28 09:22:10] [serial_getLedstatus]      (led)Value: ";
"

[2022-04-28 09:22:10] [serial_getLedstatus]  LED Status (function): 10
[2022-04-28 09:22:10] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:10] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:11] [serial_disableData]  Clearing buffer. Current counter: 0. Returned: 0
[2022-04-28 09:22:11] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '0'
[2022-04-28 09:22:12] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:12] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:13] [serial_disableData]  Clearing buffer. Current counter: 0. Returned: 0
[2022-04-28 09:22:13] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '0'
[2022-04-28 09:22:13] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:13] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:14] [serial_disableData]  Clearing buffer. Current counter: 0. Returned: 0
[2022-04-28 09:22:14] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '0'
[2022-04-28 09:22:14] [serial_set_interface_attribs]  Interface in non-canonical mode
[2022-04-28 09:22:14] [serial_disableData]  Canonical mode set to 0
[2022-04-28 09:22:15] [serial_disableData]  Clearing buffer. Current counter: 0. Returned: 0
[2022-04-28 09:22:15] [serial_disableData]  Finished sending 'disable_log' command do serial device! Data length in buffer: '0'
(biast) Read '4' bytes:  0x23 0x0 0x3b 0xa
    (biast)Value: "#"

[2022-04-28 09:22:15] [serial_getBiaststatus]  BIAS-T Status (function): 0
[2022-04-28 09:22:15] [serial_set_interface_attribs]  Interface in canonical mode
[2022-04-28 09:22:15] [serial_enableData]  Finished sending 'enable_log' command do serial device!


再次:设备正在 return 设置正确的字节数 (4),即使 os 选项的值是 3,但我的程序无法准备好所有这些字节,只有最后 2. 我在这里错过了什么? 谢谢!

Ok, my problem is: when I set my LED status do 3 (0x3), I could not read status from device!
...
I'm surre that the device is sending correct values (4 bytes, in this case, 0x23, 0x03, 0x3B, 0x0A ...

0x03也是名为ETX的ASCII控制码

termios 特殊字符 VINTR 的默认值为 0x03,也称为 ETX 和 Ctrl-C.
当 c_lflag 的 termios ISIG 属性被设置时,然后接收 VINTR 字符将导致进程获得 SIGINT 信号,并且该字符从输入缓冲区中删除。


if (canonical == 1) {
    ...
    tty.c_lflag |= ICANON | ISIG; /* canonical input */
    ...
} else {
    ...
    tty.c_lflag &= ~(ICANON);
    tty.c_lflag &= ~(ECHO | ECHOE);        
    ...
}

您的程序将 ISIG 属性设置为“规范”模式,否则 c_lflag 中的该属性保持不变。
显然 VINTR 仍然具有默认值 0x03

最终效果是 ISIG 属性始终启用,因此接收 0x03 将始终不可读。