我的任务是通过 RS485 2 线系统实现 ModBus 协议。(实际上是三根线,A/B 和 GND)。ModBus 不是重点,但在此之前的步骤……通过接口进行简单的 I/O。
我正在使用 FTDI USB-RS485 转换器将 Linux 主机(不可互换)连接到 Windows 主机(可与另一台 Linux 主机互换,但我想避免这种情况)
编码应该是 19200, 8, n, 1。但它似乎不起作用。
我手头没有确切的代码,但在 Linux 上我正在这样做:
int fd = open("/dev/ttyS3", O_RDWR | O_CTTY);
if(fd == -1) return "Error while opening the port";
接下来,我配置端口。
struct termios tty;
tcgetattr(fd, &tty);
cfsetispeed(&tty, B19200);
cfsetospeed(&tty, B19200);
tty.c_cflag = CS8; //Empties the cflags and sets the character width.
tty.c_cflag |= (CLOCAL | CREAD); //Sets 'recommended' options.
tty.c_lflag = 0;
tty.c_iflag = 0;
tty.c_oflag = 0;
tcgetattr(fd, TCSANOW, &tty);
目前还没有计划奇偶校验和流量控制,因为最终结果将连接到一个低级电路板,我需要自己处理信号。此外,没有任何电线可以实现“不受限制的通信”。(毕竟我不希望 XON/XOFF 字符限制我可以传输的字节范围)
所有这些功能都正确执行并设置了数据。
在 Windows 上,我像这样打开串行端口:
DCB SP;
HANDLE hSerial = CreateFile("COM6", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hSerial == INVALID_HANDLE_VALUE) return "Error while opening the port";
GetCommState(hSerial, &SP);
奇偶校验和流量控制被禁用。字节大小设置为 8。
编辑:既然有人问过,这是我在 Windows 上的波特率代码(来自内存) SP.DCBlength= sizeof(SP); SP.BaudRate = 19200; SP.奇偶校验 = 无奇偶校验;SP.StopBits = ONESTOPBIT;SetCommState(hSerial, &SP);
同样,所有这些功能都可以完美运行。
现在,对于让我头疼的测试用例。
在 Linux 主机上,我创建了一个 256 字节大小的字节缓冲区。这个缓冲区填充了从 0 到 255 的字符值......然后通过网络通过写入发送。同时,对方正在用“ReadFile”等待数据到达。
使用此配置,对于“其他 Linux 主机”以及 Windows 主机,256 字节到达......但是它不是 0-255 的数字,而是 00 06 等。
当我在设置我真正想要的选项之前将 termios 结构的所有成员设置为 0 时,我可以让 Linuxhost 工作。我猜,这是因为控制字符......但是如果我这样做,Windows 主机要么只接收 256 个字节中的 4 个。
正如我所说,不幸的是我手头没有代码。如果有人知道我可以从什么角度解决这个问题,我将不胜感激。一旦我再次可以访问它,我将发布更多代码。
我如何实现读取操作:
DWORD nBytes = 0;
char Buffer[256], *ptr = Buffer;
int Rem = 256;
while(Rem) {
ReadFile(hSerial, ptr, Rem, &nBytes, 0);
Rem -= nBytes;
ptr += nBytes;
}
//Evaluate Buffer
需要注意的是,我确实设置了超时,但不记得确切的值。
编辑:因为我现在可以再次访问我的工作地点,所以这是实际的(当前)代码。
const char *InitCOM(const char *TTY) {
struct termios tty;
hSerial = open(TTY, O_RDWR | O_NOCTTY | O_NDELAY);
if(hSerial == -1) return "Opening of the port failed";
fcntl(hSerial, F_SETFL, 0);
if(tcgetattr(hSerial, &tty) != 0) return "Getting the parameters failed.";
if(cfsetispeed(&tty, B19200) != 0 || cfsetospeed(&tty, B19200) != 0) return "Setting the baud rate failed.";
//CFlags
//Note: I am full aware, that there's an '=', and that it makes the '&=' obsolete, but they're in there for the sake of completeness.
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; //8-bit characters
tty.c_cflag |= (CLOCAL | CREAD);und erlaubt 'Lesen'.
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
//Input Flags
tty.c_iflag &= ~IGNBRK;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
//Local Flags
tty.c_lflag = 0;
//Output Flags
tty.c_oflag = 0;
//Control-Characters
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 5;
if(tcsetattr(hSerial, TCSAFLUSH, &tty) != 0) return "Setting the new parameters failed";
return NULL;
}
至于实际的发送/接收代码:
int main(int argc, char* argv[]) {
#if defined FOR_PC
const char *err = InitCOM("/dev/ttyUSB0");
#else
const char *err = InitCOM("/dev/ttyS3");
#endif
if(err) printf("Error while initalizing: %s ErrNum: %d\n", err, errno);
else {
/*unsigned char C[256]; //Original code with the array
int nBytes;
#ifdef FOR_PC
int Rem = 256, ReqCount = 0;
unsigned char *ptr = C;
while(Rem > 0) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(hSerial, &fds);
select(hSerial+1, &fds, NULL, NULL, NULL);
nBytes = read(hSerial, ptr, Rem);
if(nBytes > 0) {
Rem -= nBytes;
ptr += nBytes;
++ReqCount;
}
}
printf("Number of received Bytes: %d in %d sends.\n\n", 256 - Rem, ReqCount);
for(int i = 0; i < 256; ++i) {
printf("%02X ", C[i]);
if((i%32) == 31) printf("\n");
}
#else
for(int i = 0; i < 256; ++i) C[i] = i;
nBytes = write(hSerial, C, 256);
printf("\nWritten Bytes: %d\n", nBytes);
#endif*/
//Single-Byte Code
unsigned char C = 0x55;
#ifdef FOR_PC
while(true) { //Keeps listening
fd_set fds;
FD_ZERO(&fds);
FD_SET(hSerial, &fds);
select(hSerial+1, &fds, NULL, NULL, NULL);
read(hSerial, &C, 1);
printf("Received value 0x%02X\n", C);
}
#else
write(hSerial, &C, 1); //Sends one byte
#endif
close(hSerial);
}
return 0;
}
至于示波器:我已经测试了发送的两个方向。两人都非常出色地完成了他们的工作。
0x55 的信号在 50 微秒的长度上是一个恒定的 Up/Down(应该如此,因此设置波特率也没有问题)。
那么我的“接收”代码中有什么我做错了吗?“选择”错了吗?