vendredi 25 avril 2014

Étude de CI: Registre à décalage 74HC165

Après avoir exploré le fonctionnement du registre à décalage 74HC595 qui permet, entre autres choses, d'ajouter des sorties numériques supplémentaires à votre Arduino, nous étudions aujourd'hui son complément, le 74HC165 qui vous permettra, en cas de besoin, d'ajouter des entrées numériques supplémentaires à votre Arduino (vous pouvez, par exemple, brancher à l'Arduino un nombre quasi illimité d'interrupteurs).

Car alors que le 74HC595 est doté d'une entrée série et de 8 sorties parallèles, c'est exactement le contraire pour le 74HC165:  il comporte 8 entrées parallèles et une sortie série.

Sur le schéma de brochage ci-contre, les 8 entrées sont identifiées par les symboles D0, D1, D2, etc.  Chaque entrée pourrait, par exemple, être reliée à un interrupteur permettant de sélectionner le niveau logique bas (0 V) ou haut (5 V).

Q7 (broche 9) est la sortie, et la broche 7 (Q7 avec une barre au-dessus) est une sortie complémentaire dont l'état est toujours contraire à Q7.

La broche 10 (DS) est une entrée série, qui sera utile lorsque vous désirez relier plusieurs HC165 en cascade (elle sera alors reliée à la sortie Q7 du HC165 précédent).

Lorsque la broche 1 PL (pour parallel load) est au niveau logique bas (0 V), l'état des 8 entrées parallèle (D0 à D7) est mémorisée par le HC165.  La sortie Q7 prend alors le même état logique que l'entrée D7.   Lorsque la broche 1 "PL" est au niveau logique haut (5 V), l'entrée série DS est activée.

Chaque fois que la broche 2 "CP" (horloge) passe de 0 à 5 V, les états logiques sont décalées d'une position dans le registre (DS vers D0, D0 vers D1, etc., et Q7 prend la nouvelle valeur de D7.

Si cette description rapide vous apparaît quelque peu nébuleuse, je vous invite à construire sur breadboard un petit circuit d'expérimentation avec le HC165.  On pourrait toujours utiliser 8 interrupteurs pour contrôler l'état de nos 8 entrées, mais pour plus de simplicité, on peut se contenter de les relier manuellement au potentiel approprié au moyen de câbles conducteurs.  Une exception, toutefois:  le signal d'horloge (broche 2) doit être contrôlé par un interrupteur sans rebond, question d'éviter...les rebonds (voir cet article sur la façon de construire des interrupteurs sans rebond).  Si vous utilisez un interrupteur qui n'est pas doté d'un mécanisme anti-rebond, vous risquez d'envoyer 2 ou 3 signaux d'horloge consécutifs lorsque vous désirez en produire un seul.

Réalisons donc notre circuit d'expérimentation:  nous alimentons le CI en branchant la broche 8 à la masse (GND) et la broche 16 à 5 V (si vous ne disposez pas d'une source d'alimentation continue de 5 V, vous pouvez utiliser la sortie 5 V d'un Arduino, ou mettre 3 piles AA en série).  La broche 15 (clock enable) doit être activée en la reliant à la masse (GND).

