vendredi 25 mai 2018

Chapitre 30 : Utilisation du système graphique X11


Dans les chapitres 13 à 17, nous avons vu comment utiliser le framebuffer pour afficher des dessins sur l’écrans du raspberry. Mais continuer à utiliser cette solution est terriblement couteuse en temps de développement car il faut tout gérer (dessin, police, couleurs etc.)
Pour pouvoir aller plus loin, il va falloir utiliser des librairies graphiques qui vont faire une grand part de notre travail. Mais ici au lieu d’utiliser des libraires comme openvg ou QT  , nous allons nous plonger dans la librairie de X11 car X11 est le système graphique de base de tous les Unix et donc de Linux et donc de raspbian. Si vous avez déjà entendu parler de X11, vous pensez que ce système graphique est très lourd à mettre en place et à programmer car il permet de créer des fenêtres, des boutons, des menus des dessins et il possède des dizaines de fonctions !!. C’est ce que nous allons voir dans les chapitres suivants de ce blog.
Tout d’abord nous allons mettre en place les outils nécessaires au développement avec X11 sur le raspberry.
Donc chargement de la librairie nécessaire :  sudo apt-get install libx11-dev
Puis adaptation du script de compilation pour appeler la librairie et dont voici un exemple :

#compilation assembleur avec librairie X11
#echo $0,$1
echo "Compilation de "$1".s"
as -o $1".o"   $1".s" -a >listingX11.txt
gcc  -o $1 $1".o" ~/vincent/asm/routinesARM.o -e main -lX11 -L/usr/lpp/X11/lib
echo "Fin de compilation."

(remarque : routinesARM.o contient les objets de mes propres routines vues dans les chapitres précédents).
En lisant la documentation sur X11, j’apprends que ce système est à base de client-serveur et qu’il possible d’afficher les écrans sur n’importe quel autre ordinateur disposant d’un serveur X11. Cela me convient car je pourrais travailler de mon ordinateur sous windows, de compiler et lancer les programmes sur le raspberry avec une connexion ssh  par putty et de voir les résultats sur l’écran du PC. Pour cela, j’installe le serveur gratuit XMing disponible à l’adresse https://sourceforge.net/projects/xming/
L’installation se passe sans problème. Si le serveur ne se lance pas, il faut l’activer en lançant le programme Xlaunch.exe se trouvant dans le répertoire d’installation sur votre PC (chosir les options fenêtes multiples puis laisser toutes les autres par défaut).
Maintenant il faut créer une connexion ssh par putty spéciale en configurant l’option enable X11 forwarding se trouvant dans le menu connexion sous-menu SSH :


Bien entendu, il faut renseigner l’adresse IP de votre raspberry et le port et sauvegarder la configuration sous un nom adéquat ( par exemple raspX11). Maintenant il ne vous reste plus qu’à ouvrir une session ssh avec cette configuration de saisir l’identifiant de connexion et le mot de passe.
Pour vérifier si cela fonctionne, vous lancer l’application graphique fournie en standard sur le raspberry : leafpad et normalement vous devez voir apparaitre sur votre ordinateur Window un petit éditeur de texte. Super non ?
Si vous aller dans le menu ouvrir, vous voyez que l’arborescence qui s’affiche est bien celle de votre raspberry et pas celle de votre ordinateur windows, preuve que l’application leafpad s’exécute sur le raspberry et que c’est seulement l’affichage (et le clavier et la souris) qui est géré par le serveur XMing sur votre PC.
Il faut aussi rechercher la documentation sur Internet concernant X11 (et il en a beaucoup !!). Un excellent document en anglais écrit pour le langage C par Ross Maloney nous servira de trame pour partir à la découverte des nombreuses fonctions des libraires X11. A télécharger sur le site : http://yenolam.com/writings/xlibbook-0.5.pdf
Et une référence sur les fonctions X11 : https://www.x.org/docs/X11/xlib.pdf
https://fr.wikipedia.org/wiki/X_Window_System
Et pour tester tout cela voici un premier petit programme qui va faire appel à la fonction XOpenDisplay pour établir une connexion avec le serveur d’affichage. Si la connexion est ok, nous récupérons un pointeur vers une structure appelée Display (Affichage) et qui contient les informations sur la connexion (plus loin dans le programme, nous afficherons le contenu de cette structure pour voir ce qu’elle contient). Ensuite nous nous contentons d’appel la fonction XBell en lui passant dans le registre r0 le pointeur du Display et dans r1 la valeur 100 (volume maximum). Cette fonction fait biper le haut-parleur de votre PC grâce au serveur XMing.  Puis nous fermons le Display par la fonction XCloseDisplay pour libérer les ressources utilisées et nous terminons le programme.
Vous remarquerez que les noms de fonctions commencent tous par X et que nous passons le pointeur du Display dans le registre r0 et tous les programmes commenceront par cet appel de connexion. Ce premier programme doit fonctionner sans problème !! Ah oui, n’oubliez pas d’activer votre haut-parleur sur votre PC pour entendre le son.

