Mélange port série, C++ et boost::asio

Mélange port série, C++ et boost::asio - C++ - Programmation

Marsh Posté le 01-05-2009 à 18:41:58    

Bonjour :hello:  
 
J'ai un problème un peu compliqué vu que ca mélange l'accès au port série sous Windows, du C, et tout ca avec Matlab. Je ne sais d'ailleurs pas trop dans quelle catégorie poster :/
Je vais donc essayer d'etre le plus clair possible.
 
Contexte :
Le but c'est de piloter un robot à partir d'un ordinateur embarqué (EeePC).  
L'ordinateur communique avec un microcontroleur qui gère les moteurs (pour lire le déplacement du robot et envoyer des consignes) via un port série (COM3).
On utilise Matlab pour faire tout ca parceque c'est pratique pour développer/debugger.
 
Le problème :
Les fonctions de lecture/ecriture sur port série de Matlab sont trop longues. On a peu de données à transmettre mais les appels aux fonctions de lecture/écriture sont fréquent. L'idéal serait de pouvoir lire et écrire toutes les 5ms, actuellement la durée d'execution des fonctions empeche de descendre sous les 10ms par cycle lecture/écriture.
 
J'ai d'abord réécrit les fonctions de lecture/écriture en C avec les fonctions WriteFile et ReadFile d'après une source trouvée et à l'aide de la MSDN (Outre les fonctions "mex" propres à Matlab c'est du C normal) :

Code :
  1. #include <mex.h>
  2. #include <Windows.h>
  3. // serial_write.c
  4. // Simple Matlab command to write to a serial port
  5. // This is needed because Matlab's serial commands frequently crash
  6. //
  7. // Originally written 2009 April 15 by Kevin Barry
  8. // This code is entered into the Public Domain and may be freely used for
  9. // any purpose.
  10. // This code is provided "AS IS", without express or implied warranty
  11. // of any kind.
  12. HANDLE hCom = NULL;
  13. void closeSerial()
  14. {
  15.     CloseHandle(hCom);
  16.     hCom = NULL;
  17. }
  18. void openSerial(char *portname, int baud)
  19. {
  20.     char portname_w32[255];
  21. /*COMMCONFIG lpCC;
  22.     COMMTIMEOUTS lpTo;
  23.      
  24. */
  25.     DCB parametres;
  26.     COMMTIMEOUTS timings;
  27.    
  28.     // Win32 can't open >= COM10 with "COM10"
  29.     // Needs to be \\.\COM10
  30.     sprintf(portname_w32, "\\\\.\\%s", portname);
  31.    
  32.     if (hCom != NULL) {
  33.         mexWarnMsgTxt("Already have an open port, closing first" );
  34.         closeSerial();
  35.     }
  36.     hCom = CreateFile(portname_w32, GENERIC_READ | GENERIC_WRITE,
  37.                         0, NULL, OPEN_EXISTING, 0, NULL);
  38.     mexAtExit(closeSerial);
  39.     if (hCom == INVALID_HANDLE_VALUE) {
  40.         hCom = NULL;
  41.         mexErrMsgTxt("Could not open serial port" );
  42.     }
  43.    
  44.     parametres.DCBlength = sizeof(DCB);
  45.     GetCommState(hCom, &parametres);
  46.    
  47.     parametres.fBinary = TRUE;
  48.     parametres.fParity = TRUE;
  49.     parametres.fOutxCtsFlow = FALSE;
  50.     parametres.fOutxDsrFlow = FALSE;
  51.     parametres.fDtrControl = DTR_CONTROL_DISABLE;
  52.     parametres.fRtsControl = RTS_CONTROL_DISABLE;
  53.     parametres.fOutX = FALSE;
  54.     parametres.fInX = FALSE;
  55.     parametres.fErrorChar = FALSE;
  56.     parametres.fAbortOnError = FALSE;
  57.    
  58.     parametres.BaudRate = baud;
  59.     parametres.ByteSize = 8;
  60.     parametres.Parity = 3; //0-4=no, odd, even, mark, space
  61.     parametres.StopBits = 2; //0,1, 2=1,1.5, 2
  62.            
  63.     SetCommState(hCom, &parametres);
  64.    
  65.     GetCommTimeouts(hCom, &timings);
  66.    
  67.     timings.ReadIntervalTimeout = 2;
  68.     timings.ReadTotalTimeoutMultiplier = 1;
  69.     timings.ReadTotalTimeoutConstant = 0;
  70.     timings.WriteTotalTimeoutMultiplier = 0;
  71.     timings.WriteTotalTimeoutConstant = 2;
  72.    
  73.     SetCommTimeouts(hCom, &timings);
  74.    
  75.     SetupComm(hCom, 1024, 1024);
  76. }
  77. void mexFunction (int nlhs, mxArray *plhs[],
  78.                               int nrhs, const mxArray *prhs[])
  79. {
  80.     char action;    //0 Ouvrir, 1 Lire, 2 Ecrire, 3 Fermer
  81.     char data[1024];
  82.     int num;
  83.     int i;
  84.     unsigned char *sortie;
  85.                    
  86.     action = (char)mxGetScalar(prhs[0]);
  87.    
  88.     switch(action)
  89.     {
  90.         case 0 : //Ouvrir             
  91.            /* eg. openSerial("COM3", 262144) */
  92.             openSerial(mxArrayToString(prhs[1]), (int)mxGetScalar(prhs[2]));
  93.             break;           
  94.         case 1 : //Lire
  95.             num = (char)mxGetScalar(prhs[1]);
  96.             num = readSerial(data, num);
  97.            
  98.             plhs[0] = mxCreateNumericMatrix(1, num, mxUINT8_CLASS, mxREAL);
  99.             sortie = mxGetData(plhs[0]);
  100.             for (i=0; i<num; i++)
  101.                 sortie[i] = data[i];
  102.             plhs[1] = mxCreateDoubleScalar(num);
  103.             break;           
  104.         case 2 : //Ecrire
  105.             num = (int)mxGetScalar(prhs[2]);
  106.             mxGetString(prhs[1], data, num+1); //Récupère letableau de char à envoyer
  107.             plhs[0] = mxCreateDoubleScalar(writeSerial(data, num)); //Renvoi le nb d'octets écrits à Matlab
  108.             break;
  109.         case 3 : //Fermer
  110.             closeSerial();
  111.             break;
  112.         default :
  113.             mexWarnMsgTxt("Mauvais code,\n0:Ouvrir\n1:lire\n2:Lire\n3:Fermer" );
  114.             break;
  115.     }
  116.     return;
  117. }
  118. int writeSerial(char *str, int n)
  119. {
  120.     DWORD bytesWritten;
  121.     if (hCom == NULL)
  122.         mexErrMsgTxt("Cannot write. Open serial port first" );
  123.     if ( WriteFile(hCom, str, n, &bytesWritten, NULL) ) {
  124.         return bytesWritten;
  125.     } else {
  126.         char err_str[128];
  127.         sprintf(err_str, "Could not write %d", GetLastError());
  128.         mexErrMsgTxt(err_str);
  129.     }
  130.     return 0;
  131. }
  132. int readSerial(char *buffer, int num)
  133. {
  134.     DWORD nLus;
  135.    
  136.     if (hCom == NULL)
  137.         mexErrMsgTxt("Cannot write. Open serial port first" );
  138.     ReadFile(hCom, buffer, num, &nLus, NULL);
  139.     //return nLus;
  140. }


 
J'ai énormément gagné en vitesse d'écriture (~1ms pour executer la fonction), par contre en lecture ca a explosé, dans les 14ms en moyenne pour lire.
 
Je me suis dit que je pourrais passer sur un Windows CE (qui est "temps réel" ) pour gagner en rapidité mais je ne pense pas que je vais gagner grand chose.
 
Question :
Qu'est-ce que je peux faire pour réduire le temps de lecture ?
Aussi, la lecture a un comportement étrange, une fois que tout le buffer a été lu, ca me renvoit quand même des valeurs plus ou moins aléatoires :??:  
 
Et question pour les connaisseurs :
Le fichier C est compilé par Matlab qui en fait un fichier binaire serialrw.mex. Pour l'utiliser j'écrit quelque chose comme ca dans la commande de Matlab :

Code :
  1. serialrw(0, 'COM3', 262144)
  2. serialrw(2, char([65 66 67]), 3)
  3. serialrw(1, 3)

Mais comment il se passe le handle du port COM entre les appels  :??:


Message édité par Dagnir le 08-05-2009 à 13:13:52

---------------
Nous vous souhaitons de beaux rêves, c'est le cinéma gratuit.
Reply

Marsh Posté le 01-05-2009 à 18:41:58   

Reply

Marsh Posté le 01-05-2009 à 18:54:08    

boost::asio fourni une base propre pr la gestion de ce genre d epériphérique. J'ai posté un bout de code C++ pr l'attaque du port série.

Reply

Marsh Posté le 08-05-2009 à 00:26:01    

D'abord un grand merci  :jap:  
J'aurai mis le temps mais j'ai réussis à utiliser boost::asio pour communiquer avec mon microcontrolleur par le port série, en utilisant le code que tu a posté fin 2008.
 
J'ai quand même quelques questions :whistle: (qu'il vaut peut etre mieux que je pose sur ton topic ?)
 
