La programmation en série RS485

J'ai été chargé de la mise en œuvre du protocole ModBus sur une RS485 2 fils système. (En fait, c'est trois fils, A/B et GND).
ModBus n'est pas la question, mais l'étape avant que...simple je/S sur l'interface.

Je suis en utilisant le FTDI USB-RS485 convertisseur pour connecter un hôte Linux (non interchangeable) à un ordinateur hôte Windows (interchangeable avec un autre hôte Linux, mais j'aimerais éviter)

L'encodage est censé être 19200, 8, n, 1.
Mais il ne semble tout simplement pas de travail.

Je n'ai pas le code exact à portée de main, mais sur Linux, je suis en train de faire ceci:

 int fd = open("/dev/ttyS3", O_RDWR | O_CTTY);
 if(fd == -1) return "Error while opening the port";

Ensuite, j'ai configurer le 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);

De la parité et de Contrôle de Flux sont actuellement pas prévu, puisque le résultat final sera vous connecter à un faible niveau, où j'ai besoin de prendre soin des signaux de moi-même. En outre, il n'y a pas de fils, ce qui permettrait de "sans entrave de la communication". (Après je ne veux pas d'un XON/XOFF personnage à la limite de la plage d'octets que je peux transmettre)

Toutes ces fonctions vont bien et les données est définie.

Sur Windows, j'ai ouvert le port série comme ceci:

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);

La parité est désactivé, ainsi que le contrôle de flux. Taille en octets est fixé à 8.

Edit:
Depuis, il a été demandé, voici mon code pour le débit en bauds sur Windows (de mémoire)
SP.DCBlength= sizeof(SP);
SP.BaudRate = 19200;
SP.La parité = NOPARITY;
SP.Bits d'arrêt = ONESTOPBIT;
SetCommState(hSerial, &SP);

Encore une fois, toutes ces fonctions s'exécutent parfaitement.

Maintenant, pour le cas de test qui me donne un mal de tête important.

Sur l'hôte Linux, j'ai créer un octet de la mémoire tampon de 256 octets taille.
Ce tampon est rempli avec le caractère des valeurs de 0 à 255...et puis envoyées sur le fil de l'écriture.
Dans le même temps, l'autre côté est en attente avec "ReadFile" pour les données arrivent.

Avec cette configuration, pour 'les autres hôte Linux", ainsi que pour l'hôte Windows, 256 Octets arriver...mais c'est PAS le nombres de 0 à 255, mais quelque chose de 00 06 etc.

Je peux obtenir le Linuxhost au travail, quand je suis le réglage de tous les membres de la structure termios à 0 avant de paramétrer les options que je veux réellement. Je devine, c'est parce que les caractères de contrôle,...mais si je fais ça, l'hôte Windows soit ne reçoit que 4 de 256 octets.

Comme je l'ai dit, malheureusement je n'ai pas le code à portée de main. Si quelqu'un a une idée de quel point j'ai pu le résoudre, je lui en serais très reconnaissant. Je vais poster plus de code, une fois que j'ai à nouveau accès.

Comment je me suis mise en œuvre de l'opération de lecture:

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

À noter, je n'ai mis les délais d'attente, mais ne me souviens pas les valeurs exactes.

Edit: Depuis que j'ai maintenant accès à mon lieu de travail, encore une fois, c'est l' (actuel) du code.

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;

}

Que pour l'envoi/la réception de code:

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;

}

Comme de l'Oscilloscope: j'ai testé les deux directions à l'envoi. Les deux ont fait leur travail tout à fait admirable.

Le signal de 0x55 est une constante de Haut/vers le Bas à la longueur de 50 microsecondes (comme il se doit, le réglage de la vitesse de transmission est plus un problème).

Donc, il y a quelque chose dans mon "recevoir" code que j'ai fais de mal? Est le 'select' mal?

Comme vous détendre suggère, vérifier le débit en bauds de configuration.
Merci de poster le code de Windows configurer le débit en bauds et de l'appel de SetCommState.
Là vous allez.
'4' est toujours suspect numéro sur les systèmes 32 bits, (sizeof(char*)).
La chose est, ReadFile() a une fâcheuse tendance à revenir plus tôt, n'ayant pas lu autant d'octets que vous souhaitez. C'est pourquoi il a " le lpNumberOfBytesRead paramètre, de sorte que vous pouvez savoir combien de votre tampon il a lu ce temps et, si nécessaire, émettre une autre ReadFile ().

OriginalL'auteur ATaylor | 2012-09-21