lundi 21 mai 2018

Chapitre 29 : chaines de caractères


Dans cet article, nous allons voir plusieurs routines très utiles pour traiter les chaines de caractères (string). Une chaine de caractères est toujours stockée en mémoire et est accessible par son adresse de début contenu dans un pointeur ou un registre. Il existe plusieurs manières de  gérer une chaine soit par exemple comme le C en terminant celle çi par un zéro binaire soit en mettant dans le premier octet (ou le demi mot ou le mot) sa longueur.
Ici nous utiliserons la définition comme le C cad avec un zéro final. Nous avons vu qu’une chaine se définissait en mémoire par la directive .asciz (ou aussi .string)qui mettra le zéro binaire automatiquement à la fin de la chaine. Pour des suites de caractères sans 0 final, il faut utiliser la directive .ascii.
Ces routines peuvent être utiliser tel quel mais elles serviront surtout de base pour écrire des fonctions plus intégrables ou répondant à d’autres besoins spécifiques. Par exemple le calcul de la longueur peut être fait directement dans le code soit avoir à appeler la routine ( pour alléger le nombre d’instructions et réduire le temps d’exécution).
La première fonction permet de vérifier et de corriger une chaine de caractère si celle ci n’est pas correcte. Elle force un zéro binaire à la rencontre du premier caractère non alphanumérique au sens strict. En effet, sur mon système les programmes sont encodés avec l’option uft8 et donc les caractères accentués ne sont pas considérés comme des caractères ascii mais sont codées sur 2 caractères (ou plus pour des alphabets exotiques). Donc suivant les cas, il faudra adapter cette fonction pour gérer vos besoins. Elle peut être utile pour la lecture des lignes d’un fichier car chaque ligne ne se termine pas par un 0 binaire mais par les caractères 0x0D0A.
La deuxième fonction permet de calculer la longueur d’une chaine. Il s’agit d’une boucle simple qui compte le nombre de caractères jusqu’à trouver le zéro final.
Ensuite nous trouvons les fonctions de copie d’une chaine entière et de copie de n caractères d’une chaine
Puis 2 fonctions de concaténation de chaines,  la première oblige la fourniture par le code appelant de la zone de réception . La seconde utilise le tas (heap) Linux pour réserver la zone de réception et retourne son adresse au code appelant.
De même 2 fonctions de comparaison de chaine, la première qui tient compte de la casse (majucules-minuscules) et l’autre qui n’en tient pas compte.
Puis 2 fonctions de recherche, une pour chercher un seul caractère et l’autre pour chercher une sous-chaine à l’intérieur d’une chaine. Ces fonctions s’arrêtent au premier caractère ou sous chaine trouvé et sont donc à adapter pour rechercher d’autres occurrences.
Enfin une fonction d’insertion d’une sous chaine dans une autre puis une fonction de tri. Pour cela toutes les chaines qui ont servies dans les tests précédents sont identifiées par un pointeur stocké dans une table. Cette table permet l’affichage des chaines puis leur tri et enfin un affichage après le tri pour vérification. Dans notre exemple nous appelons la fonction de comparaisonsanscasse mais vous pouvez la remplacer par l’autre fonction de comparaison. Le tri utilisé est un tri shell dans lequel seuls les pointeurs des chaines sont déplacés. Ainsi les chaines quelle que soit leur longueur ne sont pas déplacées ce qui permet au tri d’être efficace. Attention, ce tri utilise une gestion des incréments simplifiée ce qui peut entrainer pour des grosses quantités de chaine une dégénérescence du tri (pour plus de détails, voir la théorie et les multiples analyses du tri shell).
Pour terminer, une fonction permet d’éclater une chaine en plusieurs sous chaines en fonction d’un séparateur. La fonction retourne l’adresse d’une table qui contient dans le premier poste, le nombre de zones extraites puis ensuite dans chaque poste l’adresse de la sous-chaine. Dans ce programme, nous découpons la chaine avec un séparateur espace. Nous affichons le résultat dans une boucle qui balaye la table fournie en retour de la fonction.
La table des zones est aussi stockée sur le tas grâce à l’appel system linux Brk.

Exercice :   écrire une fonction qui inverse une chaine
                   améliorer la recherche d'un caractère pour trouver le 2ième, 3iéme etc.
 

lundi 7 mai 2018

Chapitre 28 : retour sur les algorithmes de division.


Le processeur arm de mon raspberry n’admet pas la division entière comme instruction de base et il faut donc la programmer. Le chapitre 15 du site Thinckingeek propose plusieurs algorithmes et il s’en trouve d’autres sur Internet et dans les livres sur les algorithmes. Dans ce chapitre je vais m’y intéresser en mesurant le temps nécessaire pour effectuer des milliers de divisions avec chacun d’entre eux.
Dans ce programme, nous décrivons dans la .data, le message nécessaire à l’affichage du temps et dans le .bss 2 tables de 10000 postes de 4 octets : la première contiendra le dividende sous forme de nombres aléatoires et la deuxième le diviseur aussi sous forme de nombres aléatoires. Lors de la création de cette dernière nous nous arrangerons pour que le diviseur soit toujours dans une tranche de nombres mille fois inférieur au dividende. Ces tables permettront d’effectuer des divisions de nombres aléatoires identiques pour tous les algorithmes testés.
Dans le code, nous commençons par générer les 10000 dividendes et les 10000 diviseurs aléatoires que nous stockons dans les 2 tables. Ensuite nous créons 2 sous routines pour enregistrer l’heure de départ d’un test (debutChrono) et l’heure de fin du test (stopChrono) pour calculer par différence le temps écoulé. Pour cela nous nous servons de l’appel system Linux gettimeofday (code 0x4E) pour enregistrer l’heure. La sous routine stopChrono affichera le temps en secondes et microsecondes.
Ensuite nous créons toutes les sous routines de division que nous voulons tester : celles du chapitre 15, une qui fait appel à la division en virgule flottante double précision du processeur, une tirée d’un livre sur l’assembleur ARM et 2 programmée par moi et issus de différents idées d’algorithmes trouvées sur Internet. Chaque routine commence par un test du diviseur pour vérifier qu’il soit différent de zéro. Dans ce cas, la routine retourne la valeur -1 dans le registre r0 car il est peu probable en division non signée d’avoir ce résultat. Mais il serait peut être préferable de positionner le carry à 1 dans ce cas (et penser à le mettre à 0 si la division est OK) ce qui coute quelques instructions de plus.
Pour chacune d’entres elles, nous effectuons les mêmes divisions de nombres aléatoires et nous affichons le temps mis.
Le premier algorithme est identique à la première division du chapitre 15 de http://thinkingeek.com/arm-assembler-raspberry-pi/. Après plusieurs séries de tests, il s’avère le plus lent de tous avec en moyenne 4 ms pour 10000 divisions.
Le second fait appel aux instructions en virgule flottante puisque la division est disponible !! Celle solution s’avère efficace puisque l’on tombe à 2ms. Remarque : j’avais fait des tests sur de grands dividendes et j’avais trouvé des écarts sur des résultats que je n’arrivais pas à expliquer. Et enfin j’ai trouvé une erreur dans l’instruction vcvt.f64.u32 d1, s1  car j’avais mis vcvt.f64.s32 d1, s1 car au début je pensais que le s de S32 voulait dire single pour simple précision alors qu’il veut dire signed. Donc il faut faire très attention lors du codage de ces instructions  et sans cesse tester et vérifier !!!
Le troisième reprend tel quel l’algorithme better_unsigned_division de thinkingeek Le résultat est le meilleur
 de tous car on descend à 1,4 ms. Reste à comprendre comment il fonctionne !!!
