Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » gint : un noyau pour développer des add-ins
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

gint : un noyau pour développer des add-ins

Posté le 20/02/2015 17:30

Ce topic fait partie de la série de topics du fxSDK.

En plus des options de programmation intégrée comme le Basic Casio ou Python, la plupart des calculatrices Casio supportent des add-ins, des programmes natifs très polyvalents avec d'excellentes performances. Les add-ins sont généralement programmés en C/C++ avec l'aide d'un ensemble d'outils appelé SDK.

Plusieurs SDK ont été utilisés par la communauté avec le temps. D'abord le fx-9860G SDK de Casio avec fxlib pour Graph monochromes (plus maintenu depuis longtemps). Puis le PrizmSDK avec libfxcg pour Prizm et Graph 90+E (encore un peu actif sur Cemetech). Et plus récemment celui que je maintiens, le fxSDK, dont gint est le composant principal.

gint est un unikernel, ce qui veut dire qu'il embarque essentiellement un OS indépendant dans les add-ins au lieu d'utiliser les fonctions de l'OS de Casio. Ça lui permet beaucoup de finesse sur le contrôle du matériel, notamment la mémoire, le clavier, l'écran et les horloges ; mais aussi de meilleures performances sur le dessin, les drivers et la gestion des interruptions, plus des choses entièrement nouvelles comme le moteur de gris sur Graph monochromes.

Les sources de gint sont sur la forge de Planète Casio : dépôt Gitea Lephenixnoir/gint

Aperçu des fonctionnalités