Pour connaître l'état logique de la sortie Q7, nous utiliserons  une LED protégée par une résistance d'au moins 330 Ω (vous pouvez aussi mesurer le signal de sortie au moyen d'un voltmètre, si vous préférez).

Initialement, la broche 1 (PL) est à 5 V, et la broche 2 (CP), reliée à un interrupteur sans rebond, est à 0 V (l'anti-rebond de l'interrupteur n'est pas représenté sur le circuit illustré ci-dessous).

Maintenant, ajustons le niveau logique des entrées, en essayant d'utiliser un motif que nous pourrons ensuite reconnaître.  Allons-y pour:
DS (broche 10) à 5 V
D0 (broche 11) à 0 V
D1 (broche 12) à 5 V
D2 (broche 13) à 0 V
D3 (broche 14) à 5 V
D4 (broche 3) à 5 V
D5 (broche 4) à 0 V
D6 (broche 5) à 0 V
D7 (broche 6) à 5 V

Ça devrait ressembler à ceci (le dessin ne montre pas la source de tension de 5 V):



Le registre comporte 8 mémoires, chacune d'entre elles pouvant être au niveau logique haut ou bas. Commençons par transférer en mémoire l'état des broches D0 à D7:  pour ce faire, vous mettez temporairement la broche 1 à 0 V, puis vous la remettez à 5 V (au moyen du fil jaune sur le circuit ci-dessus).

Le tableau ci-dessous montre l'état de chacune des mémoires du registre:



Vous devriez constater que la LED s'est allumée:  la sortie Q7 prend toujours le même état logique que la mémoire associée à D7, qui est dans l'état logique haut.

Envoyons maintenant un front ascendant sur le signal d'horloge:  on appuie une seule fois sur le bouton sans rebond de façons à faire passer temporairement la broche 2 de 0 V à 5 V.  Ce signal d'horloge cause un décalage des valeurs placées en mémoire (c'est pour cette raison que ça s'appelle un registre à décalage):  la mémoire D7 prend la valeur qui était précédemment dans D6, D6 prend la valeur qui était précédemment dans D5, et ainsi de suite jusqu'à D0, qui mémorise le niveau logique de l'entrée DS (broche 10).

Le tableau ci-dessous montre le nouveau contenu des mémoires:


Puisque le niveau de la mémoire D7 est maintenant bas, la sortie Q7 s'est mise à 0 V et la LED est maintenant éteinte.

On appuie à nouveau sur le bouton relié à la broche 2, et les valeurs sont à nouveau décalées d'une position:


La LED demeure éteinte, puisque la mémoire D7 est encore au niveau bas.

Pour varier un peu, on peut mettre l'entrée DS (broche 10) à 0 V avant d'appuyer à nouveau sur le bouton de la broche 2:




Chaque fois que vous appuyez sur le bouton, c'est maintenant la valeur 0 (de l'entrée DS) qui s'installera dans la mémoire D0.

Et vous pouvez continuer d'appuyer sur le bouton de la broche 2 pour constater que la LED s'allume ou s'éteint selon les valeurs mises en mémoire.

Supposons maintenant qu'on décide de changer l'état logique des entrées:  plaçons les toutes à 5 V, sauf DS qu'on laisse à 0 V.



Pour que ces changements aient un impact sur l'état des mémoires, il faut impérativement mettre temporairement la broche 1 à 0 V.


À vous d'expérimenter avec le circuit jusqu'à ce que vous ayez compris le fonctionnement du CI à votre entière satisfaction.  La LED devrait être allumée les 7 premières fois que vous appuyez sur le bouton, puis demeurer éteinte ensuite.

Maintenant, que nous comprenons bien son fonctionnement, tentons une application pratique:  relions le 74HC165 à un Arduino, afin que ce dernier puisse lire l'état des 8 entrées du HC165.

Le circuit:  Le HC165 est alimenté par la sortie 5 V de l'Arduino, on n'a plus besoin de la LED, ni de l'interrupteur sans rebond.  La broche 2 du HC165 est commandée par la broche 2 de l'Arduino, et la broche 1 du HC165 est contrôlée par la broche 3 de l'Arduino.  La broche 9 du HC165 (sortie) est reliée à la broche 4 de l'Arduino. Vous branchez les 8 entrées du HC165 comme vous voulez:  certaines d'entre elles à 5 V, d'autres à 0 V.


Et voici un sketch qui affiche l'état de chaque entrée du 74HC165 dans le moniteur série du logiciel Arduino:

/********************HC165****************************************
Lecture et affichage au moniteur série des 8 entrées d'un
registre à décalage 74HC165
http://electroniqueamateur.blogspot.com/2014/04/etude-de-ci-registre-decalage-74hc165.html
*****************************************************************/
#define ParallelLoadPin 3
#define ClockPin 2
#define DataPin 4
int valeurs[8];
void setup()
{
pinMode(ParallelLoadPin, OUTPUT);
digitalWrite(ParallelLoadPin, HIGH);
pinMode(ClockPin, OUTPUT);
digitalWrite(ClockPin, LOW);
pinMode(DataPin, INPUT);
Serial.begin(9600); // pour l'affichage des valeur au moniteur série
}
void loop()
{
// on met la pin 1 du HC165 à 0 V pour enregistrer l'état de ses 8 entrées
digitalWrite(ParallelLoadPin, LOW);
digitalWrite(ParallelLoadPin, HIGH);
for (int i=0; i <= 7; i++){
// on note l'état de la sortie (pin 9) du HC165
valeurs[7-i] = digitalRead(DataPin);
// et on envoi un front montant sur la pin 2 pour décaler les valeurs
digitalWrite(ClockPin, HIGH);
digitalWrite(ClockPin, LOW);
}
// on affiche les résultats dans le moniteur série
Serial.print("0: ");
Serial.print(valeurs[0]);
Serial.print(" 1: ");
Serial.print(valeurs[1]);
Serial.print(" 2: ");
Serial.print(valeurs[2]);
Serial.print(" 3: ");
Serial.println(valeurs[3]);
Serial.print("4: ");
Serial.print(valeurs[4]);
Serial.print(" 5: ");
Serial.print(valeurs[5]);
Serial.print(" 6: ");
Serial.print(valeurs[6]);
Serial.print(" 7: ");
Serial.println(valeurs[7]);
delay(800); //pour que les valeurs ne défilent pas trop vite
}
view raw HC165.ino hosted with ❤ by GitHub

Yves Pelletier (Twitter:  @ElectroAmateur)


lundi 21 avril 2014

Robot éviteur d'obstacles version 2.0


Il y a quelques mois, j'avais construit un robot éviteur d'obstacles muni d'un capteur à ultrasons HC-SR04.  Mais ce robot avait une vision périphérique très limitée: il ne détectait que les obstacles situés directement devant lui, et ses roues s'accrochaient régulièrement dans des obstacles qui n'avaient pas été détectés.

Cette nouvelle version du robot est munie de 3 capteurs à ultrasons HC-SR04 situés côte à côte à l'avant du robot.  Les deux capteurs situés sur les côtés sont légèrement inclinés vers l'extérieur, ce qui élargit encore un peu plus le champ de vision du robot.

En plus du champ de vision amélioré, l'ajout des deux capteurs supplémentaires permet de faire tourner le robot du bon côté:  si un obstacle est détecté en avant à droite, mais pas à gauche, on fera tourner le robot vers la gauche, et vice-versa si  un obstacle est détecté à gauche mais pas à droite.

Matériel 

C'est le même matériel que la dernière fois, mais avec deux capteurs HC-SR04 supplémentaires:  un châssis pour robot à deux roues motrices (chaque roue étant directement reliée à un motoréducteur), un Arduino Uno (en fait c'est un Duemilanove, mais peu importe), un contrôleur de moteur L293D, 3 sondes ultrasonores HC-SR04, 6 piles AA rechargeables NiMh pour alimenter les moteurs, et une pile de 9 V pour alimenter l'Arduino.


