Forum Casio - Vos tutoriels et astuces


Index du Forum » Vos tutoriels et astuces » [Tutoriel] Communiquez entre caltos en C/C++ !
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

[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


Pages : 1, 2Suivante
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

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
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
NemhardyHors ligneGrand maître des Traits d'EspritPoints: 1235 Défis: 54 Message

Citer : Posté le 19/03/2014 20:13 | #


Dark Storm a écrit :
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 !
N'attendez pas qu'il n'y ait plus de miel : スススススススススススススススススススススススススス養蜂家スススススススススススススススススススススススススススススススススススス蜂家
LancelotHors ligneMembrePoints: 1274 Défis: 160 Message

Citer : Posté le 19/03/2014 21:39 | #


Cool !
Calculatrices : Casio 35+ SH4 (modifiée 75) et fx-CG 20 PRIZM
Projets que je soutiens
Des exemples parmi tant d'autres
Pokémon Jade de Dododormeur
Zelda de Smashmaster
Super Geek Brothers de Siapran
Mes Programmes
Mes Programmes
Mes Projets
Mes Projets
ColorLib
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
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

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
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
TotoyoHors ligneMembre d'honneurPoints: 15904 Défis: 101 Message
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

Citer : Posté le 07/04/2014 19:10 | #


Merci, c'est corrigé.
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
TheprogHors ligneMembrePoints: 1447 Défis: 20 Message

Citer : Posté le 07/04/2014 19:11 | #


*Impatient*
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

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 !
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
TenmatxHors ligneMembrePoints: 994 Défis: 2 Message

Citer : Posté le 09/04/2014 18:53 | #


D'accord, il y a une énorme ânerie !

Dark storm a écrit :
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)

si vos utilisez cette méthode

T'as oublié une lettre.
Mes programmes de maths
Mes programmes de maths

Toutes les formules de Première S.
Toutes les formules de Terminale S.
Un programme de calculs.
Mes meilleurs jeux
Mes meilleurs jeux

Jeu gagnant des 48h CPC n°12

Mon site de discussion pour ados : http://entre-ados.net/
Mon éditeur de cours en ligne et plateforme de partage : http://wordline.xyz
IntelligideHors ligneMembre de CreativeCalcPoints: 46 Défis: 5 Message

Citer : Posté le 09/04/2014 19:02 | #


Peut-tu m'expliquer comment envoyer des tableaux de int? Car quand je fais
int Tableau[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int taille = 16;
erreur = Serial_WriteBytes(monTableau, taille*4);


Puis
unsigned int Buffer[17]={0};
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
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

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é
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
IntelligideHors ligneMembre de CreativeCalcPoints: 46 Défis: 5 Message

Citer : Posté le 10/04/2014 18:47 | #


erreur = Serial_WriteBytes(monTableau, taille*4); //erreur=0 tout c'est bien passé
erreur = Serial_ReadBytes(Buffer, 17*4, &tailleLue); //erreur=0 tout s'est bien passé et tailleLue=68
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

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
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
IntelligideHors ligneMembre de CreativeCalcPoints: 46 Défis: 5 Message

Citer : Posté le 17/04/2014 07:04 | #


C'est bon c'était ça merci
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

Citer : Posté le 17/04/2014 18:33 | #


Super

Ajouté le 18/04/2014 à 19:14 :
Edit : Ajout d'un sommaire
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
PositonHors ligneRédacteurPoints: 2396 Défis: 57 Message

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.
<<< Si jusque là vous aviez cru que mon pseudo est "Position", il est encore temps de regarder à gauche

Ça m'énerve les gens qui ne finissent pas leurs
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

Citer : Posté le 18/04/2014 21:38 | #


Ils sont là uniquement pour la redirection depuis d'autres pages
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
TheprogHors ligneMembrePoints: 1447 Défis: 20 Message

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 ?
Dark stormHors ligneMembre d'honneurPoints: 10766 Défis: 174 Message

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"
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
TheprogHors ligneMembrePoints: 1447 Défis: 20 Message

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 ?
Pages : 1, 2Suivante

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2019 | Il y a 67 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd