Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.

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: 20317 Défis: 143 Message

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

Posté le 20/02/2015 17:30

Les SDKs classiques pour écrire des add-ins sont le fx-9860G SDK de Casio avec fxlib (pour Graph monochrome) et le PrizmSDK avec libfxcg (pour Prizm et Graph 90+E). Voici mon alternative : le fxSDK avec gint, pour toutes les plateformes.

Contrairement à fxlib et libfxcg, qui appellent les fonctions de l'OS pour faire leur travail, gint est un noyau indépendant de l'OS qui exploite seul le matériel et le met à disposition de votre add-in. Il vous offre plus de finesse sur le contrôle du matériel, notamment le clavier, l'écran et les horloges, de meilleurs performances sur le dessin, les drivers et la gestion de interruptions, et des choses entièrement nouvelles comme le moteur de gris.

Toutes les sources de gint sont publiques et accessibles sur la forge de Planète Casio :

» Dépôt Gitea Lephenixnoir/gint «

Voici plus précisément ce que gint vous offre de nouveau :

• Un contrôle détaillé du clavier pour les jeux, parfait pour les combos !
• Des timers avec une précision de 60 ns, d'autres à 30 µs
• Toutes vos images converties automatiquement sans code à copier (plus de Sprite Coder)
• Des polices personnalisées
• Des fonctions de dessin, d'images et de texte fulgurantes et optimisées la main
• Mesurer les performance de votre code à la microseconde près (avec libprof)
• Le contrôle du matériel et des interruptions
• Plein de petites choses pratiques comme dprint(1, 1, "x=%d", x)

• (Graph monochrome) Un moteur de gris pour faire des jeux en 4 couleurs !
• (Graph monochrome) La compatibilité SH3 et SH4, avec le même fichier g1a.

• (Graph 90+E) Une nouvelle police de texte, plus lisible et économe en espace
• (Graph 90+E) Le dessin en plein écran, sans les bordures blanches et la barre de statut !
• (Graph 90+E) Un driver écran capable de triple-buffering

Le coût de tout ceci, c'est que vous avez une copie du code de gint dans votre add-in. Cela prend environ 20 ko de place (selon la quantité de fonctions que vous utilisez), soit à peu près comme le sprintf() de fxlib qui fait 18 ko !

Et voici quelques photos et captures d'écran !





Tester gint sur votre machine

La fin du portage vers la Graph 90+E signera la sortie de gint v2. L'add-in de test de l'application est désormais gintctl :

» Dépôt Gitea Lephenixnoir/gintctl «

En plus de tester les fonctionnalités de gint, cet add-in contient quelques outils permettant d'inspecter la machine, la mémoire, et les registres. Je le développe au fur et à mesure, et je posterai un protocole de test complet avec la sortie de la v2 !

Utiliser gint pour développer des add-ins

Normalement, vous avez besoin du fxSDK pour développer avec gint. Le fxSDK est compatible avec Linux et Mac OS, et on peut réfléchir à un portage sous Windows s'il y a vraiment des intéressés. Il faut l'installer en premier (et avoir un cross-compilateur GCC).

La procédure de compilation et d'installation de gint est décrite sur le README du dépôt, c'est du configure - make tout à fait banal.

Une fois que gint est installé sur votre système, voyez les tutoriels de développement pour avoir un aperçu de son fonctionnement. La plupart des choses sont expliquées dans les en-têtes (fichiers .h) de la bibliothèque que vous pouvez consulter en ligne, sur votre copie locale du dépôt, ou dans les dossiers d'installation du compilateur.

Obtenir la dernière version de gint après une mise à jour

Je pousse régulièrement des mises à jour de gint sur le dépôt du projet. Pour les télécharger, tapez git pull, puis recompilez et réinstallez gint avec make et make install.

Changelog et infos de migration

Ci-dessous se trouve la liste des posts indiquant les nouvelles versions de gint, et les instructions pour modifier vos add-ins quand c'est nécessaire.

gint 2.4.0 (27 Avril 2021) — Api GINT_CALL() pour les callbacks
gint 2.3.1 (2 Février 2021)
gint 2.3.0 (29 Janvier 2021)
gint 2.2.1 (12 Janvier 2021)
gint 2.2.0 (11 Janvier 2021)
gint 2.1.1 (16 Septembre 2020)
gint 2.1.0 (21 Août 2020) — Polices UnicodeNouvelle API du moteur de gris
gint 2.0.3-beta (10 Juillet 2020) — Modifications de l'API timer
gint 2.0.2-beta (17 Juin 2020)


Anecdotes et bugs pétés

Ô amateurs de bas niveau, j'espère que vous ne ferez pas les mêmes erreurs que moi.

Toujours spécifier les flags dans .section en assembleur
Ne pas oublier des registres lors de la sauvegarde du contexte durant une interruption
Aligner correctement les adresses des sections dans le linker script
Toujours spécifier l'alignement des structures packed (message du 01/03/2017)

Fichier joint


Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 53, 54, 55, 56, 57, 58, 59, 60 Suivante
Kikoodx Hors ligne Labélisateur Points: 2665 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.
Je devrais dormir, et pourtant me voilà.
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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).
Loieducode En ligne Membre Points: 125 Défis: 0 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
J'ai beacoup trop de projets, nyohoho!
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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 ?
Loieducode En ligne Membre Points: 125 Défis: 0 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)
J'ai beacoup trop de projets, nyohoho!
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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.
Loieducode En ligne Membre Points: 125 Défis: 0 Message

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


A ba ca marche, merci!
J'ai beacoup trop de projets, nyohoho!
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 Message

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


Nouvelle version : gint 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)
Kikoodx Hors ligne Labélisateur Points: 2665 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
Je devrais dormir, et pourtant me voilà.
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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();
Kikoodx Hors ligne Labélisateur Points: 2665 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


Je devrais dormir, et pourtant me voilà.
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 Message

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


Let's gooo
Kikoodx Hors ligne Labélisateur Points: 2665 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 !


Je devrais dormir, et pourtant me voilà.
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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.
Kikoodx Hors ligne Labélisateur Points: 2665 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.
Je devrais dormir, et pourtant me voilà.
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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.
Kikoodx Hors ligne Labélisateur Points: 2665 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é
Je devrais dormir, et pourtant me voilà.
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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?
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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.

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();
Potter360 Hors ligne Rédacteur Points: 790 Défis: 0 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 ?
Hop là... toi qui lis cette signature... tu pourrais aussi aller voir mon projet Elphorina, un jeu de RPG-building !
Lephenixnoir En ligne Administrateur Points: 20317 Défis: 143 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.
Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 53, 54, 55, 56, 57, 58, 59, 60 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 v42 © créé par Neuronix et Muelsaco 2004 - 2021 | Il y a 60 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