Circuit

Là encore, je n'ai rien changé aux branchements du robot initial, ce qui explique au moins partiellement l'utilisation quelque peu désordonnée des différentes pins de l'Arduino.

Le schéma ci-dessous indique les branchements du L293D à l'Arduino, aux deux moteurs et à son alimentation.  Ne pas oublier de relier ensemble toutes les masses:  le GND de l'Arduino, les 4 pins GND du L293D et la pin GND de chaque capteur HC-SR04 sont toutes reliées ensemble.


Chaque capteur ultrasonore est muni de 4 pins:  la pin Vcc est reliée à la sortie 5 V de l'Arduino (ou à la pin 16 du L293D) et, comme je l'ai indiqué plus haut, la pin GND qui doit être reliée aux GND de l'Arduino.

Il nous reste à brancher les pins qui permettent l'échange d'information entre chaque capteur ultrasonore et l'Arduino.  Puisque les pins disponibles sur l'Arduino devenaient rares, j'ai décidé d'utiliser les pins A0, A1, A2 et A3 comme des entrées/sorties numériques plutôt que comme des entrées analogiques.  C'est permis! Pour ce faire, il s'agit d'utiliser dans le sketch les fonctions digitalRead ou digitalWrite.

Capteur A (à droite)      
        Pin Trig à Arduino 12
        Pin Echo à Arduino 11

Capteur B (au centre)    
        Pin Trig à Arduino A0
        Pin Echo à Arduino A1
    
Capteur C (à gauche)
        Pin Trig à Arduino A2
        Pin Echo à Arduino A3
     
Le sketch:

/***************************************************************
*
* Robot éviteur d'obstacle muni de trois capteurs à ultrasons HC-SR04.
*
* Comportement: Le robot avance en ligne droite, sauf s'il
* rencontre un obstacle, auquel cas il tourne sur place jusqu'à
* ce qu'il n'y ait plus d'obstacle devant lui.
*
* http://electroniqueamateur.blogspot.com/2014/04/robot-eviteur-dobstacles-version-20.html
*
****************************************************************/
// définitions de constantes
#define motorPin1a 3 // Marche avant du premier moteur
#define motorPin1b 4 // Marche arrière du premier moteur
#define speedPin1 9 // pin d'activation L293D pour le premier moteur
#define motorPin2a 5 // Marche avant du deuxième moteur
#define motorPin2b 6 // Marche arrière du deuxième moteur
#define speedPin2 10 // pin d'activation L293D pour le deuxième moteur
// capteur à ultrasons:
#define trigPinDroite 12
#define echoPinDroite 11
#define trigPinCentre A0
#define echoPinCentre A1
#define trigPinGauche A2
#define echoPinGauche A3
// variables globales
int Mspeed = 0; // vitesse du moteur
int seuil = 30; // distance minimale pour laquelle on accepte un obstacle
long look (short capteur){ // évaluation de la distance de l'obstacle
long temps;
short trigPin, echoPin;
if (capteur == 0){ // capoteur de droite
trigPin = trigPinDroite;
echoPin = echoPinDroite;
}
if (capteur == 1){ // capoteur du centre
trigPin = trigPinCentre;
echoPin = echoPinCentre;
}
if (capteur == 2){ // capoteur de gauche
trigPin = trigPinGauche;
echoPin = echoPinGauche;
}
// Nous envoyons un signal haut d'une durée de 10 microsecondes, en sandwich
// entre deux signaux bas. Des ultrasons sont émis pendant que le signal est haut
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Lors de la réception de l'écho, le module HC-SR04 émet
// un signal logique haut (5 v) dont la durée est égale au
// temps écoulé entre l'émission et la réception de l'ultrason.
pinMode(echoPin, INPUT);
temps = pulseIn(echoPin, HIGH);
return temps * 340/(2*10000);
}
void setup() {
// réglage des broches à output
pinMode(motorPin1a, OUTPUT);
pinMode(motorPin1b, OUTPUT);
pinMode(speedPin1, OUTPUT);
pinMode(motorPin2a, OUTPUT);
pinMode(motorPin2b, OUTPUT);
pinMode(speedPin2, OUTPUT);
pinMode(echoPinDroite, INPUT);
pinMode(trigPinDroite, OUTPUT);
digitalWrite(trigPinDroite, LOW);
pinMode(echoPinCentre, INPUT);
pinMode(trigPinCentre, OUTPUT);
digitalWrite(trigPinCentre, LOW);
pinMode(echoPinGauche, INPUT);
pinMode(trigPinGauche, OUTPUT);
digitalWrite(trigPinGauche, LOW);
}
void loop() {
long distanceDroite, distanceCentre, distanceGauche;
long previousDroite, previousCentre, previousGauche;
Mspeed = 700; // vitesse du moteur 0 à 1023
distanceDroite = look(0); // y a-t-il un obstacle à droite?
distanceCentre = look(1); // y a-t-il un obstacle au centre?
distanceGauche = look(2); // y a-t-il un obstacle à gauche?
if (distanceDroite > seuil && distanceCentre > seuil && distanceGauche > seuil){
// aucun obstacle détecté, donc marche avant:
analogWrite(speedPin1, Mspeed);
digitalWrite(motorPin1a, HIGH);
digitalWrite(motorPin1b, LOW);
analogWrite(speedPin2, Mspeed);
digitalWrite(motorPin2a, HIGH);
digitalWrite(motorPin2b, LOW);
delay(100);
}
else { // on a détecté un obstacle
// nouvelle vérification pour éviter les faux positifs
previousDroite = distanceDroite;
previousCentre = distanceCentre;
previousGauche = distanceGauche;
distanceDroite = look(0); // y a-t-il un obstacle à droite?
distanceCentre = look(1); // y a-t-il un obstacle au centre?
distanceGauche = look(2); // y a-t-il un obstacle à gauche?
if ((distanceDroite <= seuil) && (previousDroite)<= seuil ){
// obstacle confirmé
if (distanceDroite < distanceGauche) { // on tourne à gauche
analogWrite(speedPin1, Mspeed);
digitalWrite(motorPin1a, HIGH);
digitalWrite(motorPin1b, LOW);
analogWrite(speedPin2, Mspeed);
digitalWrite(motorPin2a, LOW);
digitalWrite(motorPin2b, HIGH);
delay(110);
if (distanceGauche <= seuil){ // il y a aussi un obstacle proche à gauche
delay(700); // on continue de tourner un peu plus
}
}
else // l'obtacle à gauche est plus proche que l'obstacle à droite
{
// on tourne assez longtemps vers la droite
analogWrite(speedPin1, Mspeed);
digitalWrite(motorPin1a, LOW);
digitalWrite(motorPin1b, HIGH);
analogWrite(speedPin2, Mspeed);
digitalWrite(motorPin2a, HIGH);
digitalWrite(motorPin2b, LOW);
delay(900); // tournons de façon significative
}
}
else if ((distanceGauche <= seuil) &&(previousGauche <= seuil)) {
// obstacle à gauche seulement, on tourne à droite
analogWrite(speedPin1, Mspeed);
digitalWrite(motorPin1a, LOW);
digitalWrite(motorPin1b, HIGH);
analogWrite(speedPin2, Mspeed);
digitalWrite(motorPin2a, HIGH);
digitalWrite(motorPin2b, LOW);
delay(100);
}
else if ((distanceCentre <= seuil) && (previousCentre <= seuil)){
// mince obstacle droit devant, on tourne à droite
analogWrite(speedPin1, Mspeed);
digitalWrite(motorPin1a, LOW);
digitalWrite(motorPin1b, HIGH);
analogWrite(speedPin2, Mspeed);
digitalWrite(motorPin2a, HIGH);
digitalWrite(motorPin2b, LOW);
delay(90);
}
}
}


Comportement du robot:  


Le robot tourne à gauche s'il perçoit la présence d'un obstacle en avant à droite, et il tourne à droite s'il perçoit la présente d'un obstacle en avant à gauche.  Si l'obstacle est droit devant, il tourne à droite.  Il n'est doté d'aucune mémoire, ce qui peut le rendre assez hésitant lorsqu'il arrive face à un coin de mur, mais le sketch induit juste assez d'asymétrie pour éviter que le robot se prenne dans une boucle sans fin.  Il finit généralement par trouver une voie de sortie, même si ce n'est pas toujours de la façon la plus élégante et efficace!

Yves Pelletier (Twitter:  @ElectroAmateur)

dimanche 6 avril 2014

Étude de CI: registre à décalage HC595

 J'inaugure aujourd'hui une nouvelle série d'articles qui consistera à explorer le fonctionnement d'un circuit intégré.  Pour cette première livraison, j'ai choisi le registre à décalage HC595, dont voici la fiche technique.


Ce circuit intégré est largement utilisé pour augmenter le nombre de sorties d'un microcontrôleur. Votre Arduino ne comporte pas suffisamment de sorties pour une application précise?  Vous y ajoutez un ou plusieurs registres à décalage et le problème est réglé!

Le registre à décalage HC595 comporte 16 broches.  8 d'entre elles (QA, QB, etc) sont des sorties qui peuvent prendre une valeur logique haute ou basse. L'état de ces 8 sorties dépendra des informations envoyées à l'entrée du signal série (broche numéro 14) ainsi qu'aux entrées d'horloge (broches numéros 11 et 12).

Chaque sortie du registre à décalage est associée à une mémoire qui peu prendre la valeur logique 0 et 1.

Chaque fois que la broche 11 passe du niveau logique 0 au niveau logique 1 (donc de 0 V à 5 V), le contenu de chaque mémoire est décalé d'une position:  la mémoire associée à la broche 7 prend le contenu de la mémoire associée à la broche 6, la mémoire associée à la broche 6 prend le contenu de la mémoire associée à la broche 5, etc.   Et la mémoire associée à la broche 15?  Elle prend la valeur qui correspond à l'état de la broche 14:  0 si la broche 14 est à 0 V, 1 si la broche 14 est à 5 V.


Mais jusque là, seul l'état des mémoires associées aux sorties à changé:  l'état des sorties elles-mêmes ne changera que lorsque la broche 12 passera du niveau logique 0 au niveau logique 1:  à ce moment, chaque sortie prendra l'état logique correspondant au contenu de la mémoire qui lui est associée.

Vous me suivez?  ... Hum...la meilleure façon, c'est d'essayer vous-mêmes!

Pour bien comprendre le comportement d'un registre à décalage, je vous invite à construire sur un breadboard un petit circuit d'expérimentation qui consistera en un HC595 (évidemment), une alimentation continue de 5 V, 8 LEDs qui indiqueront l'état des sorties, et 3 interrupteurs sans rebond pour contrôler l'état des 3 principales entrées (je vous réfère à cet article pour la construction d'un module de 6 interrupteurs sans rebonds).  Si vous ne disposez pas du matériel nécessaire (ou si vous n'aimez pas construire des circuits électroniques, auquel cas je ne sais pas ce que vous êtes venus faire ici!), vous pouvez toujours vous rabattre sur ce simulateur en ligne, mais c'est moins amusant.

En ce qui concerne l'alimentation, le HC595 tolère n'importe quelle source de tension continue située entre 2 et 6 V:  j'utiliserai 5 V pour cette expérience (vous n'avez pas de source de tension de 5 V?  Vous pouvez utiliser la sortie 5 V d'un Arduino, ou encore 3 piles AA placées en série).

Mais attention:  d'après la fiche technique,  l'intensité de courant traversant le circuit intégré. ne doit pas dépasser 70 mA:  il faut donc prendre soin d'accompagner chaque LED d'une résistance de protection suffisante pour que le courant à travers celle-ci ne dépasse pas un huitième de 70 mA, soit 8,75 mA. Puisque la chute de potentiel aux bornes d'une LED rouge avoisine 2 V, il y aura 3 V aux bornes de la résistance. Selon la loi d'Ohm, nous avons donc besoin d'une résistance égale à (3 V)/(0,00875 A), soit 343 Ω. Alors on choisit la résistance conventionnelle la plus proche, soit 390 Ω.

Voici donc notre montage.  L'alimentation de 5 V est branchée aux broches 16 (+) et 8 (-). Chacune des sorties (broches 15, 1, 2, 3, 4, 5, 6, 7) est reliée à une LED en série avec une résistance de 390 Ω. Chacune des entrées (broches 11, 12 et 14) est reliée à un interrupteur de façon à ce que l'entrée prenne une valeur logique haute lorsqu'on appuie sur le bouton.  Le schéma ne comporte aucun anti-rebond pour les interrupteurs, mais au minimum l'interrupteur relié à la broche 11 devrait être sans rebonds, sinon vous risquez d'obtenir des résultats assez imprévisibles.  La broche 13 (output enable) est reliée à la masse en permanence, et c'est le contraire pour la broche 10  (reset), qu'on désactive en la maintenant à 5 V.



Prêts à expérimenter?  Au départ, lorsque vous alimentez le circuit, il est possible que certaines des LEDs soient déjà allumées.  Si c'est le cas, initialisez votre circuit par une des deux méthodes suivantes:

1) Branchez temporairement la broche 10 à la masse (0 V) avant de la brancher à nouveau à 5 V
ou
2) Appuyez 8 fois sur le bouton qui est relié à la broche 11, et ensuite appuyez une fois sur le bouton qui est relié à la broche 12.

Si tout va bien, vos 8 LEDs sont maintenant éteintes, ce qui indique que chacune des 8 sorties est au niveau logique bas (0 V).


Maintenant, appuyez sur le bouton qui est relié à la broche 14 pour envoyer un signal logique haut.  Tout en le maintenant enfoncé, appuyez brièvement sur le bouton qui est relié à la broche 11:  Aucune LED ne s'allume, mais l'état des mémoires à changé: la mémoire associée à la broche 15 est maintenant haute (car la broche 14 était dans cet état lorsque la broche 11 est passée de bas à haut), et le contenu de chaque mémoire est décalé d'une position.


