RS485 2 線式システムを介した ModBus プロトコルの実装を任されています。(実際には、A/B と GND の 3 本のワイヤです)。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.Parity = NOPARITY; 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 です (当然のことながら、ボーレートの設定も問題ありません)。
私の「受信」コードに間違っているものはありますか?「選択」は間違っていますか?