Le suivant donne aussi de bons résultats avec 1,8ms et les 2 autres sont moins efficaces mais restent dans la moyenne. 
Ces 2 derniers essaient de diminuer le nombre de boucles internes en évitant les zéros inutiles en début des nombres
 grâce à l’instruction clz. Dans ces programmes j’utilise l’instruction rsb bien utile . En effet avant pour enlever
 la valeur d’un registre d’une constante, je mettais la constante dans un premier registre puis je l’enlevais 
par l’instruction sub. Mais c’est plus simple avec rsb r1,r1,#32 soit r1 =  32 –r1.
 
Nous voyons donc ici différents manières de programmer la même routine et les incidences sur le temps d’exécution. 
Ces algorithmes sont à étudier de très près car ils montrent des utilisations diverses des instructions assembleurs.
Exercice :  essayer d’améliorer ces routines (piste : diminuer le nombre de boucles, ou diminuer le nombre
 d’instructions des boucles) 
                Vérifier que ces algorithmes sont exacts pour une large valeur de dividende et diviseur en stockant les résultats de chacun puis en les comparant pour détecter un éventuel écart.
                Adapter le programme pour effectuer des divisions signées.

vendredi 27 avril 2018

Chapitre 27 : Logique floue et GPIO du Raspberry


Le chapitre 2 du livre précédemment cité sur l’Intelligence Artificielle propose un exemple de logique floue. Cela m’a donnée l’idée de l’appliquer au GPIO du Raspberry. Pour cela, je voudrais gérer la luminosité d’une LED en fonction de la lumière reçue par 2 cellules photoélectriques.
C’est un projet complexe car outre l’adaptation de la logique floue à l’assembleur, il faut aussi arriver à récupérer les intensités lumineuses de chaque photo résistance puis moduler la luminosité de la LED au travers des instructions du GPIO.
Donc je vais le décomposer en 3 parties : un programmepour tester la récupération de la luminosité et connaitre l’échelle de variation possible. Un deuxième programme pour tester la variation de la lumière émise par la LED et là aussi connaitre l’échelle des valeurs possibles et enfin un 3ième programme pour gérer les ensembles et règlesflous.
Comme pour le programme sur le système expert, je vous renvoie pour les concepts théoriques au livre en question.
En premier, recherche sur Internet du montage pour lire les valeurs d’une photo résistance et d’un exemple de programme associé pour comprendre le principe. Le montage est assez simple. J’adapte le programme déjà écrit précédemment pour intégrer la lecture de l’état de la photo résistance.
Pour la Led, je recherche comment gérer sa luminosité. C’est moins simple car il faut alternativement l’allumer et l’éteindre avec des durées différentes. Et il va falloir donc trouver une solution pour garder la luminosité constante pendant un laps de temps jusqu’au calcul suivant.
Enfin je traduis en assembleur les routines données en Java dans le livre précédent. Pour simplifier la saisie, je développe toute une partie pour récupérer les variables d’entrées, de sortie et les règles dans un fichier de configuration. Les routines de gestion seront rassemblées dans un fichier objet qui sera lié à un programme maitre. Seul celui-là sera à modifié et à recompiler en fonction des valeurs d’entrées nécessaires à la résolution.
Les routines de gestion des ensembles floues sont assez compliquées. En plus, j’ai choisi d’utiliser des listes chainées pour gérer toutes les données nécessaires. Cela oblige d’utiliser un grand nombre de pointeurs ce qui ne facilite pas la lecture du programme. Puis c’est du brut de fonderie, je n’ai pas réfléchi à toutes les améliorations possibles.
Au début du programme, nous trouvons les descriptions des structures nécessaires : une pour les points, une pour les ensembles flous, une pour le contrôleur, une pour les variables et enfin la dernière pour les valeurs. Vous remarquerez qu’en plus des données propres, des pointeurs vers la structure suivante ont été ajoutés pour gérer les listes chainées.
Ensuite nous trouvons les descriptions des libellés nécessaires puis les réservations de place des variables. Peu de variables sont déclarées car toutes les données seront créées dans un tas et accessibles seulement par des pointeurs.Dans ce programme nous utiliserons le tas du système (heap) en récupérant l’adresse de début du tas par la fonction call system brk de Linux. Nous ajouterons notre taille nécessaire et nous fixerons la nouvelle taille du tas par le même call system brk. Cette solution permet de créer plusieurs contrôleurs avec des configurations différentes dans le même programme ( cas de figure non testé à ce jour).
La première routine FLOUcreationcontroleur concerne la création du contrôleur à partir du nom du fichier de configuration passé en paramètre dans le registre r0. Nous commençons par lire le fichier de configuration pour vérifier s’il existe bien. Puis nous créons dans le tas, le contrôleur à l’aide de la description de sa structure. Pour tout ce qui concerne les pointeurs de débuts de liste chainées, nous les initialisons avec une adresse du tas contenant une valeur sentinelle (cad que le pointeur contenant l’entité suivante contient en fait sa propre adresse). Nous aurions pu nous contenter de mettre une valeur nulle dans le pointeur mais cette solution permet de s’assurer que les listes sont correctement alimentées et terminées par une sentinelle de fin. Une valeur à zéro pouvant être aussi une erreur de programmation.
Puis nous passons à une sous routine d’analyse du fichier de configuration pour créer les variables d’entrées, et leurs valeurs associées, la variable de sortie et ses valeurs associés, et les règles de détermination. Cette programmation n’est pas le top et a été un peu améliorée par rapport au programme sur le petit système expert (voir chapitre 21). J’ai écrit une sous sous routine pour boucler sur les blancs présents dans le buffer de lecture et une autre pour analyser chaque mot, mettre un 0 binaire de fin de chaine et avancer jusqu’au prochain mot valide (ou la fin de fichier) en éliminant les blancs, le caractère « : » et les caractères de fin de ligne (soit 0D0A mais à vérifier suivant votre système et votre éditeur de texte adoré). Chaque variable, valeur ou règle est stockée dans la liste chainée adéquate du contrôleur.
Après mise à jour en mémoire du pointeur du tas, la routine retourne l’adresse du contrôleur au programme appelant.
Une autre routine concerne l’ajout de valeurs déterminant le problème à résoudre. Les paramètres en entree sont l’adresse du contrôleur, le nom de la variable concernée et la valeur associée. Si la variable n’est pas trouvée dans les variables d’entrée données par le fichier de configuration, un libellé d’erreur est affiché.
Puis nous trouvons la sous routine résoudre qui est l’exact reflet des routines décrites dans le livre de MME MATHIVET. Quelques petites différences quand même : pour éviter des calculs en virgule flottante, la valeur Y de chaque point est 100 et non pas 1. Ce qui entraine qu’un résultat de 77 par exemple correspond à un résultat de 0,77 du livre. Les sous routines semblent complexes, mais j’ai mis le maximum de commentaires pour expliquer les calculs et opérations réalisées. A ce jour, j’ai testé ces routines avec les données du livre, et il y a une petite différence dans les calculs pour un des cas. Malgré mes recherches, je n’ai pas trouvé l’erreur et je suspecte qu’une entrée du livre est incorrecte !!!
Enfin la dernière sous routine concerne la réinitialisation des données du problème pour effectuer un nouveau calcul. Et pour éviter tout accroissement du tas, cette routine remet l’adresse du tas à sa valeur après l’analyse du fichier de configuration.
J’ai ajouté d’autres sous routines pour afficher les variables d’entrées, de sortie et les règles, ce qui permet de vérifier la bonne prise en compte du fichier de configuration.
Après compilation, le fichier objet sera linké avec le programme appelant ainsi que le fichier des sousroutines de gestion du gpio

