10

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 です (当然のことながら、ボーレートの設定も問題ありません)。

私の「受信」コードに間違っているものはありますか?「選択」は間違っていますか?

4

3 に答える 3

5
  1. Windows側でも適切なボーレートを設定していますか?
  2. オシロスコープを使用して、ワイヤ上に存在する実際のデータを確認します。シリアル通信のデバッグは、オシロスコープが発明された目的です。ほとんど。:)
于 2012-09-21T13:14:30.247 に答える
0

読み取り機能は簡単に爆発する可能性があります。バッファの終わり近くにいて、それを埋める量以上を読み取った場合、バッファの終わりを超えてコピーし、スタックを上書きします。

Linux の送信側では、cfmakeraw() などの「raw モード」を調べる必要があります。このようにして、システムがあなたを「助ける」ことに煩わされることはありません(改行を送信するときにCRを追加するなど、実際にはバイナリデータを台無しにします...)。マイクロソフトにはそれを行う方法がありますが、その方法を忘れてしまいました。

于 2012-10-25T17:32:42.007 に答える
0

Windows 側では、DCB 構造体に DCBLength フィールドを設定しましたか?

dcb.DCBlength = sizeof(dcb);
于 2013-05-15T12:51:39.450 に答える