Pour que ces modifications deviennent visibles, il s'agit d'appuyer brièvement sur le bouton qui est relié à la broche 12:  chaque sortie prend alors la valeur stockée dans sa mémoire (et la LED reliée à la broche 15 s'allume).


Envoyons un deuxième signal logique haut.  Encore une fois, vous appuyez sur le bouton qui est relié à la broche 14 et, tout en le maintenant enfoncé, vous appuyez sur le bouton qui est relié à la broche 11.   Toutes les valeurs étant décalées d'une position, l'état des mémoires est maintenant le suivant:


Et l'état des mémoires est transféré aux sorties lorsque vous appuyez sur le bouton de la broche 12:


Pour envoyer un signal logique bas, vous appuyez sur le bouton de la broche 11 sans appuyer sur le bouton de la broche 14.  La mémoire de la broche 15 prendra donc une valeur basse, et toutes les autres valeurs sont décalées d'une position:


Et vous appuyez encore une fois sur le bouton de la broche 12 pour que les sorties soient modifiées en conséquence.

Entrée d'une nouvelle valeur logique haute (on enfonce le bouton de la broche 11 pendant que le bouton de la broche 15 est déjà enfoncé):


On appuie sur le bouton de la broche 12:


Entrée d'une nouvelle valeur basse (bouton de la broche 11 seulement):


On appuie sur le bouton de la broche 12:


Dans la plupart des applications, on commence par régler une par une l'état des mémoires, et leur transfert vers les sorties n'est effectué qu'à la fin, lorsque toutes les mémoires ont la valeur désirée.

Ainsi, pour allumer toutes les LEDs en même temps, vous envoyez 8 messages hauts consécutifs (vous appuyez 8 fois de suite sur le bouton de la broche 11 tout en maintenant le bouton de la broche 15 enfoncé), et vous n'appuyez sur le bouton 12 qu'à la toute fin.

Pour terminer, voyons comment nous pouvons contrôler un registre à décalage au moyen d'un Arduino.  Nous n'avons plus besoin des 3 interrupteurs, ni de la source d'alimentation externe qui sera remplacée par la sortie 5 V de l'Arduino.  La broche 2 de l'Arduino est reliée à la broche 15 du 595, la broche 3 de l'Arduino est reliée à la broche 12 du 595, et la broche 4 de l'Arduino est reliée à la broche 11 du 595 (n'oubliez pas de relier toutes les masses:  GND de l'Arduino avec la broche 8 du 595).



Le sketch ci-dessous allume les 4 LEDs impaires pendant 1 seconde, puis les 4 LEDs paires.  Comme nous le verrons ensuite, ce n'est pas nécessairement la façon la plus simple de procéder, mais vous pouvez constater que l'Arduino gère les broches 11, 12 et 15 du 595 de la même façon que vous le faisiez avec les interrupteurs.

---
/**************************************************************************************
* Contrôle d'un registre à décalage hc595, sans l'utilisation de la fonction Shift Out.
* On allume les 4 LEDs paires, et ensuite les 4 LEDs impaires, en boucle.
* Connexions: Arduino 2 -- hc595 15 (entrée série)
* Arduino 3 -- hc595 12 (latch clock)
* Arduino 4 -- hc595 11 (shift clock)
* http://electroniqueamateur.blogspot.ca/2014/04/etude-de-ci-registre-decalage-hc595.html
***************************************************************************************/
#define serial 2
#define latch 3
#define shift 4
void setup() {
pinMode(serial, OUTPUT);
pinMode(latch, OUTPUT);
pinMode(shift, OUTPUT);
}
void loop() {
// d'abord, nous allumons toutes les LEDs paires
digitalWrite(latch,LOW); // intialisation de la pin 12 du 595 (une seule fois avant d'envoyer 8 signaux)
for (int i=0; i <= 3; i++){
digitalWrite(shift, LOW); // initialisation de la pin 11 du 595 (avant l'envoi de chaque signal)
digitalWrite(serial, LOW); // envoi d'un signal logique bas (LED éteinte)
digitalWrite(shift, HIGH); // inscription du signal dans la mémoire du 595 et décalage
// envoi d'un signal logique haut:
digitalWrite(shift, LOW); // initialisation de la pin 11 du 595 (avant l'envoi de chaque signal)
digitalWrite(serial, HIGH); // envoi d'un signal logique haut (LED allumée)
digitalWrite(shift, HIGH); // inscription du signal dans la mémoire du 595 et décalage
}
digitalWrite(latch,HIGH); // contenu de la mémoire transféré à la sortie du 595
delay (500); // on attend un peu
// ensuite nous allumons toutes les LEDs impaires
digitalWrite(latch,LOW); // intialisation de la pin 12 du 595
for (int i=0; i <= 3; i++){
// envoi d'un signal logique haut:
digitalWrite(shift, LOW);
digitalWrite(serial, HIGH);
digitalWrite(shift, HIGH);
// envoi d'un signal logique bas:
digitalWrite(shift, LOW);
digitalWrite(serial, LOW);
digitalWrite(shift, HIGH);
}
digitalWrite(latch,HIGH); // contenu de la mémoire transféré à la sortie du 595
delay (500); // on attend un peu
}
---

Mieux encore:  il existe une fonction conçue spécialement pour contrôler un registre à décalage:  la fonction ShiftOut.  Comme vous pouvez le constater dans le sketch ci-dessous (qui accomplit exactement la même action que le sketch précédent), il s'agit de passer à la fonction shiftOut un nombre binaire de 8 bits indiquant l'état désiré de chacune des 8 sorties du registre à décalage, la fonction s'occupe automatiquement de la gestion des broches 11 et 15 du 595 (il ne nous reste plus qu'à gérer la broche 12).