Le microcontroleur à qui j'envoi des valeurs ne voit pas passer les codes ascii des lettres que j'envoie mais des valeurs déacalées.
Exemple, si j'envoi  
Aa'\n'
le microC voit 193 225 138 au lieu de 65 97 10 13.
 
Par contre si le microC envoie 193 225 138 asio l'interprète bien comme "Aa\n" dans la console.  
D'où peut venir ce décalage ?
 
 
Pour le fonctionnement en général, j'ai du mal à saisir quand s'effectuent les lecture/écriture.
 
Le programme attend que j'aie rentrée une chaine (cin.get(ch)), la met dans ch et envoie ch au port série (c.write(ch)).
Mais ch est déclaré comme simple char donc comment il peut transmettre toute la chaine ?

Message cité 1 fois
Message édité par Dagnir le 08-05-2009 à 12:09:30

---------------
Nous vous souhaitons de beaux rêves, c'est le cinéma gratuit.
Reply

Marsh Posté le 08-05-2009 à 05:46:05    

cin >> ch mange un caractére et rend la main sans bufferisation. Tu boucle la dessus tant que la conenction ets up. la chaine est donc passé char par char.
 
j'ai peu d'xp avec les mC je pourrais pas t'aider plus :€

Reply

Marsh Posté le 08-05-2009 à 11:48:35    

