[Tutoriel] Communiquez entre caltos en C/C++ !
Posté le 19/03/2014 20:04
Qui n'a jamais rêvé de faire un jeu multijoueurs sur sa calculatrice ? Ou alors un chat pour discuter en cours ? Dans ce tutoriel, nous allons apprendre comment envoyer et recevoir des données via le port série 3 broches ("serial 3-pins" en anglais) pour pouvoir échanger des informations entre deux calculatrices. Bonne lecture !
Sommaire :
1) Comment tester les programmes que je vais créer ?
2) Que faut-il utiliser ?
3) Ouvrir et fermer le port série
4) Émettre et recevoir des variables simples
5) Émettre et recevoir des tableaux de char et des variables "complexes"
6) Annexes
1) Comment tester les programmes que je vais créer ? :
Pour pouvoir communiquer, il faut être deux. Or, en règle générale, il est difficile de trouver chez soi deux Graph 35++/75/85/95. Pour cela, il ne faut pas hésiter à demander à vos amis du collège/lycée/fac, bref, ceux qui sont susceptibles de posséder une Graph "classique". Attention, si celle-ci est une Graph 35+USB non débridée, demandez bien à son propriétaire si il est d'accord pour la modifier en Graph 75. Bon, en règle générale, la promesse de pouvoir jouer à Sims City, Pokémon ou Fruit Ninja dessus est un bon argument, mais soyez responsable du matériel emprunté.
Une fois en possession de vos deux caltos, installez sur les deux l'addin
Serial Monitor de Ziqumu. Il servira à tester la connexion. Ensuite, vous connectez le câble aux deux caltos, puis vous lancez Serial Monitor. Appuyez sur les lettres, si elles sont bien envoyées, tout va bien. Sinon, vérifiez que le câble est bien enfoncé, en bon état, etc.
Vos programmes auront alors toutes les chances de fonctionner, tant que l'erreur ne vient pas de vous.
NB : Je savais pas où mettre ça, mais dans la suite du tutoriel, dès que l'on parlera de taille de données, tout sera exprimé en
octets.
2) Que faut-il utiliser ? :
Comme d'habitude, voici les fichiers contenant les fonctions à inclure dans votre projet (en pièce-jointe). Le fichier .src est à ajouter aux fichiers du programme dans le SDK (comme un .c classique). Il contient des instructions en assembleur.
Voici tout de même le header dont nous commenterons les fonctions :
Serial.h
Cliquer pour enrouler
#ifndef _SERIAL
#define _SERIAL
/**********************************************************/
/** Les syscall suivants servent à l'acces au port série **/
/** Le buffer de réception fait 1ko, **/
/** et le buffer d'envoi fait 256 octets. **/
/**********************************************************/
//Lit un caractère du buffer de réception et le copie a l'adresse pointée par 'dest'
//Retourne 0 en cas de succes, 1 si le buffer est vide, 3 si la connexion n'est pas établie
int Serial_ReadByte(unsigned char *dest);
//Lit 'max' octets du buffer de réception et les écrit dans 'dest'
//'size' donne le nombre d'octets lu
//Retourne 0 en cas de succes, 1 si le buffer est vide, 3 si la connexion n'est pas établie
int Serial_ReadBytes(unsigned char *dest, int max, short *size);
//Ecrit 'byte' dans le buffer d'envoi
//Retourne 0
int Serial_WriteByte(unsigned char byte);
//Ecrit 'size' octets dans le buffer d'envoi depuis l'adresse 'scr'
//Retourne 0 en cas de succes, 2 si le buffer est trop plein, 3 si la connexion n'est pas établie
int Serial_WriteBytes(unsigned char *src, int size);
//FIFO = first in first out
int Serial_WriteByteFIFO(unsigned char byte);
//Retourne la taille des données du buffer de réception
int Serial_GetRxBufferSize(void);
//Retourne l'espace disponible dans le buffer d'envoi
int Serial_GetTxBufferFreeCapacity(void);
//Vide le buffer de réception
//Retourne 0 en cas de succes, 3 si la connexion n'est pas établie
int Serial_ClearReceiveBuffer(void);
//Vide le buffer d'envoi
//Retourne 0
int Serial_ClearTransmitBuffer(void);
//Ouvre et prépare l'interface de communication
//Pour plus de détails, consulter fxreverse-doc-1.pdf ci joint
int Serial_Open(unsigned char *conf);
//Ferme l'interface de communication et vide les buffers d'envoi et de réception
//Si 'mode'==1, la communication est coupée sans regarder s'il reste des données a transmettre
//Si 'mode'!=1, la fonction ne ferme pas l'interface de communication s'il reste des données a transmettre
//et retourne 5
//Retourne 0 en cas de succes (communication terminée) et 5 s'il reste des données a transmettre
int Serial_Close(int mode);
//Copie l'octet numéro 'index' du buffer de réception vers 'dest' si 'index' ne dépasse pas les données du buffer
//Retourne 0 en cas de succes, 1 si 'index' dépasse les données du buffer, 3 si la communication n'est pas établie
int Serial_Peek(int index, unsigned char *dest);
//Récupère le statut de la connexion
//Retourne 1 si la connexion est établie, 3 sinon
int Serial_IsOpen(void);
#endif
3) Ouvrir et fermer le port série :
Encore une fois, on a le code base, mais comment l'utiliser ? Pour envoyer des données via le port série, il faut ouvrir ces ports. Pour cela, on a besoin d'une liste de configuration :
unsigned char config[] = {a, b, c, d, e, f};
Et les valeurs à mettre dans ce tableau :
Source : FxReverse de Andreas Bertheussen et Simon Lothar
Donc pour ouvrir les ports, les options les plus courantes sont :
unsigned char config[] = {0, 5, 0, 0, 0, 0};
// configuration des ports à 9600 bauds, pas de bit de parité, 8 bits de longueur, et 1 bit d'arret
Pour plus d'infos sur les bits de parité/longueur/arrêt, renseignez-vous ailleurs, c'est d'un niveau supérieur et pas forcément intéressant pour un usage courant. Pour les bauds, lisez la note en annexe de ce tutoriel.
Bref, maintenant qu'on a configuré les ports, on va les ouvrir, une seule fois tant que l'on ne les a pas fermés. Donc en début de jeu dans notre cas, en même temps que
srand(), si vous utilisez de l'aléatoire.
Pour cela, exécutez cette fonction, et c'est tout :
erreur = Serial_Open(config);
erreur : 0 si tout s'est bien passé, 3 si c'est déjà ouvert, et 4 si config[0] est différent de 0.
Je vous habitue à utiliser des variables d'erreur pour que vous sachiez comment ça fonctionne, même si ce n'est pas nécessaire.
Pour le fermer, a la fin de votre programme, faites :
erreur = Serial_Close(0); // 0: arrête même si il reste des données à transmettre; 1: envoie les données puis ferme
erreur : 0 si tout s'est bien passé, 5 si il restait des données à envoyer.
4) Émettre et recevoir des variables simples :
On attaque les choses sérieuses : envoyer et recevoir des données. On va commencer avec un exemple simple, envoyer des
(unsigned) char. J'insiste sur le
char car les
int,
short, et
float sont considérés comme des tableaux de
char, et ça risque de ne pas fonctionner comme prévu si vous utilisez cette méthode.
Pour résumer brièvement, la calto possède un
buffer, c'est à dire une zone de mémoire, réservé à l'envoi et à la réception de données. Le buffer d'envoi fait 256 octets, celui de réception 1ko. Lorsque vous écrivez ou lisez dedans, la calto se charge de mettre à jour le buffer correspondant : envoyer ou effacer les données.
Donc, pour écrire une valeur dans ce buffer, il faut faire :
erreur = Serial_WriteByte(monChar); // écrit le [i]char[/i] dans le buffer d'envoi
// celui-ci est automatiquement envoyé dès que la calto le peux
erreur : 0 si tout s'est bien passé, et c'est tout.
Pour les recevoir, c'est presque pareil, sauf qu'il faut un pointeur :
erreur = Serial_ReadByte(&monChar);
erreur : 0 si tout s'est bien passé, 1 si le buffer de réception est vide, 3 si le port est fermé.
Ensuite, vous n'avez plus qu'à traiter les infos de votre coté.
5) Émettre et recevoir des tableaux de char et des variables "complexes" :
Nous allons maintenant passer à la vitesse supérieure : envoyer des tableaux de
char, ainsi que d'autres types de variables et des structures.
Pour envoyer un tableau, il faut copier l'intégralité du tableau dans le buffer d'envoi. On pourrait faire :
void Send_Tab(char *tab, int size)
{
int i;
for(i=0; i<size; i++) Serial_WriteByte(*tab[i ]);
}
Mais il existe une fonction bien plus utile et efficace, qui prend en argument un pointeur sur le tableau, et la taille des données à écrire.
char monTableau[] = {0, 1, 2, 3, 4};
int taille = 5;
erreur = Serial_WriteBytes(monTableau, taille);
erreur : 0 si tout s'est bien passé, 2 si le buffer de transmission est plein, 3 si le port est fermé.
Cette fonction est très utile en ce qu'elle permet de copier tout type de données, y compris des
short,
int,
floats, ou structures. Il suffit encore une fois d'envoyer les données avec un pointeur sur les données à copier :
short monShort = 42;
int monInt = 2048;
struct MA_STRUCTURE maStructure = {0x2A, 0xFF};
erreur1 = Serial_WriteBytes(&monShort, sizeof(short)); // envoie un short
erreur2 = Serial_WriteBytes(&monInt, sizeof(int)); // envoie un int
erreur3 = Serial_WriteBytes(&maStructure, sizeof(maStructure)); // envoie une structure de données
Pour lire ces données, vous pouvez récupérer une zone entière du buffer de réception avec
Serial_ReadBytes(), qui demande en argument un pointeur sur la zone où copier les données, la taille maxi de données à copier, et un pointeur sur
short, qui contiendra la taille des données lues - utile dans le cas où le buffer est vide avant d'avoir atteint la taille max à lire.
unsigned char monBuffer[10];
short tailleLue = 0;
erreur = Serial_ReadBytes(monBuffer, 10, &tailleLue);
erreur : 0 si tout s'est bien passé, 1 si le buffer de réception est vide, 3 si le port est fermé.
De même, on peut utiliser cette fonction pour lire d'autres types de variables :
short monShort = 42;
int monInt = 2048;
struct MA_STRUCTURE maStructure = {0x2A, 0xFF};
erreur1 = Serial_ReadBytes(&monShort, sizeof(short), &tailleLue); // récupère un short
erreur2 = Serial_ReadBytes(&monInt, sizeof(int), &tailleLue); // récupère un int
erreur3 = Serial_ReadBytes(&maStructure, sizeof(maStructure), &tailleLue); // récupère une structure de données
Attention
Il faut d'abord synchroniser la connexion entre les deux caltos avant d'envoyer - recevoir des données par paquets : en effet, si A envoie un
int à B, et que B le lit avant que tout soit transmit, il n'aura qu'une partie des données, qui seront de plus décalées bit à bit !
Faites attention à cet aspect, cela peut provoquer des bugs ! Pour s'en protéger, la fonction
int Serial_GetRxBufferSize(void) peut être utile.
Elle retourne le volume de données présentes dans le buffer de réception.
6) Annexes :
Les bauds :
Le baud est l'unité de mesure de vitesse de communication. Dans notre cas, il est équivalent au nombre d'octets/secondes, car la liaison série ne possède qu'une seule voie. Dans le cas où il existe plusieurs voies (carte SD, etc.) le nombre de bauds n'est pas égal à la vitesse en octets/secondes. Encore une fois, Google sait tout et vous éclairera sur vos questions subsidiaires.
Autres fonctions :
D'autres fonctions existent, la liste complète est dans la documentation
FxReverse de Andreas Bertheussen et Simon Lothar.
Retour au sommaire
Fichier joint
Citer : Posté le 19/03/2014 20:05 | #
Je n'ai pas fini, car j'ai pas eu le temps, mais le continuerai bientôt
Citer : Posté le 19/03/2014 20:13 | #
Pour pouvoir communiquer, il faut être deux. Or, en règle générale, il est difficile de trouver chez soi deux Graph 35++/75/85/95.
A noter que sur Prizm, on peut linker une vraie calculette à l'émulateur et les fonctions COM fonctionnent (en théorie) : https://www.cemetech.net/forum/viewtopic.php?t=8086
Peut-être une manip du même genre est possible avec le SDK des monochromes, j'en doute un peu mais sait-on jamais !
Mais sinon, bien de faire un tuto pour ces communications !
Citer : Posté le 19/03/2014 21:39 | #
Cool !
Zelda de Smashmaster
Super Geek Brothers de Siapran
Pac-Man
Pac-Man Color
Meta Ball
Add-ins Jetpack Joyride et Pac-Man sur PRIZM (les 2 non commencés mais en réflexion)
A la recherche des sprites jetpack Joride si quelqu'un les a en couleur
Citer : Posté le 07/04/2014 18:59 | # | Fichier joint
Je suis en train de continuer le tuto et j'aurai besoin de cette image
Citer : Posté le 07/04/2014 19:05 | #
On voit le curseur sur "setting value"
Vitesse des fonctions en Basic Casio | 7 days CPC | Casio Universal Wiki | Tutoriel Basic Casio
>>> Give me a click Brother <<< >>> Teste mon générateur de mots nouveaux <<<
>>> Random Youtube Video <<<
Citer : Posté le 07/04/2014 19:10 | #
Merci, c'est corrigé.
Citer : Posté le 07/04/2014 19:11 | #
*Impatient*
Citer : Posté le 07/04/2014 19:49 | #
J'ai ajouté une partie, je finirai encore plus tard
Ajouté le 08/04/2014 à 21:30 :
Mis à jour, j'ajouterai un mot sur les bauds plus tard.
Ajouté le 08/04/2014 à 21:31 :
Et je l'ai ajouté à la liste des tutos de qualité, il commence à devenir conséquent.
Ajouté le 08/04/2014 à 21:31 :
Et pour finir avec un quadruple post, n'hésitez pas à me corriger si je dis des âneries !
Citer : Posté le 09/04/2014 18:53 | #
D'accord, il y a une énorme ânerie !
unsigned char config[] = {0, 5, 0, 0, 0, 0};
// configuration des ports à 9800
Dans le tableau juste au dessus, il y a écrit que 5 correspond à 9600 et non 9800...
Ajouté le 09/04/2014 à 18:54 :
Ah aussi : paragraphe 4)
T'as oublié une lettre.
Toutes les formules de Première S.
Toutes les formules de Terminale S.
Un programme de calculs.
Super Mario 3
warrior
Jump Ball
First Fly
►Jeu gagnant des 48h CPC n°12◄
Mon site de discussion pour ados : http://entre-ados.net/ (a brûlé dans l'incendie d'OVH)
Mon éditeur de cours en ligne et plateforme de partage : http://wordline.xyz (a succombé à la concurrence de Google Drive...)
Citer : Posté le 09/04/2014 19:02 | #
Peut-tu m'expliquer comment envoyer des tableaux de int? Car quand je fais
int taille = 16;
erreur = Serial_WriteBytes(monTableau, taille*4);
Puis
short tailleLue = 0;
erreur = Serial_ReadBytes(Buffer, 17*4, &tailleLue);
Seules les 8 premières valeurs(de Buffer[0] à Buffer[7]) ont les bonnes valeurs mais les autres sont à 0
Citer : Posté le 09/04/2014 19:30 | #
Quelle est la valeur de erreur après l'appel de ces deux fonctions ? Et tailleLue après avoir récupéré les valeurs ?
Ça peut aider pour le débug, parce que je ne vois pas où se trouve le problème en l'état actuel, bien que j'ai une idée.
Ajouté le 09/04/2014 à 19:30 :
Tenmatx : merci, c'est corrigé
Citer : Posté le 10/04/2014 18:47 | #
erreur = Serial_ReadBytes(Buffer, 17*4, &tailleLue); //erreur=0 tout s'est bien passé et tailleLue=68
Citer : Posté le 12/04/2014 17:38 | #
Bizarre...
Et en essayant de lire en plusieurs fois ?
Selon moi, c'est qu'il n'a pas eu le temps de tout envoyer : Serial_WriteBytes écrit en presque instantané, mais il faut un peu de temps à la calto avant de tout envoyer.
En mettant un Sleep() entre les deux, ça fonctionne ?
Ajouté le 12/04/2014 à 17:55 :
Mis à jour :
-> Ajout d'un point en annexe sur les Bauds
Citer : Posté le 17/04/2014 07:04 | #
C'est bon c'était ça merci
Citer : Posté le 17/04/2014 18:33 | #
Super
Ajouté le 18/04/2014 à 19:14 :
Edit : Ajout d'un sommaire
Citer : Posté le 18/04/2014 19:42 | #
Le sommaire en soi est bon mais tu peux bazarder les liens. Étant donné qu'ils ouvrent un nouvel onglet, un simple mouvement de l'index d'avant en arrière est nettement plus avantageux.
Citer : Posté le 18/04/2014 21:38 | #
Ils sont là uniquement pour la redirection depuis d'autres pages
Citer : Posté le 14/08/2014 16:33 | #
Petite question,
On est obligé d'ouvrir le port com au début du programme avec Serial_Open() ou on peut le faire dans une petite fonction annexe appelée a un moment t du programme ?
Citer : Posté le 14/08/2014 16:38 | #
tu peux le faire dans une fonction annexe, le tout c'est de dire à la calto "surveille ton port série"
Citer : Posté le 14/08/2014 16:39 | #
Ok je vais voir si j'y arrive
Ajouté le 14/08/2014 à 17:24 :
Il y a un moyen de synchroniser les deux caltos pour que l'une attende l'autre si part exemple elle a plus de calculs a faire ?