Ici dans notre cas, le programme appelant va appeler la routine de création du contrôleur en lui passant le nom du fichier de configuration récupère dans la ligne de commande comme déjà vu dans des chapitres précédents. Puis va appeler dans une boucle, la routine de lecture de la photorésistance 1 la routine de lecture de la photo résistance 2, passer les 2 valeurs au contrôleur par la sous routine FLOUajoutervaleur,, appeler la résolution, puis appeler la routine d’allumage de la LED avec le résultat précèdent en paramètre. Puis attendre un certain temps avant de boucler à nouveau après avoir réinitialiser l’état du contrôleur a son état après la prise en compte des règles.
Le jour où j’écris ces lignes, je ne sais pas encore si tout cela va fonctionner correctement !! Je pense que cela va nécessiter plusieurs ajustements tant au niveau des valeurs qu’au niveau des règles. Bon je vais déjà aller tester la partie GPIO avec les montages proposés.
Quelques jours après : après quelques erreurs de montage, les 2 premiers programmes ont bien fonctionné. J’ai pu obtenir les variations de la cellule photoélectrique ( de 5000 à 80000) et j’ai donc appliqué un facteur de réduction de 1000 pour rester dans une plage de valeur de 5 à 800. Pour le réglage de la luminosité de la LED, les réglages ont été plus délicats et ne sont pas encore tout à fait satisfaisants. Mais cela suffit pour effectuer un test final.
J’effectue soigneusement le montage des 2 cellules photorésistantes (attention au sens des condensateurs) l’ajout de la led (attention aussi à son sens), je modifie le programme principal pour que les pins du GPIO correspondent bien à ceux du programme (18 pour la LED, 4 et 22 pour les cellules) J’adapte le fichier de configuration pour déterminer les différentes valeurs et je crée quelques règles de génération de la luminosité (voir plus bas la syntaxe du fichier de configuration à respecter). Et cela fonctionne !!! En passant la main devant les cellules, la luminosité de la LED varie (imparfaitement mais tout est dans le fignolage des réglages).
Pour lancer le programme il faut taper :  pilotageLedFinal configFlGpio.txt