C'est ce que j'aurais pensé, mais j'ai une lumière qui s'allume quand des données passent sur le port série, et elle ne s'allume pas quand j'appuie sur une touche quelconque mais seulement quand j'appuie sur entrée (ce qui a aussi pour effet d'envoyer un 138, au lieu d'un 10 ou 13).  
Je vais investiguer  [:columbo2]  
 
Bon sinon c'était plus pour comprendre le programme vu qu'en vrai je vais juste envoyer des séries d'entiers calculées par un autre programme, j'aurai pas besoin de l'i/o de la console.
Edit : à ce propos, le c.write(ch) n'aceppte donc q'un char à la fois. Il y aurait pas un truc pour envoyer un tableau de valeurs d'un coup ? J'ai pas encore benché le ce code mais il faut vraiment que je gratte des millisecondes sur les appels de fonction.
 
Ce qui m'interesserait plus ce serait de savoir si je peux ouvrir le port en partageant, pour faire la lecture dans un programme et l'écriture dans un autre. Je vais déjà aller voir la doc pour ca :)
 
Edit 2 :
J'ai testé ce code

Code :
  1. #include "stdafx.h"
  2. #include <iostream>
  3. using namespace std;
  4. int _tmain(int argc, _TCHAR* argv[])
  5. {
  6. char ch;
  7. cin.get(ch);
  8. printf("%d\n", ch);
  9. return 0;
  10. }

et ca donne quelquechose comme :

Code :
  1. ABCD
  2. 65
  3. 66
  4. 67
  5. 68
  6. 10

Meme résultat avec cin >> ch
Ca me parait surprenant mais bon. A noter que ca donne bien les valeurs ascii.
Edit 4 ( :D ) :
Y avait ca dans le code

Code :
  1. // on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress  
  2. // On other systems, you'll need to look for an equivalent  
  3. #ifdef POSIX
  4. termios stored_settings;
  5. tcgetattr(0, &stored_settings);
  6. termios new_settings = stored_settings;
  7. new_settings.c_lflag &= (~ICANON);
  8. new_settings.c_lflag &= (~ISIG); // don't automatically handle control-C  
  9. tcsetattr(0, TCSANOW, &new_settings);
  10. #endif

Que j'ai enlevé. Mais je veux quand meme bien qu'on m'explique comment un char peut stocker la chaine retournée.
 
Ce qui me gène plus c'est le décalage de qui donne +128 aux valeurs ascii dans le programme asio.
J'ai testé aussi, si le port COM recoit 193 (65+128), ca affiche "A"
S'il recoit 65, il affiche "A^" (où ^ est en fait un triangle fermé).
 
Edit 3 : Ou alors c'est un problème de bit de parité qui perturbe tout  [:columbo2]


Message édité par Dagnir le 08-05-2009 à 13:18:39

