Данные последовательного порта повреждены при отправке определенного байта - PullRequest
0 голосов
/ 17 мая 2018

У меня есть сценарий 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);
} 

1 Ответ

0 голосов
/ 18 мая 2018

Ваша программа не может правильно выполнить инициализацию termios.
1. Необходимо инициализировать struct termios port_settings, вызвав tcgetattr () перед любыми изменениями.
2. Поскольку вы настроилинеканонический режим, VMIN должен быть определен.По какой-то причине у вас есть закомментированное утверждение.

Как видите, в конце есть несколько байтов (CRC), которые изменяются, ...

Похоже, что входной байт со значением 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 никогда не инициализируется должным образом, эти параметры, возможно, настроены для вашей программы.


ADDENDUM

Поэтому я назвал "tcgetattr (fd, & port_settings"));»ПРАВО ПОСЛЕ ОБЪЯВЛЕНИЯ И ПРАВО ДО ПЕРЕД открытием порта.

Нет, несмотря на любые положительные результаты, которые являются нелогичным кодом.
Дескриптор файла недействителен, и если код возврата был проверен (как и все системные вызовы), вы бы поняли, что 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;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...