---
/**************************************************************************************
* Contrôle d'un registre à décalage hc595, avec l'utilisation de la fonction shiftOut.
* On allume les 4 LEDs paires, et ensuite les 4 LEDs impaires, en boucle.
* Connexions: Arduino 2 -- hc595 15 (entrée série)
* Arduino 3 -- hc595 12 (latch clock)
* Arduino 4 -- hc595 11 (shift clock)
* http://electroniqueamateur.blogspot.ca/2014/04/etude-de-ci-registre-decalage-hc595.html
***************************************************************************************/
#define serial 2
#define latch 3
#define shift 4
void setup() {
pinMode(serial, OUTPUT);
pinMode(latch, OUTPUT);
pinMode(shift, OUTPUT);
}
void loop() {
// d'abord, nous allumons toutes les LEDs paires
digitalWrite(latch,LOW); // intialisation de la pin 12 du 595
shiftOut(serial, shift, MSBFIRST, 0b01010101);
digitalWrite(latch,HIGH); // contenu de la mémoire transféré à la sortie du 595
delay (500); // on attend un peu
// ensuite nous allumons toutes les LEDs impaires
digitalWrite(latch,LOW); // intialisation de la pin 12 du 595
shiftOut(serial, shift, MSBFIRST, 0b10101010);
digitalWrite(latch,HIGH); // contenu de la mémoire transféré à la sortie du 595
delay (500); // on attend un peu
}
---

Et la broche 9 du 595, elle sert à quoi?  Il s'agit d'une sortie qui permet d'ajouter un deuxième registre à décalage (on branche la broche 9 du premier registre à la broche 15 du second).  Lorsque la broche 11 passe de 0 V à 5 V, la valeur qui était stockée dans la mémoire associée à la broche 7 est transférée à la broche 9 (vous pouvez vous en convaincre en ajoutant une neuvième LED reliée à cette broche).  En pratique, ça nous permet d'ajouter un deuxième registre à décalage:  il s'agit de relier la broche 9 du 1er registre à la broche 15 du deuxième, et vos 3 interrupteurs contrôlent maintenant l'état de 16 sorties!  (Et rien ne vous empêche d'ajouter un 3e registre relié au 2e, puis un quatrième relié au 3e, etc.).

Yves Pelletier (Twitter:  @ElectroAmateur)

samedi 5 avril 2014

Exploration de la carte STM32 Nucleo


Note: cet article, qui date de plusieurs années, parle de la programmation de la carte Nucleo avec mbed. De nos jours, je préfère programmer cette carte avec l'IDE Arduino, en utilisant exactement la même syntaxe que pour les cartes Arduino conventionnelles.

Aujourd'hui, je vous parle de mes premières expérimentations avec la carte de prototypage STM32 Nucleo, mise sur le marché en février dernier par ST Microelectronics.  J'ai fait l'essai du plus modeste des 4 modèles présentement disponibles, soit le Nucleo-F030R8 (mémoire flash de 64K, alors que c'est 512K pour le F401RE).

Basé sur un microcontrôleur ARM Cortex 32 bits STM32, cette carte est dotée  de connecteurs femelles qui respectent la norme Arduino UNO:  on peut donc brancher au Nucléo un shield qui a été conçu pour l'Arduino, et programmer le Nucleo en utilisant la numérotation de pins Arduino (D0 à D13 et A0 à A5). La carte est également munie de connecteurs mâles (appelés Morpho, une norme propre à ST):  et là, c'est du sérieux, puisqu'on a accès à chacune des 64 pins du microcontrôleurs.


Autre aspect intéressant:  son prix:  une dizaine d'euros environ.  De toute évidence, ST utilise une stratégie similaire à celle utilisée par Texas avec ses cartes Launchpad.  Je suppose que leur but n'est pas de faire des profits sur les cartes elles-mêmes, mais plutôt de promouvoir l'utilisation de le leurs microcontrôleurs.

Petite anecdote:  selon la paperasse qui accompagnait mon colis (expédié au Québec depuis les États-Unis), ma carte Nucleo a été assemblée en France.



La carte arrive avec un programme d'exemple qui s'exécutera aussitôt que vous la brancherez au port USB d'un ordinateur:  une LED clignote, et la fréquence du clignotement change lorsque vous appuyez sur le bouton bleu.  Bon, rien de bien spectaculaire.

Pour programmer la carte, vous pouvez toujours installer un gros logiciel spécialisé sur le disque dur de votre ordinateur, mais ce n'est pas nécessaire puisque Nucleo est compatible avec mbed, un compilateur en ligne auquel vous accédez au moyen de n'importe quel fureteur web (c'est particulièrement pratique si vous avez l'habitude de programmer sur plusieurs ordinateurs différents).  Après avoir choisi un nom d'utilisateur et un mot de passe, vous pouvez cliquer sur le bouton "Compiler" pour accéder au compilateur.