---------------
Nous vous souhaitons de beaux rêves, c'est le cinéma gratuit.
Reply

Marsh Posté le 08-05-2009 à 14:52:26    

je penche pr la parité en effet

Reply

Marsh Posté le 08-05-2009 à 16:55:09    

Heu, je soupconne que passer stopbits à 2 pourrait aider, j'ai trouver ca http://think-async.com/Asio/boost_ [...] _bits.html, mais je vois pas du tout quoi écrire dans le code, C plus plus caÿ dur [:ramones]
Juste pour etre sûr, c'est ce code là que j'ai repris  
http://forum.hardware.fr/forum2.ph [...] w=0&nojs=0


---------------
Nous vous souhaitons de beaux rêves, c'est le cinéma gratuit.
Reply

Marsh Posté le 09-05-2009 à 00:38:06    

Dagnir a écrit :


Aa'\n'
le microC voit 193 225 138 au lieu de 65 97 10 13.
 
Par contre si le microC envoie 193 225 138 asio l'interprète bien comme "Aa\n" dans la console.  
D'où peut venir ce décalage ?


 
char != unsigned char
 
De rien  [:fanou]

Reply

Marsh Posté le 09-05-2009 à 01:18:28    

J'y ai pensé ( :o ), mais la fonction write d'asio prend un char donc de ce coté c'est fixé.
De l'autre coté si je met les données dans un unsigned char 'A' me donne 193, si je met dans un char ca me donne -65. Donc je suis toujours bloqué, sauf si je retranche 128 systématiquement à tout ce que lit le microC, mais ca fait pas vraiment propre.
 
J'ai regardé avec le debugger, la valeur que prend ch avant d'etre envoyé par c.write(ch) c'est bien le code ASCII de ce que je tape dans la console donc c'est pas un problème de cout/cin.
 
Je crois que mon microC prend deux bits de stop exclusivement alors que la valeur par défaut en général c'est 1, ca pourrait expliquer un bit de poids fort toujours à 1. Mais j'aarrive pas à trouver de tutoriel/exemple qui montre comment le changé.
J'ai tenté de mettre

Code :
  1. boost::asio::serial_port_base::stop_bits::stop_bits  (one)

en début de main mais il me dit "one : undeclared identifier". J'avoue que j'ai un peu de peine avec le C++, j'ai encore du mal à comprendre comment certaines lignes peuvent fonctionner.


Message édité par Dagnir le 09-05-2009 à 01:19:08

---------------
Nous vous souhaitons de beaux rêves, c'est le cinéma gratuit.
Reply

Marsh Posté le 09-05-2009 à 01:47:07    

le stop_bit est une option a apssé à set_option ...

Reply

Marsh Posté le 09-05-2009 à 01:47:07   

Reply

Marsh Posté le 09-05-2009 à 01:50:23    

C'est quoi comme micro ? Quel compilateur/libc ?  
Tu utilises un "OS" ou tu joues directement avec les registres de l'uart ?
 
 

Reply

Marsh Posté le 09-05-2009 à 01:51:38    

on s'en tape un peu. Monsieur a juste à apprendre à appeler une méthode sur un objet, ASIo fera le reste :o

Reply

Marsh Posté le 09-05-2009 à 01:59:19    

Je ne pense pas qu'asio tourne sur les PIC 8 bits, que je soupçonne le monsieur d'utiliser :o

Reply

Marsh Posté le 09-05-2009 à 02:00:47    

asio est utilisé du coté du PC [:prozac] ...

Reply

Marsh Posté le 09-05-2009 à 02:07:16    

Le problème ne se situe plus que probablement pas côté pc [:spamafoote]

Reply

Marsh Posté le 11-05-2009 à 13:14:26    

Bon, après tatonnement j'ai trouvé une syntaxe qui a l'air de marcher.
J'ai rajouté

Code :
  1. boost::asio::serial_port_base::parity par(boost::asio::serial_port_base::parity::none);
  2. serialPort.set_option(par);
  3. boost::asio::serial_port_base::character_size taille(boost::asio::serial_port_base::character_size::character_size(8));
  4. serialPort.set_option(taille);
  5. boost::asio::serial_port_base::stop_bits  stopbits_option(boost::asio::serial_port_base::stop_bits::two);
  6. serialPort.set_option(stopbits_option);

dans la classe minicom_client  
 
Apparemment c'était plus le character size qui posait problème, il devait pas être à 8 par défaut.


---------------
Nous vous souhaitons de beaux rêves, c'est le cinéma gratuit.
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed