2010-03-16 8 views
0

Je suis confronté à un problème particulier concernant la communication série sous win32. Je communique avec un appareil qui ne peut accepter que des images lorsqu'il ne communique pas déjà. Je dois donc trouver un cadre valide et ensuite immédiatement envoyer ma demande.Ajuster les paramètres de la lecture du port série

J'ai développé une classe nommée Serial qui gère les opérations de base sur le port série (ouvrir, fermer, lire, écrire) puis un appel Thread dans une boucle de lecture et d'écriture.

boucle de fil

//Device is an object of class Serial 
while(device->isOpen() && !terminate) 
{ 
    unsigned int readed = 0; 
    unsigned long error = ERROR_SUCCESS; 
    unsigned char* data = device->read(&readed, &error); 

    if(error==ERROR_SUCCESS) 
    { 
     //If data received, deliver to upper level 
     if(readed>0) 
     { 
      QByteArray output((const char*)data, (signed int)readed); 
      emit dataArrived(output, readed); 
     } 
    } 
    else 
    { 
     //unrelated stuff 
    } 

    //Here I manage the writting issue 
    //Only when nothing is received, and Upper layer wants to send a frame 
    //(Upper layer only will mark as something to send when it detects a valid frame) 
    if(readed==0) 
    { 
     out_lock.lock(); 
     //If something to send... 
     if(something_to_send > 0) 
     { 
      if(device->write(output_buffer, output_size, &error)) 
      { //things... 
      } 
     } 
    } 
} 

Le fil conserve essentiellement la lecture, et quand rien reçu, voit si quelqu'un a signalé à envoyer une trame (ce qui signifie qu'une trame valide est reçu juste). Lorsque cela se produit, il écrit le cadre via le port série.

Voici mon problème. Dans la fonction série :: read():
J'utilise le chemin chevauchée de la lecture:

::ClearCommError(handle, &dwErrors, &stat); 
if(stat.cbInQue) 
{ 
    //If there's something to read, read it, please note the bytes to read parameter, here 1. 
    bool ok = ::ReadFile(handle, buffer_in, 1, &bytes_read, &ov_reader); 
    if(!ok) 
    { 
     DWORD _error = ::GetLastError(); 
     if(_error == ERROR_IO_PENDING) 
     { 
      DWORD result = ::WaitForMultipleObjects(2, waiters, FALSE,INFINITE); 
      switch(result) 
      {  //Eventshutdown 
        case WAIT_OBJECT_0: /*code omitted*/break; 
        case WAIT_OBJECT_0+1: ok = ::GetOverlappedResult(handle, &ov_reader, &bytes_read, true); 
              //check ok value omitted 
              break; 
      } 
     } 
    } 
} 

if(bytes_read>0) 
{ 
    *size = bytes_read; 
} 

Ici commence mon problème. Lorsque le périphérique m'envoie de petites trames (environ 30 octets), tout fonctionne correctement, mais lorsque des trames plus grandes sont envoyées, le code ne peut pas trouver de temps libre entre les trames. .

Si j'augmente le nombre d'octets à lire dans la fonction de lecture(), perdre la capacité de détecter lorsque le dispositif "écoute": ok = bool :: ReadFile (poignée, buffer_in, 50, & bytes_read, & ov_reader); Cela se produit parce que mon application peut recevoir la fin d'une image avec le début de la suivante. Ce comportement est très commun. En revanche, si je modifie l'argument INFINITE par un délai d'expiration valide dans la fonction WaitForMultipleObjects, je perds des données.

Donc, ma question est essentiellement ... ce que je fais mal? Pourquoi en lisant 1 octet chaque fois que je ne trouve pas de temps libre pour envoyer mes propres images?

Merci

+1

Vous avez un problème beaucoup plus important, il y a une course inévitable ici. Vous pouvez décider que l'appareil n'envoie pas une microseconde avant l'envoi des étoiles. Vous ne pouvez pas faire ce travail. –

+0

@nobugz soulève un point VRAIMENT VRAIMENT important! Est-ce que vous supposez une longue pause entre les paquets d'état ou quelque chose? – mtrw

+0

Bien que ce type de communication de l'appareil n'est pas universelle, il n'est pas non plus inconnu. Ce que le protocole appelle généralement, c'est que si vous commencez à envoyer et que le périphérique démarre également, vous perdez et devez arrêter, gérer tout ce que le périphérique envoie puis réessayer. C'est une sorte de détection de collision d'un pauvre sur une liaison RS232. C'est une douleur, et c'est mieux géré au niveau du pilote UART, mais ce n'est probablement pas une option réaliste sur Windows. –

Répondre

2

Je ne sais pas si cela vous aidera ou non, mais puisque vous avez déjà une bonne idée du nombre d'octets dans la file d'attente d'entrée du périphérique série (stat.cbInQue), il serait peut-être utile de lire que le nombre d'octets au lieu de seulement 1 octet ou un nombre quelconque d'octets (comme 50):

bool ok = ::ReadFile(handle, buffer_in, stat.cbInQue, &bytes_read, &ov_reader); 

Bien sûr, vous aurez besoin de vous assurer que le buffer_in avait la capacité de ce nombre d'octets, donc il pourrait être une autre logique que vous auriez à ajouter pour vous assurer qu'il n'y a pas de dépassement de mémoire tampon.

En outre, parce que le pilote de série et ReadFile() API dépendent fortement de mise en mémoire tampon pour le traitement des caractères reçus, vous pourriez être en mesure d'obtenir des indications plus précises du moment où les caractères ont été reçus (et non reçu) en utilisant les WaitCommEvent() et SetCommMask() API.

+0

Bonne idée, je vais essayer ceci (en utilisant le stat.cbInQue) –

+0

Ok, j'ai essayé et il semble que cela fonctionne bien.Ainsi, je vais vérifier comme une réponse accepter, même les autres ont très informations précieuses. –

1

Quelle sont les "plus grands cadres?" Lorsque vous appelez ReadFile un octet à la fois, il va évidemment falloir beaucoup de temps pour travailler sur l'ensemble de la trame, probablement plus longtemps qu'il n'en faut pour envoyer l'image elle-même en raison de l'appel.

Quelques alternatives:

  1. Est-ce que l'appareil envoyer des trames chaque fois qu'il se sent comme il? Si vous avez la possibilité de concevoir les deux extrémités du protocole, pouvez-vous passer à un style de communication commande/réponse? Pouvez-vous, dès le début du paquet, prédire le nombre de caractères dans le reste du paquet? Si c'est le cas, vous pouvez créer une machine d'état dans votre fonction read. Vous pouvez interroger un octet à la fois, puis, lorsque vous détectez le début du paquet, lisez la majeure partie du reste du paquet lors d'un appel, puis revenez à un octet à la fois. Pouvez-vous utiliser DSR/CTS pour contrôler la synchronisation?

En général, il est très difficile de lire des paquets entiers à partir d'une fonction de lecture de port série. La procédure habituelle est la lecture d'un groupe de caractères et de les passer à un niveau supérieur pour l'analyse du protocole. Il semble que vous ayez un contrôle de timing plus strict que ce que permet cette méthode. Bonne chance ...

+0

avec des cadres plus grands je voulais dire environ 90 octets ... ce n'est pas très grand je sais ... 1. Le dispositif envoie son état à intervalles réguliers 2. Deuxième octet contient la longueur de la trame 3. Je ne sais pas savoir :(Je vais enquêter. –