Данные последовательного порта повреждены при отправке определенного байта

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

0

Решение

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


ДОПОЛНЕНИЕ

Поэтому я назвал «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;
}
2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]