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
) 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
).
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
) ... on reste dans le très très expérimental par un gars qui découvre
.
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"
) :
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 11/05/2022 08:31 | #
Première suspicion : je ne vois pas de world switch. J'ai aucune idée de ce qu'il se passe dans ces syscalls ni de comment ça interagit avec gint.
Citer : Posté le 11/05/2022 08:40 | #
Oui je me suis posé la question de savoir si on a besoin ou pas, je peux essayer avec pour voir si ça change qqchose.
Je fais l'essai et update en fonction.
Ajouté le 11/05/2022 à 08:42 :
Je suis en train de me dire, visiblement le Serial_Open, SerialIsOpen et Serial_Close fonctionnent sans gint_world_switch ...
Citer : Posté le 11/05/2022 08:44 | #
Et donc ? L'écriture pourrait utiliser le DMA.
Citer : Posté le 11/05/2022 08:44 | #
certes
Citer : Posté le 11/05/2022 08:48 | # | Fichier joint
Non, faux espoir, idem malgré le remplacement de la ligne
int result = Serial_WriteSingle( (unsigned char) 'Y' );
par :
int result = (int) gint_world_switch( GINT_CALL( Serial_WriteSingle, 'Y' ) );
Mais c'était une très très bonne idée.
Citer : Posté le 11/05/2022 19:36 | #
Il faut absolument restaurer la VBR dans tous les cas car Casio utilise l'interruption du module SCIF pour fonctionner que ce soit en lecture ou en écriture. Donc le world switch est obligatoire. Essaie de passer par le syscall 0x1BBE Serial_Write(void *data, size_t count)
Ajouté le 11/05/2022 à 19:40 :
Et je viens de regarder le syscall Serial_Open(), idem, ça utilise beaucoup plus de module hardware (TMU, POWER, ...) et probablement une synchronisation est faites via le gestionnaire d'interruption. Donc c'est full world-switch si tu veux utiliser ces syscalls
Citer : Posté le 11/05/2022 20:23 | #
Ok, je vais tout mettre dans une fonction et faire un call de cette fonction a travers un gint_world_switch pour voir si ca aide.
Je fais un point quand j'ai fait l'essai.
Pour info en fouinant sur Cemetech j'ai trouvé quelqu'un qui reporte que le code suivant fonctionne (on est pas loin de mon code)
static unsigned char serialMode[6] = {0, 5, 0, 0, 0, 0};
void dumpToSerial(unsigned int * startAddress, unsigned int size) {
unsigned int i;
if (Serial_IsOpen() == 1) {
Serial_Close(1);
}
Serial_Open(serialMode);
for (i = 0; i < size; i++) {
while(1) {
response = Serial_WriteSingle(*(startAddress + i));
if (response == 0) {
break;
} else if (response == 2) {
// Transmit Buffer is full, we need to wait
} else if (response == 3) {
PrintXY(1,2," Not Open",0x00,TEXT_COLOR_BLUE);
while(1) GetKey(&key);
break;
}
}
}
}
donc on doit pas être très très loin, faut juste trouver ce qui coince ...
Citer : Posté le 11/05/2022 20:25 | #
Mets aussi une attente avant de revenir dans gint, il pourrait y avoir des trucs asynchrones.
Note que la "bonne" façon de faire est bien sûr de coder un driver dans gint.
Citer : Posté le 11/05/2022 20:33 | #
Je suis tout à fait d'accord, je suis juste dans le défrichage
Le grosse étape à la serpette, puis vient le temps des paysagistes qui font ça propre
Citer : Posté le 12/05/2022 08:13 | #
AH OUI ! Parce que le Serial_Write() est extrêmement simple : il copie l'info dans un buffer interne (à la main) puis Casio active l'interruption de "transmission" puis sort de la fonction
Bonne chance pour sycro tout ça, j'imagine qu'il y a un bit qui indique le status de la transaction (tu peux check si la valeur du bit SCSCR. TIE == 0, si c'est le cas, Casio attend plus rien (il active l'interruption uniquement quand il y a besoin et désactive une fois le buffer de transmission vide (à voir pour le buffer de réception, je n'ai pas regardé)))
Citer : Posté le 12/05/2022 08:29 | #
J'imagine que la valeur de l'octet 4 passé à Serial_Open (Stop bit) doit effectivement servir à ça.
J'avais peu de temps hier alors j'ai juste pu faire un petit essais (totalement infructueux) d'encapsulation de tout le toutim dans un world-switch.
Pas fructueux, mais au moins ça a pas crash comme à l'accoutumée.
A un moment, il faudra je pense revenir à la doc du 7305 (enfin 7724) et se palucher les registres. Ton RE sera certainement ultra utile à ce moment.
Tu aurais dispo le RE de tous les syscalls Serial_*** ? Pour voir exactement ce que chacun fait de manière détaillée.
Merci pour ton aide Yatis.
Citer : Posté le 02/08/2022 15:39 | #
Est-ce que quelqu’un a trouvé une solution pour éviter le crash ?
Citer : Posté le 02/08/2022 16:59 | #
Hello, non je n'ai toujours pas trouvé d'où venait le problème.
Bon pour être tout à fait honnête, j'ai eu pas mal de choses à gérer qui m'ont un peu éloigné de la programmation depuis quelques semaines/mois.
J'essaierai de jeter un œil, peut être qu'une vision un peu neuve me donnera de meilleures idées.
Citer : Posté le 02/08/2022 17:33 | #
Je crois avoir trouvé : les syscalls ne sont pas bons par exemple, le syscall du Serial_WriteSingle est 0x040E et pas 0x1BBC. Avec ce changement la calculatrice ne crash plus, l'Arduino reçoit quelque chose mais il reçoit le caractère "⸮" (pour moi en tt cas). Mais c'est déja une grande avancée ! Il faut prendre ça : https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_Serial.htm
Il faut juste le fix dans la lib.
EDIT : finalement ce n'est pas ça qui l'a fait marché (je n'avais finalement pas recompilé la lib). En bidouillant dans la lib (jsp quoi) ça remarche comme par magie ...
Citer : Posté le 02/08/2022 17:49 | #
Juste, quelqu'un sait pourquoi gint_world_switch(GINT_CALL( Serial_WriteSingle, text)); met une erreur ?
Citer : Posté le 02/08/2022 17:55 | #
Alors pour les numéros de syscall c'est parce que la lib elle est explicitement pour Graph 90+E. x)
Pour l'erreur avec le switch, est-ce que tu penses pas qu'il y a une information super utile que tu pourrais inclure avec la question ?
Citer : Posté le 02/08/2022 17:56 | #
la partie de code est :
gint_world_switch(GINT_CALL(Serial_WriteSingle, text));
Citer : Posté le 02/08/2022 18:02 | #
Pour les petits entiers il faut cast en int. C'est marqué dans le tuto... :x
Pour utiliser un char ou un short, castez en int et faites prendre un int à la fonction. Par exemple GINT_CALL(ma_fonction, (int)mon_char).
Citer : Posté le 02/08/2022 18:04 | #
Oui ça marche !! merci !
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 ...