Et maintenant ?   restent à fignoler les règles et réglages, à réécrire les routines pour améliorer l’utilisation des registres et la lisibilité.

Syntaxe du fichier de configuration (fichier texte) :
1ère ligne : nom du contrôleur ( ne sert à rien !!!mais est obligatoire)
2ième ligne : libellé Entrées:     indique le début de la liste des variables d’entrées
Ligne Variable:nom_de_la variable:valeur mini:valeur maxi
Pour chaque variable plusieurs lignes valeurs
Valeur:nom_de_la valeur :type_ensemble_flou :valeur1 :valeur2 :valeurn :
Avec type d’ensemble flou : EFTG   : Ensemble Flou Trapèze Gauche suivi de 4 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
                                               EFTD   : Ensemble Flou Trapèze Droit suivi de 4 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
EFTRA   : Ensemble Flou TRApèze suivi de 5 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
EFTRI   : Ensemble Flou TRIangle suivi de 5 valeurs en ordre croissant dont la première est égale à la valeur mini et la dernière à la valeur maxi
Puis le libellé Sortie : avec une seule variable et ses valeurs comme une variable d’entrée.
Puis le libellé Règles :
Puis chaque règle codifiée comme suit
SI nom_variable_entrée EST nom_de la valeur [ET nom_variable_entrée EST nom_de la valeur…] ALORS nom_variable_sortie EST nom_d’une_valeur_de_sortie
Attention : les contrôles de cette syntaxe sont très légers !!! et donc vérifier bien l’affichage lors de l’execution pour contrôler la prise en compte.