La led dans tous ses états. Un petit complément à la saga blink La Saga Blink s’avère nécessaire pour les raisons suivantes :
– et nous, les utilisateurs de l’écosystème Arduino, on n’a pas droit au C++
– exploiter le partage des ressources utilisées par les applicatifs de la saga blink,
– saluer la « copine clignotante qui vient de nous rejoindre : Plongez dans le Go
Au sommaire :
- 1 Introduction
- 2 Base d’expérimentation
- 3 Langage C++
- 4 Langage C++ – Plus Classe
- 5 Conclusion saga blink
- 6 Les threads
- 7 Conclusion
- 8 Sources
Introduction
La présentation s’adresse, toujours, aux débutants expérimentés c’est à dire ceux qui disposent d’un Raspberry Pi opérationnel et qui n’ont pas peur d’utiliser la console pour l’élaboration des programmes. Les experts n’ont pas besoin de cette présentation et la trouveront certainement encore plus inutile que la première.
Par contre, leurs remarques et commentaires sont les bienvenus pour nous permettre d’évoluer.
Base d’expérimentation
Comme il s’agit d’un complément, pour l’instant, tout est identique au chapitre 2 de la « saga Blink ».
Langage C++
Que les experts du C++ me pardonnent car la suite n’est pas conforme au concept C++, mais notre but est de s’amuser et de prendre en mains notre framboise. Ce qui est important pour nous, c’est que cela « tombe en marche ».
Comme la structure de nos programmes C sont compatibles avec C++, il suffit de renommer les fichiers dans un premier temps. Puis, il faudra les recompiler.
Comment ! ; aurais-je fait du « C++ sans que j’en susse rien » (je n’ai pas résisté à la parodie du Bourgeois gentilhomme de Molière).
Une utilisation spécifique du raspberry Pi en C++ fera l’objet d’un autre chapitre.
Langage C++ (boucle infinie)
Voir chapitre 2.4 de la « saga blink ».
wiringPi
Programme
Il suffit de renommer le fichier blink80.c en blink80.cpp avec la commande si dessous :
mv blink80.c blink80.cpp
Exécution du programme
Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) :
g++ -Wall -o blink80 blink80.cpp -lwiringPi
Le résultat de la compilation est un fichier : blink80
Lancement du programme :
sudo ./blink80
Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.
Remarque : On aurait aussi pu utiliser gcc à la place de g++ car le compilateur fait la différence entre les fichier .c et les fichier .cpp (g++ est un alias de gcc).
perso
Programme
Il suffit de renommer le fichier blink50.c en blink50.cpp et ipsGPIO.c en ipsGPIO.cpp avec la commande si dessous :
mv blink50.c blink50.cpp mv ipsGPIO.c ipsGPIO.cpp
Exécution du programme
Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers C en créant des fichier objet :
g++ -Wall -c ipsGPIO.cpp g++ -Wall -c blink50.cpp
Et pour terminer, il faut créer le fichier final via l’éditeur de lien :
g++ -Wall -o blink50 blink50.o ipsGPIO.o
Le résultat de la compilation est un fichier : blink50
Lancement du programme :
sudo ./blink50
Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.
Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :
rm *.o
Langage C++ (boucle infinie avec sortie)
Voir chapitre 7 de la « saga blink ».
wiringPi
Pour ce programme, on utilise la même méthode pour sortir de la boucle infinie que pour pigpio. La saisie non bloquante d’un caractère clavier est incluse dans un fichier .h. Les experts du C vont voir tout rouge car on ne met pas de code dans un fichier .h, mais cela fonctionne et l’on fera mieux dans le prochain source.
Programme
Il suffit de renommer le fichier blink81.c en blink81.cpp avec la commande si dessous :
mv blink81.c blink81.cpp
Exécution du programme
Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) :
g++ -Wall -o blink81 blink81.cpp -lwiringPi
Le résultat de la compilation est un fichier : blink81
Lancement du programme :
sudo ./blink81
Pour sortir de la boucle infinie, il suffit de faire un ‘q’ ou ‘Q’ au clavier.
perso
Programme
Il suffit de renommer le fichier blink52.c en blink52.cpp, ipsGPIO.c en ipsGPIO.cpp et saisieCarac.c en saisieCarac.cpp avec la commande si dessous :
mv blink52.c blink52.cpp mv ipsGPIO.c ipsGPIO.cpp mv saisieCarac.c saisieCarac.cpp
Exécution du programme
Pour exécuter se programme, il faut d’abord le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers CPP en créant des fichier objet :
g++ -Wall -c ipsGPIO.cpp g++ -Wall -c saisieCarac.cpp g++ -Wall -c blink52.cpp
Et pour terminer, il faut créer le fichier final via l’éditeur de lien :
g++ -Wall -o blink52 blink52.o ipsGPIO.o saisieCarac.o
Le résultat de la compilation est un fichier : blink52
Lancement du programme :
sudo ./blink52
Pour sortir de la boucle infinie, il suffit de faire un Ctrl+c au clavier.
Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :
rm *.o
Langage C++ – Plus Classe
Pour la suite de la présentation, nous allons modifier notre cahier des charges et notre organigramme. Le câblage restera en tout point identique à celui du début de la saga. Cette modification nous permettra de mettre en évidence le temps de réaction du programme et la consommation de ressources.
Pour la programmation en C++ et la notion de class, vous trouverez de nombreux tutoriels et cours sur la toile ou dans des ouvrages spécialisés sur le sujet.
Cahier des charges et Organigramme
Notre nouveau cahier des charges se résume à celui du paragraphe 2.3 de la saga blink en y ajoutant une sortie de la boucle infernale.
1) si appuie touche clavier, si appuie alors | 1) assignation ports GPIO |
2) allumer led, | 2)arrêt programme |
3) attendre 1 seconde, | |
4) éteindre led, | |
5) attendre 1 seconde, | |
6) continuer en 1) |
Nous avons opté pour cet organigramme pour satisfaire les ardeurs des aficionados de la ressource cpu en incluant une attente bloquante. Dans la vraie vie, il faudra trouver le bon compromis entre réactivité/ressource.
Le première étape consiste à initialiser le port GPIO23 pour qu’elle puisse piloter la led.
L’étape suivante teste si on a appuyé sur une touche clavier (q ou Q). Si la condition est vérifiée, on libère la ressource et l’on quitte le programme.
Puis, on allume la led,
Étape suivante, attendre x secondes,
Après cette attente, on éteint la led,
Suivi d’une nouvelle attente,
Pour finalement recommencer le cycle.
Programme C++
Programme
Ce programme se compose d’un fichier blink15.cpp et de la bibliothèque saisieCarac pour la saisie clavier non bloquante. L’interface avec les ports GPIO est assurée par la bibliothèque wiringPi.
saisieCarac.h et saisieCarac.cpp
Pour saisir le programme saisieCarc.h, il faut faire dans la console :
nano saisieCarac.h
Cette commande ouvre un fichier de texte vide appelé saisieCarac.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme saisie non bloquante caractère au clavier Fichier entête de saisieCarac.cpp os : RPi Linux 4.4.13+ (Jessie) logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 21/07/2016 date de mise à jour : 21/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #ifndef SAISIE_CARAC_h #define SAISIE_CARAC_h /* -------------------------------------------------------------------------- */ //prototypes fonctions int kbhit(void); /* -------------------------------------------------------------------------- */ #endif
Pour saisir le programme saisieCarc.cpp, il faut faire dans la console :
nano saisieCarac.cpp
Cette commande ouvre un fichier de texte vide appelé saisieCarac.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme saisie non bloquante caractère au clavier os : RPi Linux 4.4.13+ logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 22/06/2016 date de mise à jour : 22/06/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Bibliothèques ----------------------------------------------------------------------------- */ #include <stdio.h> //bibliothèque standard #include <termios.h> //bibliothèque entrées/sorties terminal #include <unistd.h> //bibliothèque constantes symboliques #include <fcntl.h> //bibliothèque descripteur de fichier #include "saisieCarac.h" // /* -------------------------------------------------------------------------- */ int kbhit(void){ //fonction indiquant si frappe clavier struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); //sauve paramètres terminal newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); //nouveaux paramètres terminal oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); //lecture caractère tcsetattr(STDIN_FILENO, TCSANOW, &oldt); //restaure paramètres terminal fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF){ ungetc(ch, stdin); //replace le caractère dans le flux stdin //affiche le caractère dans la console return 1; //valeur de retour caractère saisie } return 0; //valeur de retour pas de caractère } /* -------------------------------------------------------------------------- */
blink15.cpp
Pour saisir le programme blink15.cpp, il faut faire dans la console :
nano blink15.cpp
Cette commande ouvre un fichier de texte vide appelé blink15.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique LED clignotante Led rouge sur GPIO23 (4) via une résistance de 330 ohms os : RPi Linux 4.4.13+ logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 28/07/2016 date de mise à jour : 28/07/2016 version : 1.0 auteur : icarePetibles référence : www.wiringpi.com Remarques : ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Bibliothèques ----------------------------------------------------------------------------- */ #include <stdio.h> //utilisé pour printf() #include <iostream> //pour endl #include "saisieCarac.h" //entête saisie non bloquante #include <wiringPi.h> //bibliothèque wiringPi //------------------------------------------------------------------------------ #define LED 4 //numéro led = GPIO23 #define DUREE 1000 //durée demi-période en millis-sec using namespace std; //espace de nommage int main(void){ //programme principale int c(0); cout << "Led clignotante" << endl; //IHM cout << "q ou Q pour quitter :"; //IHM wiringPiSetup(); //numérotation wiringPi ou "pseudo Arduino" digitalWrite(LED, LOW); //led éteinte pinMode(LED, OUTPUT); //pin en sortie for(;;){ //boucle infinie if(kbhit()) //si touche saisie c = getchar(); //lecture touche if(tolower(c) == 'q') //si q ou Q break; //sortie boucle digitalWrite(LED, HIGH); //allume led delay (DUREE); //attente DUREE digitalWrite(LED, LOW); //éteind led delay (DUREE); //attente DUREE } digitalWrite(LED, LOW); //éteint led pinMode(LED, INPUT); //pin en entrée cout << endl << "Fin du programme" << endl; //IHM return(0); //code sortie } //------------------------------------------------------------------------------
Exécution du programme
Pour exécuter se programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers C++ en créant les fichiers objets correspondants.
g++ -Wall -c saisieCarac.cpp g++ -Wall -c blink15.cpp
Et pour terminer, il faut créer le fichier final via l’éditeur de lien :
g++ -Wall -o blink15 blink15.o saisieCarac.o -lwiringPi
Le résultat de la compilation est un fichier : blink15
Lancement du programme :
sudo ./blink15
Pour sortir de la boucle, il suffit de faire q ou Q au clavier.
Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :
rm *.o
Commentaire
La sortie de la boucle infinie va être tributaire de l’instant où l’on entre le caractère q au clavier. En effet, si la saisie de q intervient lorsque le programme exécute la ligne 43, la prise en compte de l’ordre de sortie va intervenir quasiment de suite (temps proche de 0 seconde). Par contre, si la saisie se fait lors de l’exécution de la ligne 39, il faudra attendre le passage par la ligne 35 pour provoquer la sortie (soit environ 2 seconde plus tard). C’est d’ailleurs le principal inconvénient des attentes bloquantes sur les cartes embarquées.
Attendre 2 secondes parce que la température de la piscine est passée sous la température basse et qu’il faut chauffer l’eau n’est pas un problème. En effet, ce temps ne représente rien par rapport à l’inertie thermique de la piscine.
Attendre 2 secondes pour arrêter un dispositif mécanique en mouvement lorsqu’on actionne un fin de course est une autre affaire. Je vous fait grâce, des contraintes mécaniques, des déformations physiques, de la casse matériel et, des éventuelles, atteintes à l’intégrité physique des personnes.
Tout est possible, ce n’est qu’un problème de choix mais il faut le faire en toute connaissance de cause.
Programme C++ avec Class
Programme
Profitons de C++ pour créer une classe qui nous permettra d’instancier notre (nos) led (leds). Pour créer notre classe GPIOClassOut nous utiliserons les drivers Linux (utilisés dans de nombreux exemples précédents) pour accéder aux ports GPIO. Cet technique n’est pas la plus efficace mais elle permet de savoir ce que l’on fait.
Notre programme comporte un fichier blink05.cpp (programme principal) et de deux librairies saisieCarc.h et GPIOClassOut.h.
Comme d’habitude les fichiers saisieCarac.h et saisieCarc.cpp sont des fichiers C. Je sais, ce n’est pas bien mais je n’ai pas d’autres solutions pour l’instant.
Nota : Si l’un des lecteurs à une solution élégante pour saisir un caractère non bloquante au clavier en C++, je suis preneur.
saisieCarac.h et saisieCarac.cpp
Identique aux programmes du chapitre 4.2.1.1
GPIOClassOut.h et GPIOClassOut.cpp
Pour saisir le programme GPIOClassOut.h, il faut faire dans la console :
nano GPIOClassOut.h
Cette commande ouvre un fichier de texte vide appelé GPIOClassOut.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique LED clignotante Fichier entête de GPIOClassOut.cpp os : RPi Linux 4.4.13+ logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 21/07/2016 date de mise à jour : 21/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #ifndef GPIOClassOut_H #define GPIOClassOut_H class GPIOClassOut{ //classe GPIO en sortie public: //attributs et méthodes publics GPIOClassOut(); void begin(int broche); //initialise la sortie void on(); //met la sortie à 1 (3.3V) void off(); //met la sortie à 0 (0V) void close(); //ferme la / les sortie(s) void toggle(); //inverse état de la sortie private: //attributs et méthodes privés void export_gpio(); //méthode export void gpio_direction(); //méthode direction uniquement sortie //pour notre exemple void unexport_gpio(); //méthode unexport char read_gpio(); //lecture état port sortie int _broche; //numéro du port }; #endif
Le fichier entête contient les attributs (variables) et les méthodes (fonctions) de notre class GPIOClassOut.cpp. Certains attributs et certaines méthodes sont déclarés en « public » et elles sont accessibles à partir de notre programme blink.
Tout ce qui est déclaré en « private« est uniquement à usage interne de la Class.
Pour saisir le programme GPIOClassOut.cpp, il faut faire dans la console :
nano GPIOClassOut.cpp
Cette commande ouvre un fichier de texte vide appelé GPIOClassOut.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique LED clignotante Fichier bibliothèque class GPIOClassOut.cpp os : RPi Linux 4.4.13+ logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 21/07/2016 date de mise à jour : 21/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Biliothèques ----------------------------------------------------------------------------- */ #include "GPIOClassOut.h" //entête class #include <stdlib.h> //pour .c_str() #include <sstream> //pour stringstream #include <fstream> //pour la lecture fichier using namespace std; //espace de nommage /* -------------------------------------------------------------------------- */ /* Classe GPIOClassOut */ GPIOClassOut::GPIOClassOut(): _broche(0) //valeur par défaut { } /* Initialisation sortie */ void GPIOClassOut::begin(int broche){ _broche = broche; //copie dans variable privée if(_broche <= 0){ //si <= 0 _broche = 23; //valeur par défaut } export_gpio(); //méthode export gpio_direction(); //méthode direction } /* Exporte la sortie */ void GPIOClassOut::export_gpio(){ string commande; //variable commande à transmettre stringstream ss; //variable de conversion string broche; //numéro broche en string ss << _broche; //conversion de int en string, on aurait ss >> broche; //pu utiliser la méthode .to_string() //mais nécésitait une directive de //compilation supplémentaire commande = "echo " + broche + " > /sys/class/gpio/export"; //constitution de la commande à //transmettre system(commande.c_str()); //transmission de la commande linux } /* Assigne la sortie */ void GPIOClassOut::gpio_direction(){ string commande; //variable commande à transmettre stringstream ss; //variable de conversion string broche; //numéro broche en string ss << _broche; //conversion de int en string ss >> broche; //conversion de int en string commande = "echo out > /sys/class/gpio/gpio" + broche + "/direction"; //constitution de la commande à //transmettre system(commande.c_str()); //transmission de la commande linux } /* Libère la ressource */ void GPIOClassOut::unexport_gpio(){ string commande; //variable commande à transmettre stringstream ss; //variable de conversion string broche; //numéro broche en string ss << _broche; //conversion de int en string ss >> broche; //conversion de int en string commande = "echo " + broche + " > /sys/class/gpio/unexport"; //constitution de la commande à //transmettre system(commande.c_str()); //transmission de la commande linux } /* Inverse l'état de la sortie */ void GPIOClassOut::toggle(){ char etat = read_gpio(); //lecture état de la sortie if(etat == '1') //si état haut off(); //mettre à 0 par méthode off else //si état bas on(); //mettre à 1 par méthode on } /* Mise à 1 (3.3V) de la sortie */ void GPIOClassOut::on(){ string commande; //variable commande à transmettre stringstream ss; //variable de conversion string broche; //numéro broche en string ss << _broche; //conversion de int en string ss >> broche; //conversion de int en string commande = "echo 1 > /sys/class/gpio/gpio" + broche + "/value"; //constitution de la commande à //transmettre system(commande.c_str()); //transmission de la commande linux } /* Mise à 0 (0V) de la sortie */ void GPIOClassOut::off(){ string commande; //variable commande à transmettre stringstream ss; //variable de conversion string broche; //numéro broche en string ss << _broche; //conversion de int en string ss >> broche; //conversion de int en string commande = "echo 0 > /sys/class/gpio/gpio" + broche + "/value"; //constitution de la commande à //transmettre system(commande.c_str()); //transmission de la commande linux } /* Libère la ressource */ void GPIOClassOut::close(){ unexport_gpio(); //méthode unexport } /* Lit l'état de la sortie */ char GPIOClassOut::read_gpio(){ string nomFichier; //variable pour le nom fichier stringstream ss; //variable de conversion string broche; //numéro broche en string ss << _broche; //conversion de int en string ss >> broche; //conversion de int en string nomFichier = "/sys/class/gpio/gpio" + broche + "/value"; //constitution du chemin et nom fichier ifstream Fp(nomFichier.c_str(), ios::in); //ouverture fichier char valeur = 0; //valeur contenu fichier Fp.get(valeur); //lire et mettre dans valeur Fp.close(); //ferme le fichier return valeur; //valeur de retour } /* -------------------------------------------------------------------------- */
Normalement le programme sur-commenté devrait suffisant pour la compréhension.
blink05.cpp
Pour saisir le programme blink05.cpp, il faut faire dans la console :
nano blink05.cpp
Cette commande ouvre un fichier de texte vide appelé blink05.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique LED clignotante Led rouge sur GPIO23 via une résistance de 330 ohms Ajout sortie boucle infinie os : RPi Linux 4.4.13+ logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 29/07/2016 date de mise à jour : 29/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Bibliothèques ----------------------------------------------------------------------------- */ #include "GPIOClassOut.h" //entête class #include <iostream> //pour cout et endl #include <unistd.h> //pour usleep #include "saisieCarac.h" //entête saisie caractère non bloquant #include <stdio.h> //bibliothèque entrées/sorties #define DUREE 1000000 //durée demi-période en micro seconde using namespace std; //espace nommage /* -------------------------------------------------------------------------- */ int main(void){ int c; cout << "LED's clignotantes" << endl; cout << "'q' pour sortir" << endl; //IHM GPIOClassOut led; //instance led led.begin(23); //initialise led sur GPIO23 led.on(); //allume led for(;;){ //boucle infinie if(kbhit()) //si touche saisie c = getchar(); //lecture touche if(tolower(c) == 'q') //test si 'q' ou 'Q' break; //sortie boucle infernale led.toggle(); //permute état led usleep(DUREE); //attente pendant DUREE } led.close(); //libère la ressource cout << "\nFin du programme" << endl; return 0; //code sortie } /* -------------------------------------------------------------------------- */
Exécution du programme
Pour exécuter ce programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console) pour les différents fichiers C++ en créant les fichiers objets correspondants.
g++ -Wall -c saisieCarac.cpp g++ -Wall -c GPIOClassOut.cpp</span> g++ -Wall -c blink05.cpp
Et pour terminer, il faut créer le fichier final via l’éditeur de lien :
g++ -Wall -o blink05 blink05.o saisieCarac.o -GPIOClassOut
Le résultat de la compilation est un fichier : blink05
Lancement du programme :
sudo ./blink05
Pour sortir de la boucle, il suffit de faire q ou Q au clavier.
Remarque : On peut supprimer les fichiers objets (.o) du répertoire courant par :
rm *.o
Commentaire
La sortie de la boucle infinie va être tributaire de l’instant où l’on entre le caractère q au clavier. En effet, si la saisie de q intervient lorsque le programme exécute la ligne 42, la prise en compte de l’ordre de sortie va intervenir quasiment de suite (temps proche de 0 seconde). Par contre, si la saisie se fait lors de l’exécution de la ligne 40, il faudra attendre le passage par la ligne 36 pour provoquer la sortie (soit environ 1 seconde plus tard). Si l’on avait utilisé les méthodes on() et off() pour faire clignoter notre led, l’attente serait d’environ 2 secondes.
Conclusion saga blink
A force de triturer cette led qui clignote dans tous les sens, nous avons de plus en plus d’idées de mise en œuvre. L’utilisation de leds multicolores ou de leds intelligentes type WS2812b permettrait également de proposer des petits programmes intéressants pour les débutants. Mais le but n’est pas d’écrire un ouvrage de 458 pages sur la led clignotante.
Tout doit avoir une fin. Quoi que !
Les threads
Tous ceux qui veulent en savoir plus, peuvent consulter les différentes documentations du web, ouvrages spécialisés ou l’article de Bud Spencer : Penser ‘Thread’ pour simplifier vos programmes
Je ne vais pas vous faire un cours sur les threads, j’en suis parfaitement incapable. De formation charcutier/zingueur qui aspire à devenir pâtissier/couvreur, on ne peut pas tout assumer.
C’est quoi les threads ? Cela ne se mange pas, même s’ils sont difficiles à digérer. Pour faire simple, notre programme va créer un ou plusieurs « programmes » (threads ou fils ou tâches) qui vont s’exécuter de manière parallèle (en même temps ou quasiment en même temps) que notre programme. Pendant que le thread effectue une tâche plus ou moins bloquante, un autre thread peut assurer une surveillance, par exemple, des entrées ou clavier (voir figure).
Exemples de threads
Dans nos exemples nous n’utiliserons pas les notions d’échanges d’informations entre threads, de blocage, etc… Les mutex ou autres termes pharmaceutiques, tout droit issus du Codex, sont du domaine des spécialistes que nous ne sommes pas. Pour nous la framboise est un fruit ou un objet d’amusement.
Un thread
Pour faire des tests avec ce concept, nous avons opté pour une approche hors ports GPIO. Tous ces programmes pourront être testés sur le Raspberry Pi ou sur votre ordinateur de bureau.
Langage C
Programme
Pour saisir le programme thread01.c, il faut faire dans la console :
nano thread01.c
Cette commande ouvre un fichier de texte vide appelé thread01.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> //fonction thread void* fctThread(){ printf("<thread>\n"); while(1){ //boucle infinie printf("*"); //affiche des * fflush(stdout); //affichage immédiat usleep(500000); //attente 500 ms } return NULL; } //programme principal int main(){ pthread_t monThread; //instance thread printf("Début programme\n"); printf("Zone d'initialisation\n"); printf("\ntouche <Entrée> pour quitter !\n\n"); pthread_create(&monThread, NULL, fctThread, NULL); //création du thread getchar(); //attente saisie clavier pthread_cancel(monThread); //supprime le thread pthread_join(monThread, NULL); //attente fin thread printf("\nZone libération des ressources\n"); printf("Fin du programme\n"); return 0; }
La fonction lignes 18 à 25 est exécutée par un thread en parallèle du programme main().
On déclare une instance thread à la ligne 29, la création proprement dite du thread est faite ligne 33.
Ligne 35, notre programme main() attend une saisie au clavier.
La ligne 36 se charge de la destruction du thread et l’instruction de la ligne 37 attend la fin du cycle du thread.
Exécution du programme
Pour exécuter se programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console).
gcc -Wall -o thread01 thread01.c -lpthread
Le résultat de la compilation est un fichier : thread01
Lancement du programme :
./thread01
Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.
Résultat du programme
Vous devriez voir dans votre console un affichage semblable à celui ci :
Début programme Zone d'initialisation touche <Entrée> pour quitter ! <thread> ************************************************************************************************* Zone libération des ressources Fin du programme
La « Zone d’initialisation » correspond à l’emplacement où l’on peut initialiser les paramètres de nos ports GPIO.
La « Zone libération des ressources » correspond à l’emplacement où l’on libère les ressources de la carte.
<thread> : Message de démarrage du thread.
**********…….*********** : Le résultat de l’exécution du thread, peut être une led qui clignote.
Autre présentation
L’utilisation de la structure précédente fonctionne très bien mais il serait plus simple, d’un point de vue développement, de déporter le code de notre thread dans un fichier annexe.
Dans ce cas, notre programme thread01.c deviendrait un pseudo-ordonnanceur pour la led clignotante.
La nouvelle version se compose des fichiers thread02.c, monProg.h et monProg.c.
Pour saisir le programme monProg.h, il faut faire dans la console :
nano monProg.h
Cette commande ouvre un fichier de texte vide appelé monProg.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Fichier entête pour monProg.c os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #ifndef MON_PROG_H #define MON_PROG_H //prototypes void* fctThread(); #endif
Pour saisir le programme monProg.c, il faut faire dans la console :
nano monProg.c
Cette commande ouvre un fichier de texte vide appelé monProg.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Simulation programme raspberry Pi os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include "monProg.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> void* fctThread(){ while(1){ //boucle infinie // //On pourra mettre dans la boucle while notre programme raspberry Pi // printf("*"); //affiche des * fflush(stdout); //affichage immédiat usleep(500000); //attente 500 ms } return NULL; }
Pour saisir le programme thread02.c, il faut faire dans la console :
nano thread02.c
Cette commande ouvre un fichier de texte vide appelé thread02.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include "monProg.h" //programme principal int main(){ pthread_t monThread; //instance thread printf("Début programme\n"); printf("Zone d'initialisation\n"); printf("\ntouche <Entrée> pour quitter !\n\n"); pthread_create(&monThread, NULL, fctThread, NULL); //création du thread getchar(); //attente saisie clavier pthread_cancel(monThread); //supprime le thread pthread_join(monThread, NULL); //attente fin thread printf("\nZone libération des ressources\n"); printf("Fin du programme\n"); return 0; }
Pour l’exécution, il faudra faire :
gcc -Wall -o thread02 thread02.c monProg.c -lpthread
Le résultat sera un fichier : thread02
Lancement du programme :
./thread02
Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.
L’affichage dans la console sera, en tout point, identique au précédent exemple.
Langage C++
Programme
Notre programme est composé par trois fichiers : monProg.h, monProg.cpp et thread05.cpp.
Pour saisir le programme monProg.h, il faut faire dans la console :
nano monProg.h
Cette commande ouvre un fichier de texte vide appelé monProg.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Fichier entête pour monProg.cpp os : RPi Linux 4.4.13+ (Jessie) logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #ifndef MON_PROG_H #define MON_PROG_H //prototypes void* fctThread(void *); #endif
Pour saisir le programme monProg.cpp, il faut faire dans la console :
nano monProg.cpp
Cette commande ouvre un fichier de texte vide appelé monProg.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Simulation programme raspberry Pi os : RPi Linux 4.4.13+ (Jessie) logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include "monProg.h" #include <cstdio> #include <iostream> #include <unistd.h> using namespace std; void* fctThread(void *){ while(1){ //boucle infinie // //On pourra mettre dans la boucle while notre programme raspberry Pi // cout << "*" << flush; usleep(500000); //attente 500 ms } return NULL; }
Pour saisir le programme thread05.cpp, il faut faire dans la console :
nano thread05.cpp
Cette commande ouvre un fichier de texte vide appelé thread05.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads os : RPi Linux 4.4.13+ (Jessie) logiciel : g++ (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include <cstdio> #include <iostream> #include <unistd.h> #include <pthread.h> #include "monProg.h" using namespace std; //programme principal int main(){ pthread_t monThread; //instance thread cout << "Début programme" << endl; cout << "Zone d'initialisation" << endl; cout << endl << "touche <Entrée> pour quitter !" << endl << endl; pthread_create(&monThread, NULL, fctThread, NULL); //création du thread cin.ignore(); //attente saisie clavier pthread_cancel(monThread); //supprime le thread pthread_join(monThread, NULL); //attente fin thread cout << endl << "Zone libération des ressources" << endl; cout << "Fin du programme" << endl; return 0; }
Exécution du programme
Pour exécuter se programme, il faut au préalable le compiler. La compilation se fait par la commande ci-dessous (en mode console).
gcc -Wall -o thread05 thread05.cpp monProg.cpp -lpthread
Le résultat de la compilation est un fichier : thread05
Lancement du programme :
./thread05
Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.
Résultat du programme
Vous devriez voir dans votre console un affichage semblable à celui ci :
Début programme Zone d'initialisation touche <Entrée> pour quitter ! <thread> ************************************************************************************************* Zone libération des ressources Fin du programme
Remarque : On notera la similitude entre les programmes C et C++.
Python
L’exemple ci-dessous utilise la même structure de programme, c’est-à-dire un programme principal (main) qui se charge de lancer le thread et de la détruire lors d’une saisie clavier.
Programme
Pour saisir le programme monProg.py, il faut faire dans la console :
nano monProg.py
Cette commande ouvre un fichier de texte vide appelé monProg.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ Programme de test des threads Fonction d'affichage des * logiciel : python 3.4.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : """ import sys import time debut = True #variable globale def fctThread(): """affichage * dans la console""" global debut #transfaire de variable while debut: #affichage message au premier passage print("<thread>") #signal start thread debut = False sys.stdout.write("*") #affichage * sys.stdout.flush() #affichage immédiat time.sleep(0.5) #attente 500 msec
Pour saisir le programme thread50.py, il faut faire dans la console :
nano thread50.py
Cette commande ouvre un fichier de texte vide appelé thread50.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ Programme de test des threads logiciel : python 3.4.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : """ import sys import time from threading import Thread from monProg import * class Afficheur(Thread): """Lanceur de fonction""" def __init__(self): Thread.__init__(self) self.running = True self.debut = True def run(self): while self.running: fctThread() #notre fonction programme def stop(self): self.running = False if __name__ == '__main__': """Programme principal""" print("Début programme") print("Zone d'initialisation") print() print("touche <Entrée> pour quitter !") print() thread_1 = Afficheur() #création du thread thread_1.start() #lancement du thread input() #attente saisie clavier thread_1.stop() #détruit le thread thread_1.join() #attente fin cycle thread print() print("Zone libération des ressources") print("Fin du programme")
Exécution du programme
Pour exécuter se programme, il suffit de lancer la commande ci-dessous (en mode console).
python3 thread50.py
Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.
Résultat du programme
Début programme Zone d'initialisation touche <Entrée> pour quitter ! <thread> ************************************************************************************************* Zone libération des ressources Fin du programme
Remarque : On notera la similitude entre les programmes python, C et C++.
Et pour quelques threads de plus
Pour créer deux ou plusieurs threads, il suffit d’étendre ce que l’on a mis en œuvre dans le chapitre suivant. Par exemple, sous la forme :
instance thread_1 instance thread_2 création thread_1 création thread_2 lancement thread_1 lancement thread_2 attente saisie clavier destruction thread_1 destruction thread_2 attente fin thread_1 attente fin thread_2
Dans ce synoptique, le programme principal lance les deux threads (qui vivent leur vie) et attend une action de l’utilisateur pour sortie du programme.
Dans le cas de carte embarquée, la priorité n’est pas de terminer le programme mais de boucler indéfiniment pour assurer la fonction qui est la sienne. La structure adoptée au paragraphe précédent peut convenir à sortir proprement d’une ou plusieurs boucles sans fin à des fins de débogages.
Pour l’exploitation du raspberry Pi en tant que carte embarquée, il peut être préférable de créer au minimum deux threads. L’un des threads traiterait les actionneurs en sortie (plus en moins rapide) et l’autre l’acquisition des capteurs nécessitant un traitement plus rapide (réactivité, stabilité, sécurité, etc…).
Pour répondre à cette problématique, il nous faudrait une structure de la forme :
instance thread_1 instance thread_2 création thread_1 création thread_2 lancement thread_1 lancement thread_2 attente fin thread_1 attente fin thread_2
Notre programme principal instancie, créé et lance les threads puis attend que cela se passe (jusqu’à l’arrêt des deux threads). Bref, couché sur une plage de sable fin, les doigts de pieds en éventail et entrain de « siroter » une boisson fraîche.
Dans les exemples de ce chapitre, nous allons utiliser ce principe. Un des thread affichera nos petits astérisques (sortie) et l’autre attendra une saisie clavier (entrée).
Langage C
Programme
L’exemple se compose des fichiers thread10.c, monProg2.h et monProg2.c.
Pour saisir le programme thread10.c, il faut faire dans la console :
nano thread10.c
Cette commande ouvre un fichier de texte vide appelé thread10.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include "monProg2.h" volatile char leCarac = '\0'; //programme principal int main(){ pthread_t monThread_1, monThread_2; //instances thread printf("Début programme\n"); printf("Zone d'initialisation\n\n"); pthread_create(&monThread_1, NULL, sortie, NULL); //création du thread pthread_create(&monThread_2, NULL, entree, NULL); //création du thread pthread_join(monThread_1, NULL); //attente fin thread pthread_join(monThread_2, NULL); //attente fin thread printf("\nZone libération des ressources\n"); printf("Fin du programme\n"); return 0; }
A la ligne 19, on déclare une variable globale utilisée dans nos threads.
Ligne 23, on crée instances Threads. La création des threads se fait aux lignes 26 et 28 avec l’appel des fonctions à exécuter sortie() et entree().
Aux lignes 30 et 31, on attend la fin des threads.
Pour saisir le programme monProg2.h, il faut faire dans la console :
nano monProg.h
Cette commande ouvre un fichier de texte vide appelé monProg2.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Fichier entête pour monProg1.c os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #ifndef MON_PROG2_H #define MON_PROG2_H //prototypes void* sortie(); void* entree(); #endif
Pour saisir le programme monProg2.c, il faut faire dans la console :
nano monProg.c
Cette commande ouvre un fichier de texte vide appelé monProg2.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Simulation programme raspberry Pi os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #include "monProg2.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> extern volatile char leCarac; //variable globale //fonction sortie() affiche * void* sortie(){ while(leCarac != 'q'){ //tant que différent de q // //On pourra mettre dans la boucle while notre programme raspberry Pi // printf("*"); //affiche des * fflush(stdout); //affichage immédiat usleep(500000); //attente 500 ms } return NULL; } //fonction entrée saisie clavier void* entree(){ while(leCarac != 'q'){ //tant que différent de q // //On pourra mettre dans la boucle while notre programme raspberry Pi // printf("q pour quitter !\n"); leCarac = getchar(); //saisie clavier q+<Entrée> } return NULL; }
La fonction sortie() affiche nos petites astérisques tant que le leCarc est différent de ‘q’.
La fonction entree() attend une saisie clavier tant que leCarc est différent de ‘q’.
Exécution du programme
Pour l’exécution, il faudra compiler nos différents programmes par :
gcc -Wall -o thread10 thread10.c monProg2.c -lpthread
Le résultat sera un fichier : thread10
Lancement du programme :
./thread10
Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.
L’affichage dans la console sera similaire aux exemples précédents.
Résultat du programme
Vous devriez voir dans votre console un affichage semblable à celui ci :
Début programme Zone d'initialisation q pour quitter ! *********************************q** Zone libération des ressources Fin du programme
La « Zone d’initialisation » correspond à l’emplacement où l’on peut initialiser les paramètres de nos ports GPIO.
La « Zone libération des ressources » correspond à l’emplacement où l’on libère les ressources de la carte.
**********…….*********** : Le résultat de l’exécution du thread, pourquoi pas une led qui clignote.
Langage C++
Programme
L’exemple se compose des fichiers thread30.cpp, monProg2.h et monProg2.cpp.
Pour saisir le programme thread30.cpp, il faut faire dans la console :
nano thread30.cpp
Cette commande ouvre un fichier de texte vide appelé thread30.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #include <cstdio> #include <iostream> #include <unistd.h> #include <pthread.h> #include "monProg2.h" using namespace std; volatile char leCarac = '\0'; //variable globale pour les threads //programme principal int main(){ pthread_t monThread_1, monThread_2; //instances thread cout << "Début programme" << endl; cout << "Zone d'initialisation" << endl << endl; pthread_create(&monThread_1, NULL, sortie, NULL); //création du thread pthread_create(&monThread_2, NULL, entree, NULL); //création du thread pthread_join(monThread_1, NULL); //attente fin thread pthread_join(monThread_2, NULL); //attente fin thread cout << endl << "Zone libération des ressources" << endl; cout << "Fin du programme" << endl; return 0; }
Notre programme est, en tout point, conforme à celui de l’exemple précédent hormis les syntaxes pour l’affichage console.
Pour saisir le programme monProg2.h, il faut faire dans la console :
nano monProg2.h
Cette commande ouvre un fichier de texte vide appelé monProg2.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Fichier entête pour monProg1.cpp os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #ifndef MON_PROG2_H #define MON_PROG2_H //prototypes void* sortie(void *); void* entree(void *); #endif
Pour saisir le programme monProg2.cpp, il faut faire dans la console :
nano monProg2.cpp
Cette commande ouvre un fichier de texte vide appelé monProg2.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme de test des threads Simulation programme raspberry Pi os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #include <cstdio> #include <unistd.h> #include <iostream> #include "monProg2.h" using namespace std; extern volatile char leCarac; //variable globale :( //fonction sortie() affiche * void* sortie(void *){ while(leCarac != 'q'){ //tant que différent de q // //On pourra mettre dans la boucle while notre programme raspberry Pi // cout << "*" << flush; //affiche immédiatement les * usleep(500000); //attente 500 ms } return NULL; } //fonction entrée saisie clavier void* entree(void *){ while(leCarac != 'q'){ //tant que différent de q // //On pourra mettre dans la boucle while notre programme raspberry Pi // cout << "q pour quitter !" << endl; leCarac = getchar(); //saisie clavier q+<Entrée> } return NULL; }
Les commentaires du programme et ceux des exemples précédents sont suffisants à la compréhension.
Exécution du programme
Pour l’exécution, il faudra compiler nos différents programmes par :
g++ -Wall -o thread30 thread30.cpp monProg2.cpp -lpthread
Le résultat sera un fichier : thread30
Lancement du programme :
./thread30
Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.
L’affichage dans la console sera similaire aux exemples précédents.
Résultat du programme
Vous devriez voir dans votre console un affichage semblable à celui ci :
Début programme Zone d'initialisation q pour quitter ! *********************************q** Zone libération des ressources Fin du programme
Remarque : Les puristes C++ auraient utilisés la bibliothèque thread à la place de thread.h et chrono à la place unistd.h. Mais je ne suis pas à un blasphème près.
Langage Python
Programme
Notre programme se compose de deux fichiers thread70.py et monProg2.py.
Pour saisir le programme monProg2.py, il faut faire dans la console :
nano monProg2.py
Cette commande ouvre un fichier de texte vide appelé monProg2.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ Programme de test des threads Fonctions d'affichage des * et saisie clavier logiciel : python 3.4.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : """ import sys import time from thread70 import leCarac #importe la variable globale def sortie(): """affichage * dans la console""" global leCarac while(leCarac != 'q'): #boucle tant que leCarac différent de q # #on pourra mettre dans la boucle notre programme raspberry Pi # sys.stdout.write("*") #affiche * sys.stdout.flush() #affichage immédiat time.sleep(0.5) #attente 500 msec def entree(): """saisie caractère au clavier""" global leCarac while(leCarac != 'q'): #boucle tant que leCarac différent de q # #on pourra mettre dans la boucle notre programme raspberry Pi # leCarac = input() #saisie caractère
Ce fichier contient les deux fonctions exécutées par les threads.
Pour saisir le programme thread70.py, il faut faire dans la console :
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ Programme de test des threads logiciel : python 3.4.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : """ import sys import time from threading import Thread from monProg2 import * leCarac = '\0' #variables globale class MonThread(Thread): """Lanceur de fonction""" def __init__(self, fonction): Thread.__init__(self, None, fonction) self._fonction = fonction self.running = True def run(self): while self.running: self._fonction() #notre fonction programme def stop(self): self.running = False if __name__ == '__main__': """Programme principal""" print("Début programme") print("Zone d'initialisation") print() print("q pour quitter !") print() thread_1 = MonThread(sortie) #création du thread thread_2 = MonThread(entree) thread_1.start() #lancement du thread thread_2.start() thread_1.stop() #détruit le thread thread_2.stop() # thread_1.join() #attente fin cycle thread thread_2.join() print() print("Zone libération des ressources") print("Fin du programme")
Lignes 22 à 34, on retrouve notre classe qui permet de créer, lancer et arrêter nos threads.
Exécution du programme
Pour exécuter se programme, il suffit de lancer la commande ci-dessous (en mode console).
python3 thread70.py
Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.
Résultat du programme
Vous devriez voir dans votre console un affichage semblable à celui ci :
Début programme Zone d'initialisation q pour quitter ! *********************************q** Zone libération des ressources Fin du programme
Led clignotante et threads
Revenons à nos moutons ! Après cet interlude sur ma vision des threads, nous allons les mettre en pratique.
Langage C
Pour cet exemple, nous allons utiliser un thread pour commander notre led et sortir de la boucle infinie par la touche <Entrée> du clavier.
Programme
Le programme est composé des fichier thread20.c, monProg3.h et monProg3.c.
Pour saisir le programme thread20.c, il faut faire dans la console :
nano thread20.c
Cette commande ouvre un fichier de texte vide appelé thread20.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique led clignotante Led rouge sur GPIO23 (4) via une résistance de 330 ohms os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include "monProg3.h" #include <wiringPi.h> //programme principal int main(){ pthread_t monThread; //instance thread printf("Led clignotante\n"); //initialisation port wiringPiSetup(); //pseudo-Arduino pinMode(LED, OUTPUT); //broche en sortie //fin initialisation port printf("\ntouche <Entrée> pour quitter !\n"); pthread_create(&monThread, NULL, sortie, NULL); //création du thread getchar(); //attente saisie clavier pthread_cancel(monThread); //supprime le thread pthread_join(monThread, NULL); //attente fin thread //libération ressources digitalWrite(LED, LOW); //éteint led pinMode(LED, INPUT); //broche en entrée //fin libération ressources printf("Fin du programme\n"); return 0; }
L’initialisation se fait lignes 26 et 27, et la libération de ressources se fait lignes 36 et 37. Le reste du programme est conforme à la structure du paragraphe 6.1.1.1.4.
La directive LED est déclarée dans le fichier monProg3.h.
Pour saisir le programme monProg3.h, il faut faire dans la console :
nano monProg3.h
Cette commande ouvre un fichier de texte vide appelé monProg3.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique led clignotante Fichier entête pour monProg3.c os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #ifndef MON_PROG3_H #define MON_PROG3_H //directives #define LED 4 //numéro port led=GPIO23 #define DUREE 500 //demi-période 500 ms //prototypes void* sortie(); //led clignotante #endif
Pour saisir le programme monProg3.c, il faut faire dans la console :
nano monProg3.c
Cette commande ouvre un fichier de texte vide appelé monProg3.c (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique led clignotante Fonction sortie() os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Bud Spencer Remarques : ----------------------------------------------------------------------------- */ #include "monProg3.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <wiringPi.h> void* sortie(){ while(1){ //boucle infinie // //On pourra mettre dans la boucle while notre programme raspberry Pi // digitalWrite(LED, HIGH); //allume led delay(DUREE); //attente digitalWrite(LED, LOW); //éteint led delay(DUREE); //attente } return NULL; }
On retrouve ci-dessus que des choses déjà vus.
Exécution du programme
Pour l’exécution, il faudra faire :
gcc -Wall -o thread20 thread20.c monProg3.c -lpthread -lwiringPi
Le résultat sera un fichier : thread20
Lancement du programme :
sudo ./thread20
Pour sortir de la boucle, il suffit de faire <Entrée> au clavier.
Miracle, notre led est toujours en vie et clignote dans toute sa spendeur.
Langage C++
Pour cet exemple, nous allons utiliser deux thread pour commander notre led et sortir de la boucle infinie par la touche q+<Entrée> du clavier.
Programme
Le programme est composé des fichiers thread35.cpp, monProg3.h et monProg3.cpp.
Pour saisir le programme thread35.cpp, il faut faire dans la console :
nano thread35.cpp
Cette commande ouvre un fichier de texte vide appelé thread35.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique led clignotante Led rouge sur GPIO23 (4) via une résistance 330 ohms os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #include <cstdio> #include <iostream> #include <unistd.h> #include <pthread.h> #include "monProg3.h" #include <wiringPi.h> using namespace std; volatile char leCarac = '\0'; //variable globale pour les threads //programme principal int main(){ pthread_t monThread_1, monThread_2; //instances thread cout << "Led clignotante" << endl << endl; //initialisation wiringPiSetup(); //pseudo-Arduino pinMode(LED, OUTPUT); //en mode sortie //fin initialisation pthread_create(&monThread_1, NULL, sortie, NULL); //création du thread pthread_create(&monThread_2, NULL, entree, NULL); //création du thread pthread_join(monThread_1, NULL); //attente fin thread pthread_join(monThread_2, NULL); //attente fin thread //libération ressources digitalWrite(LED, LOW); //éteint led pinMode(LED, INPUT); //broche en entrée cout << "Fin du programme" << endl; return 0; }
Pour saisir le programme monProg3.h, il faut faire dans la console :
nano monProg3.h
Cette commande ouvre un fichier de texte vide appelé monProg3.h (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique led clignotante Fichier entête pour monProg3.cpp os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #ifndef MON_PROG3_H #define MON_PROG3_H //directives #define LED 4 //numéro port led=GPIO23 #define DUREE 500 //demi-période en ms //prototypes void* sortie(void *); //led clignotante void* entree(void *); //saisie clacvier #endif
Pour saisir le programme monProg3.cpp, il faut faire dans la console :
nano monProg3.cpp
Cette commande ouvre un fichier de texte vide appelé monProg3.cpp (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
/* ----------------------------------------------------------------------------- Programme classique led clignotante Fonctions sortie() et entree() os : RPi Linux 4.4.13+ (Jessie) logiciel : gcc (Raspbian 4.9.2-10) 4.9.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : ----------------------------------------------------------------------------- */ #include <cstdio> #include <unistd.h> #include <iostream> #include "monProg3.h" #include <wiringPi.h> using namespace std; extern volatile char leCarac; //variable globale //fonction sortie() affiche * void* sortie(void *){ while(leCarac != 'q'){ //tant que différent de q // //On pourra mettre dans la boucle while notre programme raspberry Pi // digitalWrite(LED, HIGH); //allume led delay(DUREE); //attente digitalWrite(LED, LOW); //éteint led delay(DUREE); //attente } return NULL; } //fonction entrée saisie clavier void* entree(void *){ while(leCarac != 'q'){ //tant que différent de q // //On pourra mettre dans la boucle while notre programme raspberry Pi // cout << "q pour quitter !" << endl; leCarac = getchar(); //saisie clavier q+<Entrée> } return NULL; }
Exécution du programme
Pour l’exécution, il faudra faire :
g++ -Wall -o thread35 thread35.cpp monProg3.cpp -lpthread -lwiringPi
Le résultat sera un fichier : thread35
Lancement du programme :
sudo ./thread35
Pour sortir de la boucle, il suffit de faire q+<Entrée> au clavier.
Le résultat est toujours le même, notre led bien aimée clignote !
Langage Python
Pour cet exemple, nous utiliserons à nouveau deux thread pour commander notre led et sortir de la boucle infinie par la touche q+<Entrée> du clavier.
Les plus courageux pourront facilement faire la version mono-thread.
Programme
Notre programme se compose de deux fichiers thread75.py et monProg3.py.
Pour saisir le programme monProg3.py, il faut faire dans la console :
nano monProg3.py
Cette commande ouvre un fichier de texte vide appelé monProg3.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ Programme classique led clignotante Fonctions sortie() et saisie clavier logiciel : python 3.4.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : """ import sys import time from thread75 import leCarac #importe la variable globale import RPi.GPIO as GPIO def sortie(): """affichage * dans la console""" global leCarac GPIO.setmode(GPIO.BCM) while(leCarac != 'q'): #boucle tant que leCarac différent de q # #on pourra mettre dans la boucle notre programme raspberry Pi # GPIO.output(23, GPIO.HIGH) #allume led time.sleep(1) #attente 1 s GPIO.output(23, GPIO.LOW) #éteint led time.sleep(1) #attente 1 s def entree(): """saisie caractère au clavier""" global leCarac while(leCarac != 'q'): #boucle tant que leCarac différent de q # #on pourra mettre dans la boucle notre programme raspberry Pi # leCarac = input() #saisie caractère
Pour saisir le programme thread35.py, il faut faire dans la console :
nano thread35.py
Cette commande ouvre un fichier de texte vide appelé thread35.py (pour la sauvegarde faire ctrl+o et pour sortie de l’éditeur faire ctrl+x).
Le contenu du programme et de ses commentaires sont représentés ci-dessous.
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ Programme classique led clignotante Led rouge sur GPIO23 via une résistance de 330 ohms logiciel : python 3.4.2 cible : raspberry Pi date de création : 30/07/2016 date de mise à jour : 30/07/2016 version : 1.0 auteur : icarePetibles référence : Remarques : """ import sys import time from threading import Thread from monProg3 import * import RPi.GPIO as GPIO leCarac = '\0' #variables globale class MonThread(Thread): """Lanceur de fonction""" def __init__(self, fonction): Thread.__init__(self, None, fonction) self._fonction = fonction self.running = True def run(self): while self.running: self._fonction() #notre fonction programme def stop(self): self.running = False def init(): """initialisation ports""" GPIO.setwarnings(False) #désactive le mode alerte GPIO.setmode(GPIO.BCM) #numérotation ports processeur GPIO.setup(23, GPIO.OUT) #GPIO23 en sortie if __name__ == '__main__': """Programme principal""" print("Led clignotante") #initialisation init() #initialisation #fin initialisation print() print("q pour quitter !") thread_1 = MonThread(sortie) #création du thread thread_2 = MonThread(entree) thread_1.start() #lancement du thread thread_2.start() thread_1.stop() #détruit le thread thread_2.stop() # thread_1.join() #attente fin cycle thread thread_2.join() #libération des ressources GPIO.output(23, GPIO.LOW) #éteint led GPIO.cleanup() #remet les pins en entrée #fin libération des ressources print("Fin du programme")
Exécution du programme
Pour exécuter ce programme, il faut lancer la commande suivante dans la console Linux :
python3 thread35.py
Pour sortir de l’application, il suffit de faire q+<Entrée> au clavier.
Nota : Pour l’interface avec les ports GPIO, on peut utiliser une des autres bibliothèques adorbées dans la saga blink
Conclusion
L’utilisation des threads n’est pas la panacée universel mais l’une des solutions envisageables dans certains cas.
Et si notre belle led clignotante tombait amoureuse d’un beau Thread ! Dans le cadre de notre class GPIOClassOut, on pourrait imaginer de créer, les méthodes blink(), blink(int tempsAllumage, int tempsExtinction) et blinkStop().
Je fais une addiction à la led qui clignote et il est temps que je me soigne en passant à autre chose, par exemple, la « saga Push Button »
A force de parcourir des tutoriels sur la toile, nous avions noté qu’il existait de nombreux « tutos » très bien faits. Par contre, on constate également que souvent, il manquait le petit bout de quelque « chose » pour que tout se déroule bien.
Ce petit quelque « chose« que les utilisateurs avertis font sans réfléchir mais qui pose de problème aux débutants. Fort de ce constat, j’ai essayé de faire une présentation un peu différente en y intégrant toutes les étapes de la saisie jusqu’à l’exécution des programmes (Pas très écologique en terme de pages).
Faire des recherches pour comprendre est, en soi, une bonne chose mais je préfère la méthode « à force de répéter cela finira par le faire ».
Toutes les remarques, critiques, suggestions et autres approches sont les bienvenues, à partir du moment où l’on peut les partager avec les autres. Les dénigrements sont également acceptés mais dans le respect de l’autre.
A tous présents et à venir. Salut !
Sources
On pourra télécharger l’ensemble des sources sous :
http://psl.ibidouille.net/Images_forum/raspberryPi/sagaBlinkComp.zip
Ping : La Saga Blink : Un Raspberry pour faire clignoter une LED ! | Framboise 314, le Raspberry Pi à la sauce française….
Bonjour Denis,
Merci.
Nano possède également la coloration syntaxique
Je ne connais pas joe et je viens de faire un test. Plus sympa, il me rappelle wordstar sous dos.
Finalement le meilleur éditeur de texte et celui que l’on maîtrise.
Bonjour,
merci pour ses articles, je trouve cependant qu’il y a un manque pour les acharnés.(perso j’ai aimé la partie bash
pourrait t’il être envisageable d’avoir aussi un article sur l’assembleur?
http://cs.smith.edu/dftwiki/index.php/Tutorial:_Assembly_Language_with_the_Raspberry_Pi
certain ne voie peu être pas l’intérêt mais avouons que si on pouvais se passez du système d’exploitation se serait parfois mieux ?
Bonjour,
Merci pour le retour.
L’usage de l’assembleur pour le Pi n’est pas une chose simple. Cela nécessiterait un investissement de temps non négligeable que je n’ai malheureusement pas.
Mais bon qui sait !
Ping : Le bouton poussoir un composant banal, ô combien, étonnant. - Framboise 314, le Raspberry Pi à la sauce française....