二进制串口读取c中丢失的字节 [英] Binary serial port read missing bytes in c

查看:43
本文介绍了二进制串口读取c中丢失的字节的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将二进制数据从 arduino 发送到运行此代码的串行端口.在十六进制模式下使用cutecom可以清楚地读取我在该串行端口上的期望.如下图.

I am sending binary data from an arduino to a serial port where this code is running. Using cutecom in hex mode can clearly read what I expect on that serial port. As shown below.

00000000: 24 04 85 ab 47 43 04 04   24 04 85 ab 47 43 04 04 
00000010: 24 04 85 ab 47 43 04 04   24 04 85 ab 47 43 04 04 

到这里没有问题.我不相信我需要提供 arduino 代码.

No problems up to here. I don't believe I need to give the arduino code.

我正在尝试用 c 读取相同的内容.但是下面的代码只打印这个:

I am trying to read the same thing with c. However the code below only prints this:

24 85 ab 47 43 24 85 ab 47 43 24 85 ab 47 43

出于某些原因,它跳过了 04.有什么想法吗?

For some reasons it is skipping the 04. Any ideas?

#include <stdio.h>
#include <fcntl.h>   /* File Control Definitions           */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h>  /* UNIX Standard Definitions      */ 
#include <errno.h>   /* ERROR Number Definitions           */
#include <signal.h>
#include <string.h>
#include <stdint.h>

int open_serial(char *port, int baud);

void main(void)
{
    int tty = open_serial("/dev/ttyUSB0", B115200);
    uint8_t buff[256];   /* Buffer to store the data received              */
    int  n;    /* Number of bytes read by the read() system call */

    while (1) {
        n = read(tty, &buff, sizeof buff); 
        if (n > 0){
            //printf("-%d-\n ", n);
            for(int i=0;i<n;i++){   
                printf("%02x ", buff[i]);
            }
            fflush(stdout); 
        }
    }
}

int open_serial(char *port, int baud)
{

    int fd = open( port, O_RDWR | O_NOCTTY);    

    if(fd == -1)                        /* Error Checking */
        printf("\n  Error! in Opening tty  ");

    struct termios SerialPortSettings;  /* Create the structure                          */

    tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */

    /* Setting the Baud rate */
    cfsetispeed(&SerialPortSettings,B115200); /* Set Read  Speed as 115200                       */
    cfsetospeed(&SerialPortSettings,B115200); /* Set Write Speed as 115200                       */

    /* 8N1 Mode */
    SerialPortSettings.c_cflag &= ~PARENB;   /* Disables the Parity Enable bit(PARENB),So No Parity   */
    SerialPortSettings.c_cflag &= ~CSTOPB;   /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
    SerialPortSettings.c_cflag &= ~CSIZE;    /* Clears the mask for setting the data size             */
    SerialPortSettings.c_cflag |=  CS8;      /* Set the data bits = 8                                 */

    SerialPortSettings.c_cflag &= ~CRTSCTS;       /* No Hardware flow Control                         */
    SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines       */ 


    SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);          /* Disable XON/XOFF flow control both i/p and o/p */
    SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /* Non Cannonical mode                            */

    SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/

    /* Setting Time outs */
    SerialPortSettings.c_cc[VMIN] = 10; /* Read at least 10 characters */
    SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly   */


    if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
        printf("\n  ERROR ! in Setting attributes");
    else
        printf("\n  BaudRate = 115200 StopBits = 1 Parity   = none\n");

    /*------------------------------- Read data from serial port -----------------------------*/

    tcflush(fd, TCIFLUSH);   /* Discards old data in the rx buffer            */
    return fd;
}

推荐答案

我正在将二进制数据从 arduino 发送到运行此代码的串行端口.

I am sending binary data from an arduino to a serial port where this code is running.

二进制数据的传输需要将 POSIX 串行终端配置为非规范(又名原始)模式.

Transmission of binary data necessitates that the POSIX serial terminal be configured for non-canonical (aka raw) mode.

出于某些原因,它跳过了 04.有什么想法吗?

For some reasons it is skipping the 04. Any ideas?

值在 ASCII 控制字符范围(即 0x00 到 0x1F)范围内的数据的持续丢失几乎总是表明 termios 配置不正确.
由于数据丢失似乎在接收端,首先要验证的是非规范输入模式的正确规范.
你的代码有

Consistent loss of data that have values in the range of ASCII control characters (i.e. 0x00 through 0x1F) almost always indicates improper termios configuration.
Since the data loss seems to be on the reception side, the first thing to verify is the proper specification of non-canonical input mode.
Your code has

SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /* Non Cannonical mode                            */

赋值右边的属性都属于termios结构的c_lflag,而不是编码的c_iflag.
所以推测串行终端的默认模式是规范模式,由于这个错字,您的程序无法重新配置为非规范输入模式.

The attributes on the right-hand side of the assignment all belong to the c_lflag of the termios structure, rather than the c_iflag that is coded.
So presumably the default mode of the serial terminal is canonical mode, and your program fails to reconfigure to non-canonical input mode because of this typo.

您使用 cfmakeraw() 调用扩充代码的解决方法并不理想.
使用 cfmakeraw() 可以方便地避免您所犯的错误.
但是应该更正错误的语句,或者更好的是可以删除由 cfmakeraw() 调用而变得多余的操作.

Your workaround of augmenting your code with a cfmakeraw() call is not ideal.
The use of cfmakeraw() is convenient for avoiding mistakes like the one you have.
But the buggy statement should be corrected, or even better the operations that are made redundant by the cfmakeraw() call can be removed.

cfmakeraw() sets the terminal to something like the "raw" mode of the old 
Version 7 terminal driver: input is available character by character, echoing is 
disabled, and all special processing of terminal input and output characters is 
disabled. The terminal attributes are set as follows:

termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                | INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;

<小时>

顺便说一句,您的代码确实使用了推荐/首选的调用 tcgetattr() 方法,然后使用布尔运算设置/修改终端属性.使用 clean struc" 的建议不被认为是可移植的.


BTW your code does use the recommended/preferred method of calling tcgetattr(), and then setting/modifying the terminal attributes using Boolean operations. The suggestion of using a "clean struc" is not considered portable.

附录

这个 c_iflagICANON 错误的起源似乎是这个 xanthium.in 中的串口教程.早在 2016 年,作者就收到了有关该错误的通知,但并未费心修复.

The origin of this c_iflag and ICANON bug seems to be this serial port tutorial from xanthium.in. The author was notified back in 2016 of the bug, but has not bothered to fix it.

这篇关于二进制串口读取c中丢失的字节的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