Développer sur calculatrice est une activité passionnante et permet à de nombreux étudiants de mettre un pied dans le monde de la programmation. Très souvent le premier contact est réalisé via des langages de haut niveau directement intégrés aux machines, historiquement basés sur le langage BASIC, mais désormais très souvent via le langage PYTHON qui est intégré aux dernières générations de calculatrice de manière native.
Ces langages interprétés à l'exécution offrent comme principal avantage d'être relativement simples à approcher et ont souvent une courbe d'apprentissage adaptée aux débutants. Ils sont donc parfaits pour de la programmation simple dans le cadre scolaire ou pour développer de petits programmes rapidement. Un second avantage de ces langages est la mise à disposition d'un certain nombre de routines adaptées à la machine hôte afin de réaliser les opérations classiques dans un programme (vérifier si une touche est appuyée, créer un fichier et écrire dedans, lire les coordonnées de la souris ou du curseur, ...). Hélas, très souvent cette simplicité va de pair avec une relative lenteur du langage (car interprété, par opposition à compilé) et une certaine limitation dans les fonctions disponibles ou dans leur comportement. Apparaît donc très rapidement pour les plus passionnés une envie de plus d'ouverture pour des projets plus ambitieux tels que des jeux.
Une possibilité offerte sur un grand nombre de machines est de passer à la programmation dite "native", de plus bas niveau en langages compilés/assemblés tels que l'Assembleur (ou ASM) ou le C/C++. Ces langages offrent pour principaux avantages d'être particulièrement bien adaptés pour parler directement au matériel en présence. A cela s'ajouter qu'ils sont compilés, c'est à dire traduits directement en code interprétable par le processeur, sans de multiples couches logicielles intermédiaires, donnant très souvent un code nettement plus rapide et permettant de programmer à peu près tout ce que l'on veut, du moment que le matériel suit.
Ces langages interprétés à l'exécution offrent comme principal avantage d'être relativement simples à approcher et ont souvent une courbe d'apprentissage adaptée aux débutants. Ils sont donc parfaits pour de la programmation simple dans le cadre scolaire ou pour développer de petits programmes rapidement. Un second avantage de ces langages est la mise à disposition d'un certain nombre de routines adaptées à la machine hôte afin de réaliser les opérations classiques dans un programme (vérifier si une touche est appuyée, créer un fichier et écrire dedans, lire les coordonnées de la souris ou du curseur, ...). Hélas, très souvent cette simplicité va de pair avec une relative lenteur du langage (car interprété, par opposition à compilé) et une certaine limitation dans les fonctions disponibles ou dans leur comportement. Apparaît donc très rapidement pour les plus passionnés une envie de plus d'ouverture pour des projets plus ambitieux tels que des jeux.
Une possibilité offerte sur un grand nombre de machines est de passer à la programmation dite "native", de plus bas niveau en langages compilés/assemblés tels que l'Assembleur (ou ASM) ou le C/C++. Ces langages offrent pour principaux avantages d'être particulièrement bien adaptés pour parler directement au matériel en présence. A cela s'ajouter qu'ils sont compilés, c'est à dire traduits directement en code interprétable par le processeur, sans de multiples couches logicielles intermédiaires, donnant très souvent un code nettement plus rapide et permettant de programmer à peu près tout ce que l'on veut, du moment que le matériel suit.
Malheureusement, ces langages ont un côté rebutant pour la simple et bonne raison que pour réaliser les opérations simples décrites précédemment, il n'y a généralement rien de prévu (ou le strict minimum), et il faut donc développer tout un panel de "fonctions de base" pour créer un programme utilisable. C'est très souvent ce point qui limite la percée de ces langages très puissants. Bien entendu, une fois ces fonctions disponibles, elles seront réutilisables dans le futur dans d'autres programmes, mais l'effort initial à fournir est considéré comme conséquent et en décourage plus d'un.
Heureusement, il existe des bibliothèques de fonctions développées par des passionnés qui permettent de créer beaucoup plus rapidement de beaux programmes en s'affranchissant de la partie "mains dans le cambouis". Nous pouvons par exemple citer la bibliothèque SDL supportée par de nombreuses architectures (PC/MAC/PS3/TI nSpire ...) et dont le but est d'offrir un panel de fonctions permettant de créer des jeux et/ou des programmes divers et variés.
SDL est une magnifique librairie, permettant de faire de nombreuses choses, mais est parfois un peu vécue comme le marteau pour tuer la mouche. Avoir à disposition un panel réduit de fonctions permettant de réaliser seulement quelques opérations simples nécessaires à tout programme qui se respecte, sans fioritures, permettrait de combiner la simplicité d’un langage de plus haut niveau sans alourdir inutilement le poids de l’exécutable généré en incorporant tout un lot de fonctions dont seulement 1 ou 2% seront utilisées par le programme en cours de développement.
Posons-nous quelques instants et soyons plein de compassion pour ce jeune développeur voulant mettre à profit sa belle calculatrice flambant neuve et ne sachant que faire. Quelle solution pour lui ?
Et bien, jeune (ou moins jeune d’ailleurs) possesseur de TI nSpire, sache qu’en lisant les lignes suivantes, ton avenir de développeur en langage natif va se dégager de ces gros nuages noirs et que le soleil rayonnera rapidement sur toi …
En effet, dans le cadre du développement de la bibliothèque GUI Toolkit NF pour TI nSpire, un certain nombre de ces fonctions de base ont été codées afin de créer les fondations du Toolkit (NF signifiant d’ailleurs au passage « New Foundation »). Ces fonctions sont intégrées dans l’ensemble de la bibliothèque mais peuvent être utilisées indépendamment du GUI Toolkit NF.
Il a donc été choisi de créer une variante légère de cette bibliothèque (appelée nSpireLTE avec LTE pour LiTe Engine) et offrant les fonctions de base pour créer des petits programmes en C++.
Cette bibliothèque LTE se compose de 4 modules :
- KeyManagerLTE : contenant les fonctions de bas niveau permettant de lire l’état du clavier de la TI nSpire
- MouseManagerLTE : contenant les fonctions de bas niveau permettant de lire l’état du touchpad de la TI nSpire (mouvement du curseur, click central et gestion des flèches)
- TimeManagerLTE : permettant de gérer les fonctions liées au temps sur la nSpire (timer, sleep, heure actuelle)
- DebuggerLTE : permettant de debugger des programmes en offrant un panel de fonctions pour réaliser des sorties de logs dans un fichier et tracker impitoyablement les bugs et plantages.
Il a donc été choisi de créer une variante légère de cette bibliothèque (appelée nSpireLTE avec LTE pour LiTe Engine) et offrant les fonctions de base pour créer des petits programmes en C++.
Cette bibliothèque LTE se compose de 4 modules :
- KeyManagerLTE : contenant les fonctions de bas niveau permettant de lire l’état du clavier de la TI nSpire
- MouseManagerLTE : contenant les fonctions de bas niveau permettant de lire l’état du touchpad de la TI nSpire (mouvement du curseur, click central et gestion des flèches)
- TimeManagerLTE : permettant de gérer les fonctions liées au temps sur la nSpire (timer, sleep, heure actuelle)
- DebuggerLTE : permettant de debugger des programmes en offrant un panel de fonctions pour réaliser des sorties de logs dans un fichier et tracker impitoyablement les bugs et plantages.
1) Installation :
L’installation et l’utilisation est on ne peut plus simple, il suffit de télécharger l’archive et de décompresser le fichier nSpireLTE.zip dans votre projet de manière à avoir un répertoire nSpireLTE dans l’arborescence de votre projet de développement.
Ensuite, il suffit d’inclure la librairie dans votre fichier contenant la fonction main() via une directive :
- Code: Select all
#include "./nSpireLTE/nSpireLTE.hpp"
2) Les modules :
Comme précédemment annoncé, la nSpireLTE est composée de 4 modules dont le rôle est de couvrir 4 besoins très classiques en programmation. Chacun des modules sera détaillé par la suite.
Tous les modules fonctionnent de la même manière, on commence par les initialiser :
- Code: Select all
...
Module::Initialize();
...
puis on peut ensuite accéder à ses fonctions via :
- Code: Select all
...
Module::Fonction( parametres_fonctions ... );
...
Une fois l'utilisation du module terminée (généralement en fin de programme), on clos le module via:
- Code: Select all
...
Module::Close();
...
Vraiment facile à utiliser.
A) KeyManager
La gestion des événements liés au clavier est présent dans tous les programmes (ou presque) sur la TI nSpire. Il faut dire qu'avec toutes ces touches disponibles, il serait dommage de ne pas les utiliser.
Chaque touche possède un keycode selon l'image suivante
Le module KeyManager reprend à son compte la gestion de toutes les touches avec un marquage rouge. La nSpire étant une machine complexe, le module MouseManager (que nous détaillerons dans le prochain paragraphe) s'occupera quant à lui des touches marquées en bleu et en vert, toutes en rapport avec la zone du TouchPad.
Chaque touche possède 3 fonctions dédiées (par exemple pour la touche kbESC):
- KeyManager::kbESC() vaudra true (=1) si la touche est appuyée et false (=0) sinon.
- KeyManager::kbESC_Press_Event() vaudra true (=1) si la touche a été pressée depuis le dernier tour de KeyManager::Logic() et false (=0) sinon.
- KeyManager::kbESC_Release_Event() vaudra true (=1) si la touche a été relâchée depuis le dernier tour de KeyManager::Logic() et false (=0) sinon.
L'état du clavier est mis à jour via la fonction KeyManager::Logic() qu'il convient d'inclure dans la boucle de gestion des évènements.
A ces fonctions individuelles se rajoutent des fonctions plus globales :
- KeyManager::IsKeyPressEvent() vaudra true (=1) si une touche du clavier a été pressée depuis le dernier tour de KeyManager::Logic() et false (=0) sinon.
- KeyManager::IsAnyKeyPressed() vaudra true (=1) si une touche du clavier est pressée et false (=0) sinon.
Il est possible de réinitialiser l'état du driver avec un appel à KeyManager::ResetState();
Voici un exemple simple d'utilisation.
- Code: Select all
#include <stdlib.h>
#include "./nSpireLTE/nSpireLTE.hpp"
int main(int argc, char **argv)
{
KeyManager::Initialize();
bool done = false;
while (!done)
{
KeyManager::Logic();
if (KeyManager::kbCTRL() && KeyManager::kbESC())
{
done = true;
}
if (KeyManager::kb8_Press_Event())
{
printf( " La touche [8] a ete pressee.\n" );
}
if (KeyManager::kb8_Release_Event())
{
printf( " La touche [8] a ete relachee.\n" );
}
if (KeyManager::kbDOT_Press_Event())
{
printf( " La touche [.] a ete pressee.\n" );
}
if (KeyManager::kbDOT_Release_Event())
{
printf( " La touche [.] a ete relachee.\n" );
}
}
KeyManager::Close();
return 0;
}
B) MouseManager
Le module MouseManager est très similaire au module KeyManager.
Pour les touches avec les keycodes "bleus", l'utilisation est strictement identique, seul l'appel se fait via MouseManager::kb.. au lieu de KeyManager::kb..
Les événements sont du même type, par exemple pour la touche flèche droite :
- MouseManager::kbRIGHT() vaudra true (=1) si la touche est appuyée et false (=0) sinon.
- MouseManager::kbRIGHT_Press_Event() vaudra true (=1) si la touche a été pressée depuis le dernier tour de MouseManager::Logic() et false (=0) sinon.
- MouseManager::kbRIGHT_Release_Event() vaudra true (=1) si la touche a été relâchée depuis le dernier tour de MouseManager::Logic() et false (=0) sinon.
Mais le rôle de MouseManager est aussi de gérer le TouchPad lorsque il est utilisé comme une souris (c'est à dire en passant le doigt dessus et en cliquant la partie centrale) :
- MouseManager::GetX() retourne la position X du curseur de souris (sans gestion de sa représentation graphique, valeur entre 0 et 320 pixels)
- MouseManager::GetY() retourne la position Y du curseur de souris (sans gestion de sa représentation graphique, valeur entre 0 et 240 pixels)
- MouseManager::GetB() retourne l'état du bouton de souris (true si cliqué et false sinon)
Il est possible de gérer la sensibilité du TouchPad via la fonction MouseManager::SetSensibility( valeur ) avec valeur comprise entre entre 0.1 et 10.
10 étant très (très) rapide et 0.1 très (très) lent. 3 est une bonne valeur a priori.
Encore une fois rien de mieux qu'un exemple simple.
- Code: Select all
#include <stdlib.h>
#include "./nSpireLTE/nSpireLTE.hpp"
int main(int argc, char **argv)
{
KeyManager::Initialize();
MouseManager::Initialize();
bool done = false;
unsigned int MX;
unsigned int MY;
bool MB;
MouseManager::SetSensibility( 3 );
while (!done)
{
KeyManager::Logic();
MouseManager::Logic();
if (KeyManager::kbCTRL() && KeyManager::kbESC())
{
done = true;
}
MX = MouseManager::GetX();
MY = MouseManager::GetY();
MB = MouseManager::GetB();
if (MB)
{
printf( " Mouse position is (%d, %d) - Button Clicked.\n", MX, MY );
}
else
{
printf( " Mouse position is (%d, %d).\n", MX, MY );
}
}
KeyManager::Close();
MouseManager::Close();
return 0;
}
C) TimeManager
TimeManager est le module responsable de la gestion des timers et du temps.
Si vous voulez bloquer le programme, TimeManager::Delay( valeur ) vous permettra de bloquer durant valeur millisecondes.
Si vous voulez avoir l'heure courante, TimeManager::GetCurrentTime( ) vous donnera toutes les informations nécessaires.
Il est à noter que le module TimeManager ne demande pas d'appel cyclique à une fonction Logic() pour fonctionner.
- Code: Select all
#include <stdlib.h>
#include "./nSpireLTE/nSpireLTE.hpp"
int main(int argc, char **argv)
{
KeyManager::Initialize();
MouseManager::Initialize();
TimeManager::Initialize();
bool done = false;
int Heure, Minute, Seconde;
while (!done)
{
KeyManager::Logic();
MouseManager::Logic();
if (KeyManager::kbCTRL() && KeyManager::kbESC())
{
done = true;
}
TimeManager::GetCurrentTime( &Heure, &Minute, &Seconde );
if (KeyManager::IsKeyPressEvent() || MouseManager::IsKeyArrowPressEvent())
{
printf( " [Pause for the next 5s]\n" );
TimeManager::Delay( 5000 );
printf( " [OK, Done end of the 5s]\n" );
}
printf( " Hello : current time is %d:%d:%d\n", Heure, Minute, Seconde );
}
KeyManager::Close();
MouseManager::Close();
TimeManager::Close();
return 0;
}
D) Debugger
Le module Debugger est un peu particulier, mais tellement nécessaire. Programmer revient toujours à un moment où à un autre à chercher désespérément un bug et à essayer de comprendre pourquoi telle ou telle partie du code ne fonctionne pas comme prévu ou pis, fait crasher la machine.
Le module Debugger vise à proposer quelques routines pour aider à la résolution de ces soucis.
Le module Debugger permet de créer des fichiers dits de "Logs" afin de tracer les fonctions. Il est intimement lié au module TimeManager vu précédemment.
Par exemple, afin de savoir si un plantage se produit avant ou après une fonction, il suffit d'ajouter dans le code source :
- Code: Select all
...
Debugger::Log( "J'arrive juste avant la fonction à tester\n" );
fonction_a_tester();
Debugger::Log( "Je suis juste après la fonction à tester\n" );
...
Si dans le fichier de log vous avez bien les deux lignes inscrites, alors cette fonction n'est pas incriminée dans le plantage, sinon, il vous faudra analyser cette fonction plus finement, par exemple en y incorporant des lignes de logs supplémentaires, possiblement en y incluant des valeurs utiles.
Le module contient deux familles de fonctions:
- Debugger::Log( ...) : les fonctions de log sans horodatage
- Debugger::TimerLog( ...) : les fonctions de log avec horodatage permettant de garder une trace du temps écoulé depuis l'initialisation du module
Les fonctions se rapprochent des classiques "printf()" du C et permettent de multiples sorties de valeurs via les traditionnels "%d", "%l", "%s" et toutes les options de formatage de chaines.
Par défaut, le fichier log se nomme "DebugDefOut.txt.tns" et est situé à la racine de la nSpire, mais ce fichier peut être changé via un appel à la fonction : Debugger::SetDebuggerFile( nomdufichier )
Voici un exemple simple d'utilisation.
- Code: Select all
#include <stdlib.h>
#include "./nSpireLTE/nSpireLTE.hpp"
int main(int argc, char **argv)
{
KeyManager::Initialize();
TimeManager::Initialize();
Debugger::Initialize();
bool done = false;
int Heure, Minute, Seconde;
MouseManager::SetSensibility( 3 );
Debugger::Log( "Hello Dear Programmer\n" );
Debugger::TimerLog( "We will enter the Main Loop\n" );
Debugger::TimerLog( "and start logging the actions.\n" );
while (!done)
{
KeyManager::Logic();
MouseManager::Logic();
if (KeyManager::kbCTRL() && KeyManager::kbESC())
{
done = true;
Debugger::TimerLog( "CTRL+ESC pressed : ask for closure\n" );
}
if (KeyManager::kb7_Press_Event())
{
Debugger::TimerLog( "[7] pressed\n" );
}
if (KeyManager::kb8_Press_Event())
{
Debugger::TimerLog( "[8] pressed\n" );
}
if (KeyManager::kb9_Press_Event())
{
Debugger::TimerLog( "[9] pressed\n" );
}
if (KeyManager::kb7_Release_Event())
{
Debugger::TimerLog( "[7] released\n" );
}
if (KeyManager::kb8_Release_Event())
{
Debugger::TimerLog( "[8] released\n" );
}
if (KeyManager::kb9_Release_Event())
{
Debugger::TimerLog( "[9] released\n" );
}
}
Debugger::TimerLog( "We quit the Main Loop\n" );
Debugger::TimerLog( "and will return to the OS.\n" );
Debugger::Log( "Bye bye, and see you soon\n" );
KeyManager::Close();
TimeManager::Close();
Debugger::Close();
return 0;
}
3) Conclusion :
La librairie nSpireLTE vise à offrir les fonctions de base nécessaires à l'écriture et au débogage de programmes pour la nSpire. Ce court article vise à expliquer les grandes lignes d'utilisation de chacun des 4 modules de la librairie, mais n'est aucunement exhaustif. Chacun des modules contient des fonctionnalités supplémentaires. Nous ne pouvons que vous inviter à regarder dans les fichiers .hpp si vous êtes intéressés et à découvrir l'ensemble des fonctions de chacun des modules.
En vous souhaitant un bon code et plein de beaux programmes pour la nSpire.