Une fois dans le compilateur, le bouton "Import" vous donne accès à des centaines d'exemples (un peu comme si vous étiez dans github).  Il y a de la redondance, toutefois:  les trois premiers programmes que j'ai ouverts faisaient tous exactement la même chose, c'est à dire faire clignoter la LED intégrée à la carte.

Lorsque vous avez rédigé votre programme, vous cliquez sur le bouton "Compile", qui permet d'enregistrer sur l'ordinateur un fichier exécutable .bin qu'on upload ensuite dans le microcontrôleur en copiant le fichier dans la carte, comme si cette dernière était une clé USB.



Je vous présente ci-dessous trois petits programmes que j'ai conçus en me basant sur ceux qui étaient fournis.

Comme le veut la tradition, commençons par faire clignoter quelques LEDs:   trois LEDs sont branchées aux pins D2, D3 et D4 (qui occupent la même position que les pins 2, 3 et 4 de l'Arduino), avec leur résistance de protection bien entendu (150 Ω ou un peu plus, puisque la tension de sortie du Nucleo est de 3,3 V). Les 3 LEDs s'allument en alternance.


---
// Programme pour STM32 Nucleo
// Allume en alternance 3 LEDs branchées
// aux pins D2, D3 et D4.
//
// http://electroniqueamateur.blogspot.com/2014/04/exploration-de-la-carte-stm32-nucleo.html
#include "mbed.h"
DigitalOut led_1(D2); // D2 initialisée en sortie
DigitalOut led_2(D3); // D3 initialisée en sortie
DigitalOut led_3(D4); // D4 initialisée en sortie
int main() {
while(1) {
led_1 = 1; // On allume la LED 1
wait(0.5); // ...pendant 0.5 seconde
led_1 = 0; // On éteint la LED 1
led_2 = 1; // On allume la LED 2
wait(0.5); // ...pendant 0.5 seconde
led_2 = 0; // On éteint la LED 2
led_3 = 1; // On allume la LED 3
wait(0.5); // ...pendant 0.5 seconde
led_3 = 0; // On éteint la LED 3
}
}
view raw multiLED.cpp hosted with ❤ by GitHub

---

Comme vous pouvez le constater, ce n'est pas la même syntaxe qu'un sketch Arduino, mais ce n'est pas plus compliqué.

Maintenant qu'on sait allumer une LED, essayons de lire l'état d'un bouton.  La plupart des exemples disponibles allument la LED de la carte au moyen du bouton de la carte, pour changer en voici un qui permet d'allumer une LED (branchée à D3) en appuyant sur un bouton externe (branché à D2):


---
// programme pour stm32 Nucleo
// un bouton et une résistance pull down est relié à D2
// une LED et sa résistance de protection est reliée à D3
// la LED s'allume quand le bouton est enfoncé
//
// http://electroniqueamateur.blogspot.com/2014/04/exploration-de-la-carte-stm32-nucleo.html
#include "mbed.h"
DigitalIn monBouton(D2);
DigitalOut maLed(D3);
int main() {
while(1) {
maLed = monBouton; // on allume la LED si le bouton est enfoncé
}
}

---

Pour terminer, tentons de lire un signal analogique.  Trois LEDs branchées à D2, D3 et D4 s'allumeront en fonction du signal envoyé en A0 par un potentiomètre:  plus le signal analogique est élevé, plus le nombre de LEDs allumées est grand.


---
// programme pour stm32 Nucleo
// potentiomètre relié à A0
// 3 LEDs reliées à D2, D3, D4
// le potentiomètre contrôle le nombre de LEDs allumées
//
// http://electroniqueamateur.blogspot.com/2014/04/exploration-de-la-carte-stm32-nucleo.html
#include "mbed.h"
AnalogIn valeur_analogique(A0);
DigitalOut led_1(D2);
DigitalOut led_2(D3);
DigitalOut led_3(D4);
int main() {
while(1) {
uint16_t valeur = valeur_analogique.read_u16(); // lecture de la valeur analogique
if (valeur < 50){
led_1 = 0; // éteinte
led_2 = 0; // éteinte
led_3 = 0; // éteinte
}
else if ((valeur >= 50) && (valeur < 2000)){
led_1 = 1; // allumée
led_2 = 0; // éteinte
led_3 = 0; // éteinte
}
else if ((valeur >= 2000) && (valeur < 4000)){
led_1 = 1; // allumée
led_2 = 1; // allumée
led_3 = 0; // éteinte
}
else if (valeur >= 4000) {
led_1 = 1; // allumée
led_2 = 1; // allumée
led_3 = 1; // allumée
}
}
}

---

Premières impressions:  pas chère, performante (si je me fie aux spécifications du fabricant), et plutôt facile à utiliser.

La carte Nucleo F030R8 utilisée pour la rédaction de cet article a été fournie gratuitement par ST Microelectronics.

Lire aussi:  STM32 Nucleo et afficheur LCD


Yves Pelletier (Twitter:  @ElectroAmateur)