我正在编写一个程序,通过串口与现有设备进行通信,我注意到一个奇怪的模式。正在使用真正的串行端口和USB转串口适配器进行测试。我得到了相同的结果。
启动计算机或插入适配器后,串口通信正常工作。我可以将二进制数据发送到设备,然后获得响应。只要需要,程序就可以继续与设备通信。
但是,一旦程序结束(干净地关闭端口),再次运行程序会导致无法通信。它可以很好地访问串口,但它回来的只是垃圾。
(奇怪的是,垃圾似乎是与调制解调器命令混合的二进制数据,如ATE0Q0S0 = 0,这没有任何意义。再次,我不需要重置设备与它通信,只是端口,所以我不喜欢我不知道它来自哪里。)
重启或拔出设备电源无效。只有当我重新启动计算机或重置USB设备(通过拔出或驱动程序重置)时,我才能再次运行该程序并使其成功通信。
这会导致什么?从结果来看,我只能假设串口在使用后没有处于干净状态,但除了重新应用ioctl属性和关闭文件描述符之外,我找不到有关正确清理串口状态的文档。使用,我已经做过了。
也许串口端口引脚或其他东西?我不知道我将如何测试,或者为什么它会发生。
我目前的“解决方案”是坚持使用USB适配器,让我的程序在尝试使用串口之前执行USB驱动程序重置,但我希望有更好的解决方案。
修改
根据要求,这是我用来测试串口读/写的C程序。
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
// Saved termios that we can re-apply when we exit
struct termios savedPortAttributes;
// Set the serial port attributes so we can use it
void setPortAttributes( int fd )
{
struct termios tty;
memset( &tty, 0, sizeof(tty) );
if ( tcgetattr( fd, &tty ) != 0 ) {
printf( "tcgetaddr error: $i\n", errno );
return;
}
cfsetispeed( &tty, B9600 );
cfsetospeed( &tty, B9600 );
cfmakeraw( &tty );
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 5;
if ( tcsetattr( fd, TCSANOW, &tty ) != 0 ) {
printf( "tcsetaddr error: $i\n", errno );
return;
}
if ( tcflush( fd, TCIOFLUSH ) != 0 ) {
printf( "tcflush error: $i\n", errno );
return;
}
}
void test( int fd )
{
// Send a sample MODBUS command
printf( "Writing command\n" );
char sendBuffer[] = { 0x01, 0x03, 0x00, 0x0B, 0x00, 0x02, 0xB5, 0xC9 };
int bytesWritten = write( fd, sendBuffer, sizeof(sendBuffer) );
if ( bytesWritten < 0 ) {
printf( "Error writing command.\n" );
return;
}
// We don't want to wait more than 1000ms for a response
struct timespec spec;
clock_gettime( CLOCK_MONOTONIC, &spec );
int64_t startMs = spec.tv_sec * 1000 + round( spec.tv_nsec / 1.0e6 );
// Read data back from the port
printf( "Reading from port...\n" );
unsigned char buffer[1024];
int bufferOffset = 0;
int count = 0;
while ( 1 ) {
count = read( fd, &buffer[bufferOffset], sizeof(buffer) - bufferOffset );
if ( count < 0 ) {
printf( "Error reading command.\n" );
return;
}
if ( count > 0 ) {
printf( "Bytes read: " );
for ( int i = bufferOffset; i < bufferOffset + count; i++ ) {
printf( "%02x ", buffer[i] );
}
printf( "\n" );
}
bufferOffset += count;
// Test code. If we receive part of a valid MODBUS response, grab the
// field length byte so we know if we're done reading
if ( bufferOffset >= 3 && buffer[0] == 1 && buffer[1] == 3 ) {
int messageLength = buffer[2];
if ( bufferOffset >= messageLength + 5 ) {
break;
}
}
// If it's been 1000ms, stop reading
clock_gettime( CLOCK_MONOTONIC, &spec );
int64_t timeMs = spec.tv_sec * 1000 + round( spec.tv_nsec / 1.0e6 );
//printf( "%" PRId64 " , %" PRId64 "\n", startMs, timeMs );
if ( timeMs - startMs > 1000 ) {
break;
}
}
}
void main()
{
printf( "Opening port\n" );
int fd = open( "/dev/ttyUSB0", O_RDWR|O_NOCTTY );
if ( fd == -1 ) {
printf( "Unable to open port.\n" );
return;
}
tcgetattr( fd, &savedPortAttributes );
setPortAttributes( fd );
test( fd );
test( fd );
tcsetattr( fd, TCSANOW, &savedPortAttributes );
close( fd );
}