Les fonctionnalités phares de gint (avec le fxSDK) incluent :

  • Toutes vos images et polices converties automatiquement depuis le PNG, sans code à copier (via fxconv)
  • Un contrôle détaillé du clavier, avec un GetKey() personnalisable et un système d'événements à la SDL
  • Une bibliothèque standard C plus fournie que celle de Casio (voir fxlibc), et la majorité de la bibliothèque C++
  • Plein de raccourcis pratiques, comme pour afficher la valeur d'une variable : dprint(1,1,"x=%d",x)
  • Des fonctions de dessin, d'images et de texte optimisées à la main et super rapides, surtout sur Graph 90+E
  • Des timers très précis (60 ns / 30 µs selon les cas, au lieu des 25 ms de l'OS), indispensables pour les jeux
  • Captures d'écran et capture vidéo des add-ins par USB, en temps réel (via fxlink)

Avec quelques mentions spéciales sur les Graph monochromes :
Un moteur de gris pour faire des jeux en 4 couleurs !
La compatibilité SH3, SH4 et Graph 35+E II, avec un seul fichier g1a
Une API Unix/POSIX et standard C pour accéder au système de fichiers (Graph 35+E II seulement)

Et quelques mentions spéciales sur les Graph 90+E :
Une nouvelle police de texte, plus lisible et économe en espace
Le dessin en plein écran, sans les bordures blanches et la barre de statut !
Un driver écran capable de triple-buffering
Une API Unix/POSIX et standard C pour accéder au système de fichiers

Galerie d'add-ins et de photos

Voici quelques photos et add-ins réalisés avec gint au cours des années !



Arena (2016)Plague (2021)



Rogue Life (2021)



Momento (2021)



Communication avec le PC (cliquez pour agrandir)


Utiliser gint pour développer des add-ins

Les instructions pour installer et utiliser gint sont données dans les divers tutoriels recensés dans le topic du fxSDK. Il y a différentes méthodes de la plus automatique (GiteaPC) à la plus manuelle (compilation/installation de chaque dépôt). Le fxSDK est compatible avec Linux, Mac OS, et marche aussi sous Windows avec l'aide de WSL, donc normalement tout le monde est couvert

Notez en particulier qu'il y a des tutoriels de développement qui couvrent les bases ; tout le reste est expliqué dans les en-têtes (fichiers .h) de la bibliothèque que vous pouvez consulter en ligne, ou dans les ajouts aux changelogs ci-dessous.

Changelog et informations techniques

Pour tester les fonctionnalités et la compatibilité de gint, j'utilise un add-in de test appelé gintctl (dépôt Gitea Lephenixnoir/gintctl). Il contient aussi une poignée d'utilitaires d'ordre général.

Ci-dessous se trouve la liste des posts indiquant les nouvelles versions de gint, et des liens vers des instructions/tutoriels supplémentaires qui accompagnent ces versions.

VersionDateInfos supplémentaires
gint 2.10.02 Avril 2023
gint 2.9.021 Août 2022
gint 2.8.017 Mai 2022Effets dynamiques sur les imagesAPI de manipulations d'images
Overclock intégré
gint 2.7.119 Mars 2022Tutoriel capture des flux standards
gint 2.7.031 Décembre 2021
gint 2.6.029 Août 2021Tutoriel de capture vidéo par USB
gint 2.5.28 Juin 2021
gint 2.5.12 Juin 2021
gint 2.5.026 Mai 2021Intégration de fxlibc (dépôt) — Tutoriel de communication par USB
gint 2.4.027 Avril 2021Api GINT_CALL() pour les callbacks
gint 2.3.12 Février 2021
gint 2.3.029 Janvier 2021
gint 2.2.112 Janvier 2021
gint 2.2.011 Janvier 2021
gint 2.1.116 Septembre 2020
gint 2.1.021 Août 2020Polices UnicodeNouvelle API du moteur de gris
gint 2.0.3-beta10 Juillet 2020Modifications de l'API timer
gint 2.0.2-beta17 Juin 2020
gint 2.0.1-beta1er Juin 2020

Anecdotes et bugs pétés

Ô amateurs de bas niveau, j'espère que vous ne tomberez pas dans les mêmes pièges que moi.


TODO list pour les prochaines versions (2023-04-03)

gint 2.11
  1. Changements de contextes CPU. À reprendre du prototype de threading de Yatis pour permettre l'implémentation d'un véritable ordonnanceur. Demandé par si pour faire du threading Java.
  2. Applications USB. Ajouter le support de descripteurs de fichiers USB. Potentiellement pousser jusqu'à avoir GDB pour debugger.
  3. Support de scanf() dans la fxlibc. Codé par SlyVTT, plus qu'à nettoyer et fusionner.

Non classé

  • Regarder du côté serial (plus facile que l'USB) pour la communication inter-calculatrices (multijoueur) et ultimement l'audio (libsnd de TSWilliamson).
  • Un système pour recompiler des add-ins mono sur la Graph 90+E avec une adaptation automatique.
  • Support des fichiers en RAM pour pouvoir utiliser l'API haut-niveau sur tous les modèles et éviter la lenteur de BFile à l'écriture quand on a assez de RAM.



Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 53, 54, 55, 56, 57, 58, 59, 60 ··· 70, 71, 72, 73 Suivante
Kikoodx Hors ligne Ancien labélisateur Points: 3011 Défis: 11 Message

Citer : Posté le 26/05/2021 14:36 | #


Tu donnes pas de contexte, c'est dur de t'aider. J'avais des erreurs similaires et supprimer build-{cg-fx} puis rebuild les a fait disparaitre.
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 26/05/2021 14:36 | #


<setjmp.h> n'est implémenté que côté Vhex pour l'instant. Regarde dans le fichier STATUS, tu auras une bonne idée de ce qui est disponible (le DONE).
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Loieducode Hors ligne Membre Points: 170 Défis: 6 Message

Citer : Posté le 26/05/2021 16:37 | #


Aussi, j'arrive pas a faire un malloc sans que le linker m'engeule pour des memset non definis
Trickswriting(sort le 1er avril):
   90%
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 26/05/2021 17:37 | #


Euh ça c'est pas normal par contre. Tu peux me montrer vite fait la commande de link (fxsdk build-{fx,cg} VERBOSE=1) et l'inclusion des libs dans le CMakeLists.txt ?
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Loieducode Hors ligne Membre Points: 170 Défis: 6 Message

Citer : Posté le 26/05/2021 17:45 | #


Voici les logs de la compilation(le VERBOSE=1 ne change rien de special)


Et je build avec Make(pas cmake)
Trickswriting(sort le 1er avril):
   90%
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 26/05/2021 17:52 | #


Tu n'as probablement pas ajouté les bons flags. Il te faut :

-I $(GCC_BASE_FX)/include/openlibm
-lgint-fx -lc -lopenlibm

Ou pareil en CG.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Loieducode Hors ligne Membre Points: 170 Défis: 6 Message

Citer : Posté le 26/05/2021 17:58 | #


A ba ca marche, merci!
Trickswriting(sort le 1er avril):
   90%
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 26/05/2021 21:28 | #


Nouvelle version : gint 2.5.0

Release associée du fxSDK : fxSDK 2.5.0

Voici une assez grosse release avec des nouveautés très concrètes pour vous et pas trop de changements !

Plusieurs de ces éléments sont gros, je vais ajouter ci-dessous un message pour expliquer la libc plus en détail et un autre pour l'utilisation du module USB.

Nouveautés importantes :
  • Les quelques fonctions standard fournies par gint sont en train d'être déplacées vers la fxlibc, ce qui prépare une montée en puissance vers un environnement beaucoup plus proche du hosted (comme Linux), et on l'espère, un support complet du C++ avec la STL (si tout se passe bien, pas de promesses !).
  • L'API d'écriture en USB est maintenant bien développée (et je l'espère stable). Une interface usb_ff_bulk a été ajoutée, et elle fournit des transferts en bulk calculatrice → PC. C'est asynchrone, donc vous pouvez lancer un transfert, aller faire autre chose, et être notifié par un GINT_CALL() quand c'est fini. Ou bien vous pouvez simplement faire les appels synchrones à la suite et ne pas vous embêter avec les complications.
  • Le mini-protocole de communication de fxlink a été implémenté sur l'interface usb_ff_bulk, ce qui permet notamment d'envoyer du texte et des screenshots avec très très peu d'efforts.
  • L'API DMA est maintenant proprement asynchrone (les fonctions ont été renommées de façon transparente). Concrètement, vous n'avez plus besoin de finir un transfert DMA avec dma_transfer_wait(), vous pouvez vous faire notifier par un GINT_CALL() lorsque le transfert se termine.
  • Une nouvelle fonction getkey_set_feature_function() a été ajoutée ; elle permet d'ajouter des raccourcis globaux à getkey(). Son fonctionnement est simple, vous indiquez simplement une fonction à appeler à chaque événement, et cette fonction a la possibilité d'inspecter l'événement et de l'intercepter s'il correspond à un raccourci global. Sinon elle peut le laisser passer.

Modifications :
  • La majorité des fonctions de dessin sur Graph 90+E supportent maintenant C_INVERT (avant, seule la Graph mono avait cette couleur).
  • intc_handler_function() permet maintenant d'enregistrer un GINT_CALL() comme gestionnaire d'interruptions (très pratique).
  • Une nouvelle fonction dgray_getscreen() permet d'accéder à la VRAM actuellement affichée à l'écran quand le moteur de gris est actif (au lieu de la VRAM dans laquelle on prépare le prochain frame) ; c'est utilisé pour les screenshots gris.
  • La méthode par laquelle dma_transfer_wait() évitait d'endormir le CPU quand la mémoire on-chip est impliquée dans le transfert (ce qui freezerait car endormir le CPU arrête la mémoire on-chip !) a été généralisée en une barrière sur sleep(). C'est parce que maintenant on peut aussi vouloir dormir durant un transfert USB asynchrone, et c'est plus simple de laisser chaque module désactiver sleep().
  • Ajouté le modifieur L pour %f (ie. %Lf), qui permet d'afficher des long double. Ça ne sert à rien parce que long double et double sont identiques sur la calculatrice, mais c'est nécessaire pour implémenter le standard (la libc).

Corrections de bugs :
  • J'ai corrigé le clipping dans drect_border(), qui déplaçait les bordures à l'intérieur de l'écran quand le rectangle dépassait de l'écran. (#14)
  • Compatibilité C++ : auto est utilisé à la place de __auto_type en C++ dans les macros utilitaires.
  • Corrigé le fait que getkey() n'indiquait plus .mod=1 sur les événements renvoyés à cause d'un oubli quand j'ai abstrait le traitement des événements clavier dans un autre driver.
  • Corrigé un freeze quand on affichait un double de valeur 0 avec %f (... oui >_>) du fait que Grisu2b ne produit pas le résultat attendu dans cette situation.
  • Corrigé les valeurs de C_LIGHT et C_DARK qui était fausses d'une façon hilarante sur Graph 90+E. (#16)

Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3011 Défis: 11 Message

Citer : Posté le 26/05/2021 21:51 | #


Énorme cette mise à jour ! Je testerai les screenshots et l'USB dès que possible, est-ce que tu as fait des tests en rafale pour une capture vidéo du pauvre ?

Je sais que c'est mineur, mais je vois déjà des utilisations pour C_INVERT : ça va être très pratique pour des effets puissants à faible coût

J'avais regardé un peu votre libc, où est-ce que vont les flux standards ? (stdin, stdout, etc.)

Tu as oublié l'enum BFile dans tes ajouts

Ajouté le 27/05/2021 à 10:49 :
Bonjour, j'ai tenté les screenshots hier et aujourd'hui avec peu de succès.
J'ai fait des modifications minimales dans ce commit pour être sûr que ce commit pour être sûr que cela ne vienne pas de mon code :
=> https://gitea.planet-casio.com/KikooDX/pcadmin/compare/97ab19...de94db

Sur mon ordinateur, j'ai essayé de lancer fxlink -iw :
avant le transfert
après le transfert
en utilisateur régulier et en super-administrateur
Dans tous les cas, le log de fxlink reste vide et la calculatrice freeze quand elle arrive au screenshot.

fxlink -l fonctionne comme attendu.

Tout est à jour, j'ai même forcé l'update du fxsdk sur le tag 2.5.0.

Merci pour votre attention
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 27/05/2021 10:57 | #


Aha, tu vas plus vite que le tutoriel. Tu n'as pas ouvert la connexion USB.

// Liste des interfaces à ouvrir (juste usb_ff_bulk)
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
// Mieux vaut ouvrir la connexion une seule fois
usb_open(interfaces, GINT_CALL_NULL);

// Si tu veux screen dès que la connexion est ouverte, tu
// peux tester usb_is_open() à chaque tour de boucle, ou te
// faire notifier avec le callback de usb_open()
if(key == KEY_F6 && usb_is_open()) {
  usb_fxlink_screenshot(true);
}

// À la fin du programme
usb_close();

Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3011 Défis: 11 Message

Citer : Posté le 27/05/2021 11:14 | # | Fichier joint


Wow merci, j'ai appliqué ces changements et ça fonctionne du feu de dieu ! Mon premier screenshot en pièce jointe


ouais ouais
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 27/05/2021 11:16 | #


Let's gooo
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3011 Défis: 11 Message

Citer : Posté le 27/05/2021 11:34 | # | Fichier joint


J'ai essayé de faire une vidéo avec la rafale sur un projet que je ne nommerai point, ça rame un peu et ça crash de manière aléatoire mais c'est une vidéo et je suis trop content !


ouais ouais
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 27/05/2021 11:36 | #


GG ! Ça crashe par contre ?! Tu me fais un peu peur, je testerai plus en détail.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3011 Défis: 11 Message

Citer : Posté le 27/05/2021 11:41 | #


En effet, pour être précis fxlink reçoit 1336 octets (au lieu de 2048) avant de s'arrêter correctement, tandis que la calculatrice freeze. Ça m'arrive quand je change de « scène » en jeu, ce qui ne devrait pas avoir d'impact étant donné que j'ai mis le code de screenshot dans ma fonction de raffraichissement.
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 27/05/2021 11:48 | #


Aha ! Un freeze n'est pas un crash (System ERROR ou redémarrage), et je préfère d'autant que ça freeze. Pas idéal toujours, mais je suis rassuré.

Le screenshot est synchrone donc il ne devrait pas y avoir de mauvaises surprises sauf peut-être si tu le lances immédiatement après dupdate() (?). Pour faciliter la reproduction du bug, si tu as moyen de pousser le code sur ton dépôt, je prends.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3011 Défis: 11 Message

Citer : Posté le 27/05/2021 11:50 | #


Je lance le screenshot immédiatement après dupdate, je vais pousser sur mon dépôt branche bordel dans deux minutes.

Et c'est ainsi que Projet Secret™ devint public.

Ajouté le 27/05/2021 à 11:52 :
https://git.sr.ht/~kikoodx/mtem/tree/bordel

J'espère vraiment que ça t'aidera :/

EDIT : je l'ai repassé en privé
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 27/05/2021 13:21 | #


Merci ! Mais je n'ai pas eu l'occasion de voir, j'étais parti manger. (En MP si tu as encore la patience ? Sinon laisse tomber.)

Ajouté le 27/05/2021 à 16:17 :
Je réalise que je n'ai pas répondu aux autres remarques !

Kikoodx a écrit :
J'avais regardé un peu votre libc, où est-ce que vont les flux standards ? (stdin, stdout, etc.)

Nulle part pour l'instant, et rien n'est décidé. Je ne suis pas particulièrement chaud pour avoir un terminal intégré à gint, ce n'est pas la philosophie. Mais je pense proposer des fonctions supplémentaires (au moins dans la version gint) pour les rediriger vers du code utilisateur, auquel cas on peut faire un terminal dans une lib ou dans une application, sans problème.

Tu as oublié l'enum BFile dans tes ajouts

Qui se sert des fonctions de recherche de BFile hmm?
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 28/05/2021 14:52 | #


Voilà les informations pour l'USB pour cette release. C'est un début de tutoriel pour quelque chose qui sera sans doute développé dans les tutoriels officiels plus tard.

Edit : Maintenant que je l'ai fini je peux clairement dire que c'est plus qu'un début lol.

1. Interfaces et classes
2. Ouvrir et fermer la connexion USB
3. Connecter la calculatrice à fxlink
4. Endpoints USB et types de communication
5. Obtenir les numéros de pipes
6. Écrire sur un pipe : méthodes synchrone et asynchrone
7. Options d'écriture
8. Protocole fxlink et API simplifiée
9. Résumé : faire des screenshots en deux temps trois appels

Tout ce qu'on fait ici c'est de l'USB 2. La spécification USB est très compliquée, mais je vais me concentrer sur les parties utiles.

1. Interfaces et classes

Un périphérique USB 2 expose des interfaces, qui représentent les fonctionnalités de l'objet. Par exemple, un ensemble scanner/imprimante peut avoir deux interfaces, une pour la fonction scanner et une pour la fonction imprimante. Chaque interface a une classe, qui indique le rôle standard de l'objet. La classe est un code d'un octet dont la signification est fixée par le consortium USB. On trouve par exemple...

  • Classe 02 (Communications and CDC control) : périphériques de communication série (entre autres).
  • Classe 03 (Human Interface Device) : claviers, souris, contrôleurs de jeu, etc.
  • Classe 07 (Printer) : imprimantes.
  • Classe 08 (Mass Storage) : clés USB, c'est ce que LINK utilise sur G35+EII et G90+E.
  • Classe 0e (Video) : webcams et autres périphériques d'acquisition vidéo.

La classe donne à l'hôte (l'ordinateur) une indication de comment communiquer avec le périphérique, et c'est grâce à ça que l'ordinateur décide de faire apparaître une clé USB dans l'explorateur de fichiers ou une webcam dans le lecteur vidéo.

Le driver USB de gint permet d'exposer une ou plusieurs interfaces à la fois (pourvu que les interfaces soient codées évidemment). Pour l'instant, la seule interface disponible est nommée ff_bulk et utilise le code de classe ff qui est une sorte de catégorie « autres » pour laquelle l'ordinateur ne fait rien (ce qui veut dire qu'on a besoin d'un logiciel fait maison pour communiquer avec ; j'y reviendrai). Et comme l'USB c'est compliqué, pour l'instant on ne peut que transmettre de la calculatrice au PC (mais ça viendra).

2. Ouvrir et fermer la connexion USB

La connexion USB est ouverte par la fonction usb_open() à laquelle on donne une liste d'interfaces à ouvrir, et un callback (optionnel) qui est exécuté une fois que la connexion est établie. Dans sa forme la plus simple, ça donne quelque chose comme ça :

#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>

// Liste des interfaces à ouvrir
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
usb_open(interfaces, GINT_CALL_NULL);

Faites bien attention au fait que usb_open() retourne même avant que la connexion ne soit ouverte ; le temps que ça prend dépend de ce que le PC fait donc c'est impossible de le prédire ; cela dit, en règle générale, ça prend 1 seconde environ.

Si vous voulez attendre que la connexion soit ouverte pour continuer le programme, vous pouvez utiliser le callback pour passer une variable à 1 et ensuite attendre que ça se produise.

#include <gint/cpu.h>

volatile int usb_has_opened = 0;
usb_open(interfaces, GINT_CALL_SET(&usb_has_opened));

while(usb_has_opened == 0) sleep();

(La fonction sleep() de <gint/cpu.h> permet au processeur de dormir pendant l'attente, ce qui évite de consommer des piles.)

Il y a aussi une fonction usb_is_open() qui indique si le port est ouvert, vous pouvez attendre avec.

#include <gint/cpu.h>

usb_open(interfaces, GINT_CALL_NULL);
while(!usb_is_open()) sleep();

Ouvrir la connexion USB notifie l'ordinateur que la calculatrice existe et initie les communications. Les interfaces offertes par gint ne peuvent plus changer jusqu'à ce que la connexion soit fermée. Généralement, je vous conseille de laisser la connexion ouverte si vous voulez communiquer fréquemment, plutôt que de la fermer et l'ouvrir toutes les quelques secondes (ce qui revient, du point de vue de l'ordinateur, à déconnecter et reconnecter le câble toutes les quelques secondes).

Quand vous avez fini, fermez la connexion avec usb_close() ; vous pouvez alors la rouvrir plus tard (y compris avec d'autres interfaces si ça vous chante).

usb_close();

3. Connecter la calculatrice à fxlink

fxlink est un outil du fxSDK qui permet de communiquer avec la calculatrice de deux façons :

  • Il permet de copier des fichiers sur les Graph 35+E II et Graph 90+E directement depuis le terminal sans interaction (en gros il monte et démonte les systèmes de fichiers) ;
  • Il permet de communiquer directement par USB avec l'interface usb_ff_bulk de gint.

Une fois le port ouvert, vous pouvez utiliser fxlink -l pour lister les calculatrices connectées. Dans mon cas, j'obtiens ma Graph 90+E qui donne ceci :

% fxlink -l
fx-CG or G-III series (USB Mass Storage) calculator
     Device location:  Bus 1, Port 2, Device 21
      Identification:  idVendor: 07cf, idProduct: 6102
  Guessed sysfs path:  /sys/bus/usb/devices/1-2/
       Serial number:  IGQcGRe9
          Properties:  mass_storage, serial_number=IGQcGRe9

Peu d'informations sont disponibles pour détecter le modèle exact de calculatrice, mais fxlink peut lister le numéro de série et distinguer les calculatrices qui utilisent FA-124 des modèles « clé USB ». Les informations sont résumées dans "Properties" (et si vous avez plusieurs calculatrices vous pouvez utiliser ces propriétés pour sélectionner celle avec laquelle vous voulez communiquer).

La communication avec l'interface usb_ff_bulk se fait avec le mode « interactif » de fxlink (-i), où fxlink se connecte à la calculatrice quand la connexion est ouverte, puis sauvegarde tout ce qui lui est envoyé jusqu'à ce qu'il soit arrêté ou que la calculatrice se déconnecte.

Si vous lancez fxlink -i, fxlink va chercher une calculatrice dont la connexion est déjà ouverte. Devoir lancer la calculatrice avant fxlink c'est casse-pieds, donc j'ai ajouté une option -w qui indique à fxlink d'attendre si aucune calculatrice est connectée. On peut donner un délai en secondes (-w5) ou bien pas de délai du tout, auquel cas l'attente est infinie (-w). De façon générale lancez le mode interactif avec fxlink -iw comme ça vous pouvez lancer fxlink et la calto dans n'importe quel ordre.

% fxlink -iw
Connected to 1:21, starting test.

4. Endpoints USB et types de communication

Chaque interface USB définit plusieurs endpoints qui représentent les canaux de transmission entre l'hôte et le périphérique. Un endpoint a plusieurs propriétés, mais celles qui nous intéressent sont la direction et le type de transfert.

La direction c'est facile, c'est soit calto → PC soit PC → calto. Pour l'instant, gint ne sait communiquer que dans le sens calto → PC.

USB 2 définit quatre types de transfert :

  • Les transferts de contrôle, utilisés durant la connexion entre le PC et la calto pour échanger des informations (notamment la calto dit au PC ce qu'elle a comme interfaces et endpoints, et indique sa classe).
  • Les transferts bulk (« transferts en gros ») que l'ordinateur effectue avec une priorité faible mais sans limite de débit tant qu'il n'y a rien d'autre à faire (utilisé pour les transferts avec les clé USB par exemple). Les transferts bulk sont initiés par le PC.
  • Les transferts par interruptions qui sont généralement courts, mais peuvent être initiés par le périphérique et ont une priorité élevée (utilisé par exemple par les claviers/souris).
  • Les transferts isochrones (littéralement « réguliers dans le temps ») pour lesquels la calto et le PC négocient une bande passante et font passer des gros volumes en temps réel (typiquement utilisés par les webcams et autres appareils vidéo).

Comme son nom l'indique, la class usb_ff_bulk fournit des transferts bulk. Actuellement elle a un unique endpoint, qui va dans le sens calto → PC.

(Vous noterez que dans un transfert bulk, c'est le PC qui initie les transferts, pas la calto. En principe je pense qu'il faudrait avec un endpoint interruption pour que la calto puisse notifier le PC quand elle veut faire un transfert. Mais pour l'instant fxlink se contente de demander des transferts en permanence ; c'est un hack qui sera corrigé dans le futur.)

5. Obtenir les numéros de pipes

Les parties suivantes décrivent l'API USB complète et détaillée. L'interface usb_ff_bulk propose une API très simplifiée pour communiquer directement avex fxlink. Vous pouvez sauter à cette partie si la suite vous paraît trop compliquée.

Dans gint, les interfaces déclarent les endpoints qu'elles veulent exposer, les paramètres pour les pipes, et la mémoire FIFO à réserver dans le module USB pour les transferts. Lorsque vous lancez usb_open(), le driver USB de gint consulte les besoins de toutes les interfaces, détermine si le module USB a assez de ressources (il n'y a que 16 endpoints et 16 kio de mémoire FIFO en tout), et si oui attribute des ressources à chaque interface avant de se connecter au PC.

Dans la calculatrice, chaque endpoint est associé à un pipe, qui est une partie du module USB avec laquelle on peut lire et écrire des données dans des endpoints. L'API gint utilise des numéros de pipes en paramètre un peu partout. La fonction usb_interface_pipe() permet de trouver le numéro de pipe associé à un endpoint d'une interface.

Par exemple, l'endpoint de communication bulk calto → PC de l'interface usb_ff_bulk a le numéro 0x81 (le bit 0x80 indique la direction), et donc on peut connaître le numéro de pipe associé comme ceci.

usb_interface_pipe(&usb_ff_bulk, 0x81); // = 5

Évidemment ce n'est pas top d'avoir à connaître la valeur magique 0x81, donc l'interface peut fournir à la place une fonction pour obtenir le numéro du pipe directement. Par exemple usb_ff_bulk_output() :

usb_ff_bulk_output(); // = 5

Voilà comment on obtient les numéros de pipes pour communiquer sur un endpoint choisi.

6. Écrire sur un pipe : méthodes synchrone et asynchrone

On arrive ici dans le coeur du jeu. Les écritures sur le pipe se font par séquences : vous écrivez un bloc de données, puis un autre, un troisième, etc. jusqu'à avoir fini, et ensuite vous validez le transfert. Les données sont envoyées au PC dans deux situations :

  1. Si au cours d'une écriture au milieu de la séquence le pipe se retrouve plein ;
  2. Lorsque vous validez, tout ce qui n'a pas encore été envoyé est envoyé.

La fonction la plus simple pour faire une écriture est usb_write_sync(). Elle écrite le bloc donné en paramètre dans le pipe ; si le pipe est plein elle transfère les données et attend que le transfert se termine, puis continue d'écrire jusqu'à ce que le bloc soit fini. Une fois le bloc fini elle n'envoie pas les contenus du pipe, parce qu'on part du principe que vous pouvez avez d'autres choses à écrire après (dans un autre bloc de mémoire), prolongeant la séquence.

Pour valider une série d'écritures, utilisez usb_commit_sync(), qui va envoyer tout ce qui a été écrit précédemment mais pas encore envoyé, et retournera quand ce sera fini.

/* Par exemple, envoyons du texte à l'ordinateur */
int pipe = usb_ff_bulk_output();
usb_write_sync(pipe, "Yeah!", 5, 1, false);
usb_write_sync(pipe, "Hello, World!", 14, 1, false);
usb_commit_sync(pipe);

Ces deux fonctions sont dites synchrones parce qu'elles synchronisent votre programme avec le transfert USB : le programme ne reprend qu'une fois le transfert fini. Et c'est parfois dommage parce qu'il y a plein de choses que votre programme pourrait faire au lieu de regarder le module USB communiquer avec l'ordinateur (ce qui n'occupe pas le processeur !).

gint implémente donc aussi l'option supérieure (mais plus compliquée) des transferts asynchrones. Pour faire une écriture aysnchrone, utilisez usb_write_async(). Le fonctionnement est le même que usb_write_sync(), mais le timing est différent. S'il y a beaucoup de données à écrire, usb_write_async() remplit le pipe et note dans une variable quelle partie du bloc n'a pas pu être écrite. Ensuite, elle retourne immédiatement, ce qui permet à votre programme de reprendre pendant que le transfert se fait. Les écritures et les transferts vont alors s'enchaîner en tâche de fond (par interruptions) pendant que votre programme est occupé à faire autre chose. En somme, usb_write_async() retourne presque immédiatement et fait tout en tâche de fond.

Pour vous permettre de savoir que l'écriture se finit, usb_write_async() a comme argument supplémentaire un callback qui est appelé une fois que le bloc est terminé. Il en va de même pour usb_commit_async(), qui lance le transfert de ce qui a été écrit dans le pipe mais pas envoyé, et retourne immédiatement pendant que le transfert se fait en tâche de fond. L'argument final de usb_commit_async() est un callback qui est invoqué une fois le transfert fini.

On peut se servir du callback pour continuer les opérations ; par exemple, on peut refaire le même transfert que tout à l'heure en asynchrone comme ceci :

void step_1(int pipe)
{
    usb_write_async(pipe, "Yeah!", 5, 1, false, GINT_CALL(step_2, pipe));
}
void step_2(int pipe)
{
    usb_write_async(pipe, "Hello, World!", 14, 1, false, GINT_CALL(step_3, pipe));
}
void step_3(int pipe)
{
    usb_commit_async(pipe, GINT_CALL_NULL);
}
step_1();

C'est plus verbeux mais aussi beaucoup, beaucoup plus puissant, car le programme peut continuer de faire des tâches pendant le transfert, qui ne coûte quasiment pas de CPU ; il coûte surtout de la bande passante sur la mémoire.

Pour des petits transferts comme ceux-ci (5 et 14 octets) surtout ne vous emmerdez pas, faites tout en synchrone. L'asynchrone est important pour les gros transferts du fait du temps économisé sur le CPU. Vous pouvez aussi mélanger les deux à volonté pour avoir moins de callacks à faire, notez juste que usb_write_async() et usb_commit_async() renvoient un code d'erreur si vous les appelez pendant qu'une écriture ou un commit est en cours (les versions synchrones attendent que l'opération en cours se termine et ne vous poseront jamais de problème).

Notez que les callbacks sont exécutés dans un gestionnaire d'interruption donc il y a des limitations ; en gros vous pouvez voir ça comme un autre thread. Les subtilités classiques de la programmation parallèle et asynchrone s'appliquent.

7. Options d'écriture

Si vous avez regardé les paramètres de usb_write_sync(), vous aurez remarqué deux paramètres unit_size et use_dma.

Le premier, unit_size, représente la taille des accès au pipe (1, 2 ou 4 octets). Si l'alignement du bloc et sa taille sont multiples de 4, vous pouvez mettre 4, sinon s'ils sont multiples de 2 vous pouvez mettre 2, sinon mettez 1. Pour les gros blocs c'est avantageux de mettre 4, parce que ça va plus vite. Si vous avez un doute mettez 1, ça marchera à tous les coups. Notez que vous devez utiliser la même taille unitaire pour toutes les écritures d'une séquence.

Le second, use_dma, indique s'il faut utiliser le DMA pour écrire dans le pipe (plutôt que le CPU). En termes de vitesse ça ne change rien car c'est le pipe le plus lent dans l'histoire (et le CPU et le DMA arrivent tous les deux à écrire assez vite pour le saturer). Mais si le DMA fait l'écriture le programme peut continuer de s'exécuter sur le CPU (ce qui vous permet dans le cas ultime d'avoir le CPU qui calcule, le DMA qui écrit dans le pipe, et le module USB qui transfère en même temps ! x3). Si vous faites de l'asynchrone et que vous avez du gros calcul à faire, n'hésitez pas à l'utiliser, c'est gratuit. Mais si c'est pour faire du dessin dans la VRAM vous allez juste gêner l'écriture, dans ce cas laissez l'écriture finir et dessinez ensuite.

