c++ - 发送特定字节模式时串口数据损坏

标签 c++ pyserial uart crc32

我有一个发送数据流的 python 脚本,然后我有一个 Linux 嵌入式计算机接收数据(用 C++ 编写的代码)。大多数情况下它都能正常工作,但是,我注意到在发送特定字节模式 时我的数据已损坏。我已经为此苦苦挣扎了一段时间,我不知道如何解决。

Python 脚本(发送方):

serial = serial.Serial("COM2", 115200, timeout=5)
all_bytes = [0x63,0x20,0x72,0x69,0x67,0x68,0x74,0x73,0x20,0x61,0x6e,0x64,0x20,0x72,0x65,0x73,0x74,0x72,0x69,0x63,0x74,0x69,0x6f,0x6e,0x73,0x20,0x69,0x6e,0x0a,0x68,0x6f,0x77,0xff,0x20,0xf0,0x8b]

fmt = "B"*len(all_bytes)

byte_array = struct.pack(fmt,*all_bytes)

serial.write(byte_array)

C++代码(接收器)

typedef std::vector<uint8_t> ustring; // ustring = vector containing a bunch of uint8_t elements

// configure the port
int UART::configure_port()      
{
    struct termios port_settings;      // structure to store the port settings in

    cfsetispeed(&port_settings, B115200);    // set baud rates
    cfsetospeed(&port_settings, B115200);

    port_settings.c_cflag &= ~PARENB;    // set no parity, stop bits, data bits
    port_settings.c_cflag &= ~CSTOPB;
    port_settings.c_cflag &= ~CSIZE;
    port_settings.c_cflag |= CS8;
    port_settings.c_cflag |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

    port_settings.c_cc[VTIME]     =   10;                  // n seconds read timeout
    //port_settings.c_cc[VMIN]      =   0;     // blocking read until 1 character arrives 
    port_settings.c_iflag     &=  ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
    port_settings.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw
    port_settings.c_oflag     &=  ~OPOST;              // make raw

    tcsetattr(fd, TCSANOW, &port_settings);    // apply the settings to the port
    return(fd);
} 

int UART::uart_read(ustring *data,int buffer_size)
{
    // Buffer
    uint8_t * buf = new uint8_t[buffer_size];

    // Flush contents of the serial port
    //tcflush(fd, TCIOFLUSH);
    //usleep(1000);

    ustring data_received;
    // Read
    int n_bytes = 0;

    while (n_bytes < buffer_size)
    {
        int n = read( fd, buf , buffer_size );

        // Some bytes were read!
        if (n > 0)
        {
             n_bytes+=n;

             // Add to buffer new data!
             for( int i=0; i<n; i++ )
             {
                data_received.push_back(buf[i]);
             }
        }


    }


    // String received
    *data = data_received;
    cout << "Data received..." << endl;
    print_ustring(data_received);




    delete[] buf;

    return read_valid;

}


int main()
{ 
    UART uart_connection;

    vector<uint8_t> data;
    vector<uint8_t> *data_ptr = &data;
    int status = uart_connection.uart_read(data_ptr,36);


    return 0;
} 

这是正在发生的事情:

如果我发送以下字节(来自 python):

0x632072696768747320616e64207265737472696374696f6e7320696e0a686f77ff20f08b

这是我收到的(在 C++ 程序中):

0x632072696768747320616e64207265737472696374696f6e7320696e0a686f77ffff20f0

如您所见,末尾的几个字节(CRC)发生了变化,其余的似乎没问题。但这并不总是发生,它只在发送某些特定的字节模式时发生。

假设我发送了以下内容(一些其他模式):

0x6868686868686868686868686868686868686868686868686868686868686868b18cf5b2

我得到的正是我在上述模式中发送的内容!

你认为可能是 Pyserial 将我的无符号字节更改为 ASCII 吗?我不知道发生了什么。我已经为此苦苦挣扎了好几天!

编辑

对于任何感兴趣的人来说,问题显然是 struct termios 需要在声明后立即初始化。

