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....