8. Protocole fxlink et API simplifiée

fxlink écoute tout ce qui se dit sur l'endpoint bulk de la classe usb_ff_bulk, et supporte un petit protocole par lequel vous pouvez envoyer des messages. Pour ceux qui ont lu la version détaillée : c'est tout du synchrone.

Par exemple, la fonction usb_fxlink_text() enverra un message à fxlink contenant un en-tête (indiquant à fxlink que c'est du texte) et le texte à proprement parler (ce qui fait donc deux écritures et un commit). fxlink, voyant le texte arriver, l'affiche sur le terminal.

% fxlink -iw
Connected to 1:21, starting test.
New message (v1.0): application 'fxlink', type 'text', size 20 bytes
Successfully read 20 bytes
------------------
Hi! This is gintctl!
------------------

La ligne "New message" résume les informations inscrites dans l'en-tête. Le type "text" détermine ce que fxlink en fait.

La fonction usb_fxlink_screenshot() envoie un autre type de message ("image") avec une copie soit de la VRAM soit de ce qui est affiché à l'écran.

New message (v1.0): application 'fxlink', type 'image', size 177420 bytes
Successfully read 177420 bytes
Saved image (396x224, format=0) to './fxlink-image-2021.04.28-14h26-1.png'

Comme vous pouvez le voir, fxlink sauvegarde les images dans des fichiers (dans le dossier courant), avec un nom choisi en fonction de la date et de l'heure, et convertit tout en un joli PNG. C'est la méthode la plus simple pour faire un screenshot !

Pour les Graph mono utilisant le moteur de gris, usb_fxlink_screenshot_gray() fait pareil pour les screenshots en gris. La raison pour laquelle c'est séparé c'est que si vous l'utilisez le moteur de gris sera forcément dans l'add-in, et je voudrais pas que simplement prendre des screenshots d'un add-in noir et blanc vous force à avoir le moteur de gris (ce qui prendrait inutilement de la place).

Vous pouvez envoyer des messages personnalisés à fxlink, mais pas sauvagement comme je l'ai fait tout à l'heure avec mes "Yeah!" et autres "Hello, World!". Il faut inclure un en-tête sinon fxlink sera confus. Vous pouvez remplir un en-tête avec usb_fxlink_fill_header(), où vous devez spécifier le nom d'une application, le type de message, et la taille des données que vous allez envoyer (généralement en une séquence finie par un commit).

Le nom d'application "fxlink" est réservé et représente tous les types pour lesquels fxlink effectue un traitement particulier. Pour l'instant, tous les autres messages sont enregistrés dans des fichiers binaires, mais l'idée (dans le futur) c'est que si vous mettez un nom d'application X fxlink appelera un exécutable fxlink-message-X sur le PC (que vous fournissez) et comme ça vous pouvez récupérer les données dans un programme de votre choix (et les traiter, afficher, convertir, ouvrir dans un logiciel, etc).

Pour un exemple simple à copier, regardez usb_fxlink_text(), pour un exemple un peu plus compliqué regardez usb_fxlink_screenshot() qui a aussi un sous-en-tête donnant les dimensions et le format de l'image.

9. Résumé : faire des screenshots en deux temps trois appels

Au début de l'add-in, ouvrez la communication USB.

#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>

usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
usb_open(interfaces, GINT_CALL_NULL);

Au moment où vous voulez prendre un screenshot, utilisez usb_fxlink_screenshot(). La connexion doit être ouverte donc je vous conseille d'attendre une pression de touche (par exemple VARS).

if(key == KEY_VARS && usb_is_open()) {
  usb_fxlink_screenshot(true);
}

Sur le PC, lancez fxlink -iw et récupérez les images.

% fxlink -iw
Connected to 1:21, starting test.
New message (v1.0): application 'fxlink', type 'image', size 177420 bytes
Successfully read 177420 bytes
Saved image (396x224, format=0) to './fxlink-image-2021.04.28-14h26-1.png'

À la fin de l'add-in, fermez la connexion USB (fxlink se fermera tout seul ; vous pouvez aussi le couper avec Ctrl-C).

usb_close();

Si vous tentez de prendre un screenshot alors que fxlink n'est pas ouvert, le programme peut freeze. Dans ce cas lancez fxlink pour continuer l'exécution.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Potter360 Hors ligne Rédacteur Points: 1219 Défis: 2 Message

Citer : Posté le 28/05/2021 19:00 | #


Hey !
Trop bien tout ça !
J'ai mis à jour avec GiteaPC pour tester ca, cependant j'obtiens une erreur lors de la compilation : fatal error: gint/usb.h: No such file or directory

Voici mon code :
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>
int main(void)
{
        dclear(C_WHITE);
        dtext(1, 1, C_BLACK, "Sample fxSDK add-in.");
        dupdate();

        getkey();
        usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
        usb_open(interfaces, GINT_CALL_NULL);
        if(key == KEY_VARS && usb_is_open()) {
                  usb_fxlink_screenshot(true);
        }
        usb_close();
}


EDIT : autre chose, comment installer FXLink ?
Globalement, coder. Mal, mais coder.
Lephenixnoir En ligne Administrateur Points: 24146 Défis: 170 Message

Citer : Posté le 28/05/2021 20:40 | #


<gint/usb.h> est présent depuis gint 2.4.0 (ie. la release d'avant), et fxlink fait partie du fxSDK aussi depuis cette release, donc ta mise à jour n'a pas dû marcher.

Si tu n'es pas sur une branche, tu peux toujours faire

% giteapc install -u Lephenixnoir/fxsdk@master Lephenixnoir/gint@master

Au passage le code que j'ai donné dans le post ci-dessus n'est pas un copier/coller, il faut le lire et le comprendre - tu n'as même pas déclaré la variable key, ça ne risque pas de marcher.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 53, 54, 55, 56, 57, 58, 59, 60 ··· 70, 71, 72, 73 Suivante

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 105 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