Essai de communication via port Serial 3-pins CG10/20/50
Posté le 11/05/2022 08:22
Hello,
ayant juste un petit peu de temps, cette fin de semaine avant d'être une semaine off-line, je voulais regarder un peu la
libsnd et le
port serial 3-pins. Je me suis donc imprégné de la documentation que j'ai pu trouver ici et là. Mes recherches m'ont permis d'identifier les sources suivantes :
- les sources de la
libsnd de Thomas Williamson (utilisée dans l'excellentissime Nesizm) et du
PrizmSDK
- la doc de la
bible PC collectée par Simon Lothar (partie CG20 - Serial) dispo ici :
serial CG20
- l'explication de texte des
syscalls correspondants dans le Wiki de Cemetech ici :
wiki Serial
A partir de cela, je me suis fait une mini-librairie (je sais, c'est maladif en ce moment
![](https://www.planet-casio.com/images/smileys/mrgreen.gif)
) appelée
libserial (original, vous ne trouvez pas) qui reprend les
syscalls suivants directement liés au port serial 3-pins, ainsi que le header correspondant (
serial.h) :
- Serial_ClearRX
- Serial_ClearTX
- Serial_Close
- Serial_IsOpen
- Serial_Open
- Serial_Peek
- Serial_PollRX
- Serial_PollTX
- Serial_Read
- Serial_ReadSingle
- Serial_Write
- Serial_WriteSingle
- Serial_WriteUnbuffered
ainsi que les suivants, plus ou moins éloignés, mais repris dans le prizmSDK (je les virerais si il ne servent effectivement pas) :
- App_LINK_GetDeviceInfo
- App_LINK_GetReceiveTimeout_ms
- App_LINK_Send_ST9_Packet
- App_LINK_SetReceiveTimeout_ms
- App_LINK_SetRemoteBaud
- App_LINK_TransmitInit
- App_LINK_Transmit
- Comm_Close
- Comm_Open
- Comm_Terminate
- Comm_TryCheckPacket
Pour vérifier que tout fonctionne, j'ai fait un simple petit programme pour vérifier l'état du port serial (ouvert et / ou fermé), puis procéder à son ouverture et à sa fermeture, tout cela sur ma CG50 (Graph 90+E) et ma CG20 pour vérifier que les
syscalls sont toujours bien utilisables. Ok, ça compile, ça link et surtout ça fonctionne. Voici le code utilisé :
Vérif, ouverture et fermeture du port Serial - Cliquer pour dérouler
Vérif, ouverture et fermeture du port Serial - Cliquer pour enrouler
#include <gint/display.h>
#include <gint/keyboard.h>
#include <serial.h>
int main(void)
{
dclear(C_WHITE);
dtext(1, 1, C_BLACK, "Sample fxSDK Communication add-in.");
if (Serial_IsOpen() != 1)
{
dtext(1,21, C_RED, "Serial port is CLOSED !!!" );
dtext(1,31, C_RGB( 31, 24, 0), "Trying to Open it ..." );
unsigned char mode[6]={0,9,0,0,0,0};
Serial_Open( mode );
}
if (Serial_IsOpen() == 1) dtext(1,41, C_GREEN, "Serial port is now OPEN !!!" );
else
{
dtext(1,41, C_RED, "Failed to open the Serial port : STILL CLOSED !!!" );
dtext(1,51, C_BLACK, "Press a key to exit back to OS ..." );
dupdate();
getkey();
return 0;
}
if (Serial_IsOpen() == 1)
{
dtext(1,121, C_RED, "Now closing the Serial Port !!!" );
Serial_Close( 1 );
}
if (Serial_IsOpen() != 1) dtext(1,141, C_GREEN, "Serial port is now Closed !!!" );
dupdate();
getkey();
return 1;
}
C'est donc un début vraiment encourageant, dans le sens où la gestion du port se fait correctement et la mini librairie semble bien fonctionner de son côté.
Bon, donc plein d'espoir, j'essaie de passer à l'étape suivante :
- sur une des 2 machines, envoyer un paquet de
char dans le buffer de transmission (donc transmettre, en langage décodé),
- sur l'autre machine, recevoir un paquet de
char dans le buffer de réception (donc recevoir
![](https://www.planet-casio.com/images/smileys/smile.gif)
).
Le but étant ici juste d'envoyer d'un côté et de lire de l'autre côté, sans contrôle d'erreur et tout ce qu'il faudrait faire pour avoir des vraies communications "sérieuses" (donc pas taper merci
![](https://www.planet-casio.com/images/smileys/mrgreen.gif)
) ... on reste dans le très très expérimental par un gars qui découvre
![](https://www.planet-casio.com/images/smileys/grin.gif)
.
Ceci étant réalisé par le code suivant (enfin, l'expression adéquate serait plutôt : "ceci devant être réalisé par le code suivant"
![](https://www.planet-casio.com/images/smileys/sad.gif)
) :
Envoi et réception sur port Serial - Cliquer pour dérouler
Envoi et réception sur port Serial - Cliquer pour enrouler
*
#include <gint/display.h>
#include <gint/keyboard.h>
#include <serial.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
dclear(C_WHITE);
dtext(1, 1, C_BLACK, "Sample fxSDK Communication add-in.");
if (Serial_IsOpen() != 1)
{
dtext(1,21, C_RED, "Serial port is CLOSED !!!" );
dtext(1,31, C_RGB( 31, 24, 0), "Trying to Open it ..." );
unsigned char mode[6]={0,9,0,0,0,0};
Serial_Open( mode );
}
if (Serial_IsOpen() == 1) dtext(1,41, C_GREEN, "Serial port is now OPEN !!!" );
else
{
dtext(1,41, C_RED, "Failed to open the Serial port : STILL CLOSED !!!" );
dtext(1,51, C_BLACK, "Press a key to exit back to OS ..." );
getkey();
return 0;
}
dtext(1,210, C_BLACK, "SEND");
dtext(60, 210, C_BLACK, "RECV");
dtext(1, 61, C_BLACK, "Press F1 to set Send mode and F2 to set Receive mode" );
dupdate();
if (getkey().key == KEY_F1)
{
char MessageToSend[256];
strcpy( MessageToSend, "Hello World !!!" );
int CountSend = 15;
dprint(1, 81, C_BLUE, "Will send a message %s ", MessageToSend );
dupdate();
getkey();
// int result = Serial_Write( (unsigned char*) MessageToSend, CountSend ); // Fait Crash la babasse
int result = Serial_WriteSingle( (unsigned char) 'Y' ); // idem, fait aussi crash la babasse
if (result==0)
{
dprint(1, 101, C_GREEN, "Message sent" );
}
else if (result==2)
{
dprint(1, 101, C_RED, "buffer is full" );
}
dupdate();
getkey();
}
else if (getkey().key == KEY_F2)
{
unsigned char MessageRead[256];
int MaxCountRead = 256;
short int CountRead = 0;
dtext(1, 81, C_BLUE, "Will receive a message " );
dupdate();
int result = Serial_Read( MessageRead, MaxCountRead, &CountRead );
if (result==0)
{
dprint(1, 101, C_GREEN, "%s", MessageRead );
}
else if (result==1)
{
dprint(1, 101, C_RED, "buffer is empty" );
}
dupdate();
getkey();
}
if (Serial_IsOpen() == 1)
{
dtext(1,121, C_RED, "Now closing the Serial Port !!!" );
Serial_Close( 1 );
}
if (Serial_IsOpen() != 1) dtext(1,141, C_GREEN, "Serial port is now Closed !!!" );
dupdate();
getkey();
return 1;
}
En mode réception (appui sur F2 sur une des machines, cela semble fonctionner (sauf qu'il y a rien à lire donc j'ai le message correspondant affiché), par contre en émission (sur l'autre machine, après appui sur F1), j'ai systématiquement un crash avec reboot de la machine sur la ligne du Serial_Write/Serial_WriteSingle.
J'ai bien entendu testé chacune des machines en réception ET en émission, et c'est bien le même comportement sur chacune d'elle. L'émission fait systématiquement crasher et rebooter la machine.
Auriez-vous une idée ou un semblant d'idée pouvant expliquer ce comportement ?
Je précise que les syscalls sont bien ceux de la page ici (avec leur code respectifs) :
serial CG20
Ciao
Sly
Citer : Posté le 10/08/2022 16:27 | #
Mon programme marchait mais pour je ne sais quelle raison sans que je ne change rien, il recrash comme avant ou ne veut pas ouvrir la connexion ...
Si qq a une idée ...
Citer : Posté le 11/09/2022 12:15 | #
Bonjour je pence que j'ai mal fait (ou compris) qq chose car gint_world_switch(GINT_CALL(Serial_ReadSingle)) renvoie 0 (ou un pointeur vers 0) à tous les cous. Comment je peux récupérer le résultat ?
EDIT: Serial_ReadSingle(*var) marche sans gint_world_switch finalement mais le buffer de réception s'update que quand je démarre le programme ...
Merci
Citer : Posté le 11/09/2022 13:27 | #
C'est normal gint contrôle les interruptions donc l'OS n'a jamais l'opportunité de récupérer les données. Il faut bien faire un world switch. Tu lui passes quels paramètres durant le world switch ?
Edit : parce que la valeur de retour c'est un code d'erreur, par l'octet.
int rc = gint_world_switch(GINT_CALL(Serial_ReadSingle, &c));
Edit 2 : Oui et fais pas un world switch à chaque appel hein ^^"
Citer : Posté le 11/09/2022 13:29 | #
Je lui donne un pointeur vers la variable de sortie.
Je vais essayer avec gint_world_switch()
EDIT : avec gint_world_switch ça marche parfaitement !
Merci
Citer : Posté le 11/09/2022 14:17 | #
Cool ! N'oublie pas que gint_world_switch() a un coût, si tu effectues plusieurs appels à la lib serial d'un coup il vaut mieux faire un seul world switch et faire tous les appels à la suite dedans :
{
/* Exemple bidon */
Serial_ReadSingle(c1);
Serial_ReadSingle(c2);
Serial_ReadSingle(c3);
}
gint_world_switch(GINT_CALL(read_3_characters, &c1, &c2, &c3));
Tu peux rester dans le world switch aussi longtemps que tu veux tant que n'utilises pas le matériel via gint (typiquement pour dupdate(), getkey(), il faut revenir dans gint). Tu peux quand même calculer, stocker des variables, allouer de la mémoire, etc.
Citer : Posté le 11/09/2022 15:59 | #
Cool ! N'oublie pas que gint_world_switch() a un coût, si tu effectues plusieurs appels à la lib serial d'un coup il vaut mieux faire un seul world switch et faire tous les appels à la suite dedans :
Oui je dois envoyer une image pixel par pixel donc ça fait beaucoup de call. Je vais penser à faire une fonction.
Citer : Posté le 12/09/2022 19:04 | #
A lire rapidement j’ai l’impression que tu as résolu certains problèmes Yannis300307.
N’hésite pas à nous lâcher un morceau de code qui fonctionne pour servir de tuto à d’autres qui voudraient essayer.
Désolé en ce moment je suis hyperbooké et j’ai pas eu le temps de me remettre dessus.
Citer : Posté le 12/09/2022 19:12 | #
A lire rapidement j’ai l’impression que tu as résolu certains problèmes Yannis300307.
Oui mais... je sais pas vraiment comment ça se fait car je n'est rien fait de particulier et ça s'est mit à marcher ...
N’hésite pas à nous lâcher un morceau de code qui fonctionne pour servir de tuto à d’autres qui voudraient essayer.
J'ai écrit du code mais il n'est pas très lisible et tres spécifique. Si mon projet fini par marcher, je pourrais peut être le publier.
Citer : Posté le 19/11/2022 12:07 | #
En installant la lib avec giteapc, en supprimant les .o du Serial_Write et du Serial_WriteSingle et en recompilant avec "giteapc build Slyvtt/libSerial" ça marche parfaitement !
Citer : Posté le 19/11/2022 12:09 | #
Si tu fais make -B ça peut aider à tout recompiler. Mais sinon il se peut que le Makefile ait pas les dépendances bien déclarées en effet.
Citer : Posté le 19/11/2022 13:54 | #
Tu m’intéresses. Peux ru partager tes sources ? En MP si tu veux. Je vais bosser sur le serial donc avec un cas documenté fonctionnel sera utile.
Merci
Citer : Posté le 19/11/2022 14:40 | #
ce programme est quasiment celui de base (il envoie "B" quand on appuie sur exe). Je l'ai fait vraiment pou tester l'envoie donc la moitier du code ne sert à rien mais normalement avec la manipe dite au dessus, ça devrait marcher.
#include <gint/keyboard.h>
#include <serial.h>
int main(void)
{
dclear(C_WHITE);
dtext(1, 1, C_BLACK, "Sample fxSDK Communication add-in.");
if (Serial_IsOpen() != 1)
{
dtext(1,21, C_RED, "Serial port is CLOSED !!!");
dtext(1,31, C_RGB(31, 24, 0), "Trying to Open it ...");
unsigned char mode[6]={0,5,0,0,0,0};
gint_world_switch(GINT_CALL(Serial_Open, mode));
//Serial_Open(mode);
}
if (Serial_IsOpen() == 1) dtext(1,41, C_GREEN, "Serial port is now OPEN !!!");
else
{
dtext(1,41, C_RED, "Failed to open the Serial port : STILL CLOSED !!!");
dtext(1,51, C_BLACK, "Press a key to exit back to OS ...");
dupdate();
getkey();
return 0;
}
while (1) {
char text = 'B';
gint_world_switch(GINT_CALL(Serial_WriteSingle, (int) text));
dupdate();
getkey();
}
if (Serial_IsOpen() == 1)
{
dtext(1,121, C_RED, "Now closing the Serial Port !!!");
Serial_Close( 1 );
}
if (Serial_IsOpen() != 1) dtext(1,141, C_GREEN, "Serial port is now Closed !!!");
dupdate();
getkey();
return 1;
}
Citer : Posté le 19/11/2022 16:52 | #
ce programme est quasiment celui de base (il envoie "B" quand on appuie sur exe). Je l'ai fait vraiment pou tester l'envoie donc la moitier du code ne sert à rien mais normalement avec la manipe dite au dessus, ça devrait marcher.
OK merci, et as tu le code symétrique pour la réception ?
Citer : Posté le 19/11/2022 19:14 | #
Que veux-tu dire par symétrique ?
EDIT : Non je n'est pas de code qui marche vraiment pour la reception mais je travail sur un programme qui en aura besoin. Je vous tien au courant.
Citer : Posté le 21/12/2022 12:16 | #
Un petit post histoire de faire un point sur le taf du moment pour moi.
Je me suis lancé dans la merveilleuse (et folle) aventure de désassemble l'OS de la G90+E version 3.60 pour (essayer de) comprendre (et documenter dans la mesure du possible) comment fonctionne le serial via le port 3-pins.
Le but ultime étant bien entendu d'avoir le support de la communication serial dans gint, et donc possiblement du multi-joueur ou de la musique/du son.
Voici donc quelques éléments récoltés au fur et à mesure du décodage :
- visiblement, les syscalls de l'OS ne font fonctionner le SCIF_2 (confirmé par sa mise en route dans le module POWER) qu'en mode Asynchrone (SCIF.SCSMR.CA toujours à 0), je n'ai pas trouvé trace de configuration en mode synchrone (SCIF.SCSMR.CA set à 1).
- les pins correspondants aux ports TXD et RXD sont respectivement les pins PU3 et PU2 du PFC.PUCR, étant configurés en output pour le premier et en input Pull-Up MOS On pour le second. Il s'agit de la fonction "principale" du port car aucune sélection de "fonction annexe" via PSELx n'est opérée avant. C'est suffisant pour le mode Asynchrone, donc cohérent avec item précédent.
- Le mode synchrone n'est pas utilisé visiblement par l'OS. A priori on devrait pouvoir s'en servir, mais il faut identifier le pin du PFC qui correspond au port SCK afin de synchroniser les horloges externes, mais hélas je n'ai à ce stade pas réussi à identifier le pin en question. J'ai néanmoins quelques soupçons concernant PFC.PJCR car le syscall Serial_DisableHardware() fait une tambouille sur PUCR (normal donc car TXD et RXD sont dessus), mais aussi sur PJCR via une sélection de fonction. Il doit donc intervenir quelque part sur le serial.
Voilà un petit état des lieux. Fonctionnellement parlant, c'est pas encore top, à ce stade j'arrive juste à ouvrir le port en Asynchrone avec la configuration qui va bien (sans crash). Je sais envoyer un octet dans la pile FIFO de transmission, mais je n'arrive hélas pas à le relire dans le FIFO de réception qui reste désespérément vide. Visiblement le transfert de l'octet ne se fait pas, ni en "interne machine" malgré une configuration du port en loopback, ni en externe en connectant physiquement deux machines entre elles (une CG20 et un CG50). Il faudra donc investiguer plus en profondeur.
Voilà le résumé de l'épopée de ces derniers jours.
Suite au prochain épisode avec j'espère quelques bonnes nouvelles.
Citer : Posté le 21/12/2022 12:53 | #
En extrapolant un peu, j'en viens à penser que le syscall Serial_Open( unsigned char *mode) qui n'accepte que mode[0]=0 est prévu pour potentiellement être étendu (ou a été volontairement limité).
On pourrait penser que mode[0]=0 correspond à de la transmission asynchrone, donc on configure les buffers et les ports TXD et RXD. Ce qui est fait dans le syscall Serial_Open tel que présent dans l'OS.
mode[0]!=0 pourrait correspondre à de la transmission synchrone (mais non implémentée) et il faudra alors avoir une configuration spécifique des ports TXD/RXD et SCK en plus ainsi que tout un arsenal de truc en plus.
Mais cela n'est bien entendu que pure spéculation de ma part.
Il serait intéressant de voir si sur d'autres version d'OS (notamment les vieux OS SH3 ou début SH4) on a le cas complet et si Casio a censuré récemment le mode synchrone ou si au contraire il n'a juste jamais été implémenté.
Edit : bon en fait j'ai un morceau de ma réponse. Dans l'OS 2.05 de la G75+E, le code de Serial_Open() est strictement identique. Donc soit c'est plus vieux encore la modif, soit ça a jamais été implémenté du tout
Citer : Posté le 22/12/2022 12:04 | #
Suite de mes investigations sur le serial :
Lors d'une session discord avec Lephe, nous recherchions la meilleure façons de fixer les paramètres d'horloge interne du SCSMR et le code bitrate correspondant du SCBRR dans le SCIF, sachant que ce dernier doit être contenu sur une valeur codée sur 8bits et qu'il dépend en particulier de la fréquence du processeur (P_phi) et de la valeur choisie sur les bits CKS du registre SCSMR (horloge interne du SCIF calée sur P_Phi, P_phi/4, P_phi/16, P_phi/64).
Selon le bitrate de transmission/réception voulu parmis des choix donnés (300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200bps) on peut trouver des combinaisons par calcul, les formules étant données par les manuels du SH4, notamment celui du SH7724.
C'est la méthode que j'essaie de coder pour gint, connaissant la valeur de CKS et de P_phi, on peut calculer la valeur de BRR. Et bien nos amis de Casio eux on juste bourriné les valeurs dans un tableau en fonction de la machine considérée (j'ai regardé dans l'OS 3.60 de la CG50 et dans l'OS 3.12 de la CG20). on retrouve seulement les combinaisons marquée en vert dans le tableau des possibilités.
Ce qui signifie en d'autres termes, ce que je viens de vérifier, que 2 machines non overclockées peuvent communiquer entre elles, même si elles sont différentes (par exemple CG20 et CG50), par contre si au moins une des deux n'a pas le P_phi d'origine, les communications serial ne fonctionnent plus. Il serait intéressant de voir avec deux machines identiques si ce serait OK, car les CG20/50 ne gardent pas le même ratio de multiplication du P_phi lors de l'overclock.
Bref, tout ça pour dire que si on fait pas ça propre dans gint, on aura le même souci. donc il faudra bien qu'on calcule nos valeurs de BRR.
Casio n'a pas le problème car vu de sa fenêtre, une machine overclockée ça n'existe pas, il sa calent donc sur les valeurs "usines".
Voilà les pensées du jour ...
Citer : Posté le 29/01/2023 15:10 | #
Wow c'est impressionnant ces recherches ! Personnellement j'ai réussi à envoyer des donnés de la calculatrice à l'ordinateur. Mais je n'arrive pas à recevoir des données. Le Serial_ReadSingle me renvoie toujours 0 (ou alors j'ai mal fait quelque chose...).