Pages : 1
#1 Le 14/07/2008, à 07:34
- Tangram
Transmission série instable
Je suis en train de porter sur Linux un programme C pour effectuer une transmission série asynchrone à 38400 bauds entre un PC et un vieil ordinateur 8 bits via un adaptateur USB-Série.
Le problème est que la transmission ne tolère l'injection que de 16 octets de données maximum, et il faut attendre par la suite que les 16 octets soient ingérés, sinon les données transmises ne sont plus cohérentes. Je ne suis pour l'instant arrivé à envoyer une série satisfaisante de données qu'à grand coup de usleep entre chaque paquet de 16 octets.
Je m'en contenterais bien, mais je ne suis pas certain qu'il s'agisse de la meilleure solution.
N'y a-t-il pas moyen de contrôler le flux en meilleure intelligence ?
#2 Le 14/07/2008, à 12:01
- philou8237
Re : Transmission série instable
Ça dépend de ce que gère ta vielle machine. Si il sait gérer des protocole réseau sur interface série (genre SLIP) alors tu peux. Il faudrait voir ce que sait faire cette machine.
Hors ligne
#3 Le 14/07/2008, à 13:01
- Tangram
Re : Transmission série instable
Ça dépend de ce que gère ta vielle machine. Si il sait gérer des protocole réseau sur interface série (genre SLIP) alors tu peux. Il faudrait voir ce que sait faire cette machine.
J'utilise le mode RAW en asynchrone pour les transmissions (pas de protocole pour l'instant )
Mon ordinateur 8 bits se contente d'envoyer/recevoir des séries de caractères 8 bits - avec chacun un bit de start et un bit de stop - par un port série interfacé.
PS : la version Windows est déjà opérationnelle.
Dernière modification par Tangram (Le 14/07/2008, à 13:04)
#4 Le 14/07/2008, à 15:46
- philou8237
Re : Transmission série instable
Si il n'y a aucun protocole de communication au dessus de la couche liaison (bit de départ et de stop), tu ne peux pas faire de controle de flux. Essaies peut etre de voir si ton vieux poste peux réémettre à chaque fois qu'il a fini de traiter les données reçues, pour faire une sorte de "ACK".
Hors ligne
#5 Le 14/07/2008, à 19:28
- Tangram
Re : Transmission série instable
Pour l'instant, je ne m'occupe que du transfert Linux -> ordinateur 8 bits.
Travaillons un peu plus concrètement. Voici le programme :
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sysexits.h>
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
static struct termios gOriginalTTYAttrs;
static int fileDescriptor = FALSE;
/*
* Ferme le port série
*/
void SerialClose(void)
{
if (fileDescriptor)
{
tcdrain(fileDescriptor) ;
tcsetattr(fileDescriptor, TCSANOW, &gOriginalTTYAttrs) ;
close(fileDescriptor);
}
}
/*
* Ouvre le port série
*/
int SerialOpen(char *portname)
{
struct termios options;
fileDescriptor = FALSE ;
if ((fileDescriptor = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
return FALSE;
if (ioctl(fileDescriptor, TIOCEXCL) < 0)
return FALSE;
if (fcntl(fileDescriptor, F_SETFL, 0) < 0)
return FALSE;
if (tcgetattr(fileDescriptor, &gOriginalTTYAttrs) < 0)
return FALSE;
options = gOriginalTTYAttrs;
cfmakeraw(&options);
cfsetospeed(&options,B38400) ;
cfsetispeed(&options,B38400) ;
options.c_cflag &= ~(CSIZE | PARENB | CSTOPB) ;
options.c_cflag |= (CS8 | CRTSCTS | CREAD) ;
options.c_iflag &= ~(IXON | IXOFF | IXANY) ;
options.c_iflag |= (IGNBRK | IGNPAR) ;
options.c_oflag &= ~(OPOST) ;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
options.c_cc[VTIME] = 50 ; /* 5 secondes pour le timeout */
options.c_cc[VMIN] = 1 ; /* Lecture d'au moins 1 caractère */
if (tcsetattr(fileDescriptor, TCSANOW, &options) < 0)
return FALSE;
return TRUE ;
}
/*
* Envoie un bloc de données sur le port série
*/
int SerialWrite(char *buf, int size)
{
int ret ;
ret = write(fileDescriptor, buf, size) - size ;
usleep(16*290) ;
return ret ;
}
/*
* Reçoit un bloc de données sur le port série
*/
int SerialRead(char *buf, int size)
{
return (read(fileDescriptor, buf, size) - size);
}
int main(void)
{
int i ;
char deviceFilePath[] = "/dev/ttyUSB0\0" ;
char buffer[256]; /* Buffer d'input */
for(i=0;i<256;i++)
buffer[i] = i ;
if(SerialOpen(deviceFilePath) == TRUE)
{
for(i=0;i<16;i++)
{
if (SerialWrite(&buffer[i*16], 16))
printf("Probleme d'emission\n") ;
}
}
SerialClose();
return EX_OK;
}
Ce code fonctionne correctement : les 256 octets que j'envoie à mon ordinateur 8 bits sont bien reçus comme je l'entends par le programme côté 8 bits.
Seul problème : je suis obligé de mettre un usleep juste après write (fonction SerialWrite) pour attendre de mettre une autre série de 16 octets de données. Si j'injecte les 256 octets en une seule fois :
if(SerialOpen(deviceFilePath) == TRUE)
{
if (SerialWrite(&buffer[0], 256))
printf("Probleme d'emission\n") ;
}
seuls les 16 premiers octets de mon buffer sont envoyés.
Que se passe-t-il donc ? Et comment m'affranchir de ce usleep ?
#6 Le 14/07/2008, à 19:48
- robrob
Re : Transmission série instable
Suggestion pour SerialWrite:
int SerialWrite(char *buf, int size)
{
int total=0;
while((total-size)>0)
{
int sended=write(fileDescriptor, buf+total, size-total);
if (sended<=0)
return 0;
total+=sended;
}
return 1;
}
L'idée est tant que l'envoi fonctionne (write ne renvoie pas 0, il me semble) et qu'il reste des caractères, tu peux continuer à envoyer.
On peut supposer que cette limite de 16 octets vient soit du pilote de ton adaptateur USB, soit de l'adaptateur USB lui même.
Dernière modification par robrob (Le 14/07/2008, à 19:49)
Hors ligne
#7 Le 14/07/2008, à 20:33
- philou8237
Re : Transmission série instable
Oui effectivement, essaies de regarder précisément la valeur de retour de write. Normalement c'est -1 si il y a une erreur, ou sinon le nombre d'octets écrits. Ce serait intéressant de faire un
do
{
ret = write());
printf("%d written\n", ret);
}
write(ret > 0)
Pour voir si ça boucle, combien ça écrit à chaque fois et ainsi de suite.
Hors ligne
#8 Le 14/07/2008, à 20:57
- Tangram
Re : Transmission série instable
L'idée est tant que l'envoi fonctionne (write ne renvoie pas 0, il me semble)
En fait si, elle est supposée renvoyer 0 : je retire au résultat de write la taille (size) que je demande a écrire. Si la valeur retournée est 0, tout s'est bien passé. Si elle est positive, c'est que write n'a pas tout envoyé. Et si elle est négative, c'est que write a généré une erreur.
On peut supposer que cette limite de 16 octets vient soit du pilote de ton adaptateur USB, soit de l'adaptateur USB lui même.
C'est ce que j'ai pensé aussi. Cela pourrait avoir un rapport avec la taille du buffer hardware de l'adaptateur, effectivement, qui serait limité à 16 octets. Et comme ce buffer reçoit les données plus vite qu'il ne les envoie, puisque le programme C n'est pas bridé dans la vitesse - du moins sans le usleep -, les données au-delà du 16ème octet sont perdues.
Cela voudrait dire qu'il manque une couche logicielle dans la transmission série ? Faut-il alors l'implémenter soi-même ? J'avoue que ça m'ennuie un peu, vu que j'ignore comment faire, et je n'ai pas trouvé grand chose sur le Net pouvant m'éclairer un peu plus sur la question. Surtout s'il s'agit d'un traitement spécifique à l'adaptateur.
Et je peux vous assurer, une fois de plus, que mon programme fonctionne correctement tel quel Mais si je le laisse en l'état et dans le cas où le programme aurait à faire à un adaptateur dont le buffer est moins large que 16 octets, des données seront à nouveau passées à la trappe...
Dernière modification par Tangram (Le 14/07/2008, à 22:46)
#9 Le 15/07/2008, à 09:50
- philou8237
Re : Transmission série instable
Ah si le buffer de ton adaptateur USB est de 16 octets, c'est que le problème vient de là alors. Essaies de voir comment ton adaptateur communique avec le système. Il doit très probablement renvoyer des informations de contrôle de flux.
Hors ligne
#10 Le 15/07/2008, à 11:22
- Tangram
Re : Transmission série instable
Oui, c'est vrai qu'un petit signal à récupérer quelque part, ce serait l'idéal Et avec de quoi évaluer la taille du buffer hardware, ce serait parfait.
Je ne vais pas vous ennuyer plus longtemps, je vais repartir à la chasse aux informations. Et je vous communiquerai les résultats, si ça vous intéresse.
Dernière modification par Tangram (Le 15/07/2008, à 11:24)
#11 Le 15/07/2008, à 18:16
- robrob
Re : Transmission série instable
Si la valeur retournée est 0, tout s'est bien passé. Si elle est positive, c'est que write n'a pas tout envoyé. Et si elle est négative, c'est que write a généré une erreur.
Je n'arrive pas à retrouver l'info mais il me semble (et j'en suis même quasi-sûr) que la fonction write te retourne le nombre de caractères réellement envoyés. Et tout cas j'utilise cette propriété dans une classe...série
edit: pour préciser, mon code posté plus haut envoie petit à petit l'intégralité (en utilisant le nb de caractères envoyés retourné par write) des données à transmettre.
Dernière modification par robrob (Le 15/07/2008, à 18:21)
Hors ligne
#12 Le 15/07/2008, à 22:35
- Tangram
Re : Transmission série instable
Je n'arrive pas à retrouver l'info mais il me semble (et j'en suis même quasi-sûr) que la fonction write te retourne le nombre de caractères réellement envoyés. Et tout cas j'utilise cette propriété dans une classe...série
Ok. La valeur retournée par "SerialWrite" (et non write) ne peut être que négative ou nulle.
Le principal est qu'elle renvoie 0 si tout s'est bien passé.
La confusion de mon explication, malheureusement, n'entrave pas le bon fonctionnement du code et mon problème n'est toujours pas résolu
Dernière modification par Tangram (Le 15/07/2008, à 22:36)
#13 Le 16/07/2008, à 06:40
- robrob
Re : Transmission série instable
Désolé d'insister lourdement, mais as tu essayé ma suggestion de code?
Hors ligne
#14 Le 16/07/2008, à 07:44
- Tangram
Re : Transmission série instable
Désolé d'insister lourdement, mais as tu essayé ma suggestion de code?
Les comportements sont les mêmes qu'avec mon programme initial : seules les premières données (quelques fois plus que 16, le nombre des opérations dans la boucle et le temps d'exécution augmentant), sont correctement envoyées.
Je suppose qu'il faut en déduire que la valeur retournée par write n'est pas influencée par la taille du buffer de l'adaptateur ou celle de la place libre dans ce même buffer. Ce dont je me doutais déjà....
J'ai choisi pour l'instant d'envoyer les données à l'unité, ce qui m'affranchit de la capacité de l'adaptateur, à supposer qu'elle ne sera jamais inférieure à 1 octet, quelque soit le modèle utilisé :
int SerialWrite(char *buf, int size)
{
int i ;
for(i=0;i<size;i++)
{
if (write(fileDescriptor, buf+i, 1) != 1)
return -1 ;
usleep(26*10+1) ;
}
return 0 ;
}
J'attends dans ce cas le temps nécessaire à l'émission d'une donnée de 10 bits à l'aide du usleep, le temps d'exécution des lignes de code m'ajoutant quelques microsecondes de plus - mais cela n'est pas excessivement gênant à ce débit -.
Dernière modification par Tangram (Le 16/07/2008, à 07:47)
Pages : 1