这是解决它的代码:

// configure the port
int UART::configure_port()      
{
    struct termios port_settings;      // structure to store the port settings in
    tcgetattr(fd, &port_settings);
    // Open ttys4
    fd = open("/dev/ttyS4", O_RDWR | O_NOCTTY );

    if(fd == -1) // if open is unsucessful
    {
        //perror("open_port: Unable to open /dev/ttyS0 - ");
        printf("open_port: Unable to open /dev/ttyS4. \n");
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
        /* get the current options */
        printf("port is open.\n");


        cfsetispeed(&port_settings, B9600);    // set baud rates
        cfsetospeed(&port_settings, B9600);

        port_settings.c_cflag &= ~PARENB;    // set no parity, stop bits, data bits
        port_settings.c_cflag &= ~CSTOPB;   //Stop bits = 1 
        port_settings.c_cflag &= ~CSIZE;    // clear mask
        port_settings.c_cflag |= CS8;       // data bits = 8
        port_settings.c_cflag &= ~CRTSCTS;   // Turn off hardware flow control
        port_settings.c_cflag |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

        port_settings.c_cc[VMIN]      =   0;     // blocking read until 1 character arrives 
        // port_settings.c_cc[VTIME]     =   10;                  // n seconds read timeout

        port_settings.c_iflag     &=  ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
        port_settings.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw -- NON Cannonical mode
        // port_settings.c_iflag |= IGNPAR; // Input parity options
        // port_settings.c_oflag     &=  ~OPOST;              // make raw

        tcsetattr(fd, TCSANOW, &port_settings);    // apply the settings to the port
    }
    return(fd);
} 

最佳答案

您的程序未能正确执行 termios 初始化。
1. struct termios port_settings 需要在任何修改之前通过调用 tcgetattr() 进行初始化。
2. 由于您配置了非规范模式,因此需要定义 VMIN。无论出于何种原因,您已将该声明注释掉。

As you can see there are a few bytes at the end (the CRC) that are changed,...

看起来像是重复了一个值为 0xFF 的输入字节。
当设置了 PARMRK 和 INPCK 而未设置 IGNPAR 时会发生这种情况。
根据 termios 手册页:

PARMRK  If  this  bit  is  set,  input bytes with parity or framing errors are marked when passed to the program. 
        ...  
        Therefore, a valid byte \377 is passed to the program as two bytes, \377 \377, in this case.

由于您的 termios 结构从未正确初始化,这些设置可能是为您的程序配置的。


附录

So I called "tcgetattr(fd, &port_settings);" RIGHT AFTER declaring it, and RIGHT BEFORE opening the port.

不,尽管任何积极的结果都是不合逻辑的代码。
文件描述符无效,如果检查了返回码(所有系统调用都应该检查),您会发现 tcgetattr() 失败。

cfmakeraw() 是指示非规范模式的正确 termios 配置的显着值的指南:

       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;

所以你的程序应该是这样的:

    ...  
    if (tcgetattr(fd, &port_settings) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&port_settings, B9600);
    cfsetispeed(&port_settings, B9600);

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

    /* setup for non-canonical mode */
    port_settings.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    port_settings.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    port_settings.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    port_settings.c_cc[VMIN] = 1;
    port_settings.c_cc[VTIME] = 1;

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

关于c++ - 发送特定字节模式时串口数据损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50398638/

相关文章:

c++ - 没有返回对函数参数的引用的 clang 警告

python - FT232RL 上的流量控制

c - linux 中的串行缓冲区有多长?

c++ - 为什么 C++ 类需要在类范围之外定义静态字段(数据成员)?

c++ - 我如何判断到目前为止是否有任何 BOOST_CHECK 测试失败?

c++ - 字符串变量顺序

python - Debian - python 有两个 dist-packages?我应该使用哪一个?

python - linux-pyserial :could not open port/dev/ttyAMA0 No such file or directory

转换CS_UID并将其写入UART

c - 如何获取字符串大小