Pour ceux qui seraient intéressés, voici comment j’ai procédé
- Théorie sur les moteurs pas à pas
- Piloter un moteur pas à pas (via un CI L293D) avec une Arduino
- Programme Arduino
- Théorie du GPIO de la Raspberry Pi
- Assemblage de LEDs pilotées par la Raspberry
- Allumer 4 LEDs avec un script en Python
- Utilisation de la bibliothèque WiringPi en C
- Programmation du pilote en C
Théorie sur les moteurs pas à pas
Pour cette expérience, j’ai utilisé un moteur pas à pas bipolaire (4 fils). On doit contrôler le sens courant dans chacune des bobines. Dans le cas de ce type de moteur, il n’y a qu’une seule bobine pour chaque pôle.
Ce type de configuration impose de pouvoir changer le sens du courant au niveau du système d’alimentation. Ces moteurs sont appelés moteurs pas à pas bipolaires, car lors de leur rotation chacune des bobines va être polarisée de deux façons différentes. Pour faire un tour complet, il faut 8 étapes. On parle alors de demi pas.
Pour faire avancer le moteur, il faudra donc réaliser les séquences suivantes :
En gris, les pôles ne sont pas alimentés ; en noir les pôles alimentés attirent le rotor de pas en pas pour effectuer un tour complet.
Piloter un moteur pas à pas (via un CI L293D) avec une Arduino
pas serait le bras musclé tandis que le pilotage devra se faire depuis le cerveau (dans notre cas l’Arduino puis le Raspberry pi).
Si le cerveau est capable de placer correctement les valeurs aux 4 pôles (ABCD) à chaque instant, il n’a pas la force physique de déplacer le moteur.
Le moteur nécessite un courant plus fort (de l’ordre de 1A à 2A). Le cerveau travaille quant à lui avec un courant de l’ordre de 20mA.
Si on branchait directement « les muscles » du cerveau sur le moteur, ce premier grillerait sur place (trop de puissance). C’est là qu’intervient le CI L293D. Le rôle de ce dernier est de fournir une tension dans un pôle chaque fois que « le cerveau » le demande mais avec les capacités physiques « des muscles ».
Lorsque le CI reçoit un courant faible en 2A, il retourne un courant fort en 2Y
Le CI est lui-même alimenté en courant faible Vcc1
Le courant fort est placé en Vcc2 (il est possible de shunter Vcc2 sur Vcc1 pour les PETITS moteurs pas à pas)
Certains moteurs ont besoin d’un courant fort pour déplacer le rotor mais il ne doit pas resté trop longtemps dans le moteur au risque de le griller. Pour éviter cela on peut cesser d’alimenter les pattes « Enable » (EN1et2 et EN3et4).
J’ai donc connecté à mon Arduino un shield de test sur lequel j’ai placé mon CI L293D.
J’ai relié les broches 13(Enable), 8 (Pôle A), 9 (Pôle B), 10 (Pôle C), 11 (Pôle D)
J’ai choisi d’implémenter une gestion du signal de PWM avec la broche 13. Toutefois, cette option n’étant pas nécessaire pour mon petit moteur, je ne l’implémenterai pas dans la version pour Raspberry. Et je modifierai le câblage en court-circuitant les broches Enable (sur Vcc1 +5V)
Programme Arduino
Pour alimenter le moteur, il faut réaliser les séquences vues précédemment, suffisamment rapidement pour que cela semble fluide puis envoyer ce signal au CI L293D.
Il existe des programmes tout fait donnés en exemple pour piloter un moteur pas à pas mais j’ai préféré le refaire par moi-même car cela devrait pouvoir me permettre de porter mon code plus facilement lorsque j’aurai besoin de migrer sur la Raspberry Pi.
Voici donc mon code :
/*
Moteur pas à pas
Permet de faire piloter un moteur moteur
SANS utilisation de la librairie Stepper.h (moteur Bipolar)
Functions
Stepper(steps, pin1, pin2, pin3, pin4)
setSpeed(rpm)
step(steps)
AUTEUR : Frédéric RALLO
*/
#include <MsTimer2.h> // inclusion de la librairie Timer2
// dépend du moteur
const int nombreDePasParTour = 48;
// --------------------------------------------------------------------------------------------------------------------------
// METHODES PRIVEES
// --------------------------------------------------------------------------------------------------------------------------
// enable control (HIGH => Le moteur est pilotable)
int enable = 0;
// 4 fils du moteur pas à pas
int bobineA = 0;
int bobineB = 0;
int bobineA_barre = 0;
int bobineB_barre = 0;
byte etat = 0;
long nbInterruptions = 100; // mis à jour dans changerVitesse()
long vitesse = 0; //nb de tours par minute calculé dans init()
int nbInterruptionsParTour = 0; //calculé dans init()
// --------------------------------------------------------------------------------------------------------------------------
// METHODES PUBLIQUES
// --------------------------------------------------------------------------------------------------------------------------
void initMoteur(byte nombreDePasParTour, int filEN, int fil1, int fil2, int fil3, int fil4) {
// filEN, fil1..fil4 : numméro des E/S carte ARDUINO pour connection du moteur
// nombreDePasParTour est le nombre de pas par tour
// enable=HIGH --> le moteur est pilotable
// chaque tour, on active succéssivement :
// . etat=0 --> bobineA,
// . etat=1 --> bobineA + bobineB
// . etat=2 --> bobineB,
// . etat=3 --> bobineB + bobineA_barre
// . etat=4 --> bobineA_barre,
// . etat=5 --> bobineA_barre + bobineB_barre
// . etat=6 --> bobineB_barre
// . etat=7 --> bobineB_barre + bobineA
enable = filEN;
bobineA = fil1;
bobineA_barre = fil2;
bobineB = fil3;
bobineB_barre = fil4;
nbInterruptionsParTour = nombreDePasParTour * 8;
}
void changerVitesse(long v) {
digitalWrite(enable, LOW);
if ( (v>0) && (nbInterruptionsParTour>0) ) {//on doit avoir initialisé avant
//nbInterruptions = 1000/(nbInterruptionsParTour*v/60);
nbInterruptions = 3600/(nbInterruptionsParTour*v/60);
vitesse = v;
}
Serial.print("vitesse=");
Serial.print(vitesse);
Serial.print(" nombre_De_Pas_Par_Tour=");
Serial.print(nombreDePasParTour);
Serial.print(" frequence_interruption=");
Serial.println(nbInterruptions);
digitalWrite(enable, HIGH);
}
void avancerUnPas() {
switch (etat)
{
case 0: //Serial.print("bobineA :");
digitalWrite(bobineA, HIGH);
digitalWrite(bobineB, LOW);
digitalWrite(bobineA_barre, LOW);
digitalWrite(bobineB_barre, LOW);
break;
case 1: //Serial.print("bobineA bobineB :");
digitalWrite(bobineA, HIGH);
digitalWrite(bobineB, HIGH);
digitalWrite(bobineA_barre, LOW);
digitalWrite(bobineB_barre, LOW);
break;
case 2: //Serial.print("bobineB :");
digitalWrite(bobineA, LOW);
digitalWrite(bobineB, LOW);
digitalWrite(bobineA_barre, HIGH);
digitalWrite(bobineB_barre, LOW);
break;
case 3: //Serial.print("bobineA_barre bobineB :");
digitalWrite(bobineA, LOW);
digitalWrite(bobineB, HIGH);
digitalWrite(bobineA_barre, HIGH);
digitalWrite(bobineB_barre, LOW);
break;
case 4: //Serial.print("bobineA_barre :");
digitalWrite(bobineA, LOW);
digitalWrite(bobineB, LOW);
digitalWrite(bobineA_barre, HIGH);
digitalWrite(bobineB_barre, LOW);
break;
case 5: //Serial.print("bobineA_barre bobineB_barre :");
digitalWrite(bobineA, LOW);
digitalWrite(bobineB, LOW);
digitalWrite(bobineA_barre, HIGH);
digitalWrite(bobineB_barre, HIGH);
break;
case 6: //Serial.print("bobineB_barre :");
digitalWrite(bobineA, LOW);
digitalWrite(bobineB, LOW);
digitalWrite(bobineA_barre, LOW);
digitalWrite(bobineB_barre, HIGH);
break;
case 7: //Serial.print("bobineB_barre bobineA :");
digitalWrite(bobineA, HIGH);
digitalWrite(bobineB, LOW);
digitalWrite(bobineA_barre, LOW);
digitalWrite(bobineB_barre, HIGH);
//Serial.println("-------------> 1 pas");
break;
}//end Case
etat = (etat+1) % 7;
}
// --------------------------------------------------------------------------------------------------------------------------
// INITIALISATION
// --------------------------------------------------------------------------------------------------------------------------
void setup() {
pinMode(enable, OUTPUT);
Serial.begin(9600);
initMoteur(nombreDePasParTour, 13,8,9,10,11);
pinMode(enable, OUTPUT);
pinMode(bobineA, OUTPUT);
pinMode(bobineB, OUTPUT);
pinMode(bobineA_barre, OUTPUT);
pinMode(bobineB_barre, OUTPUT);
//rend le moteur devient pilotable
digitalWrite(enable, HIGH);
//définit la vitesse du moteur
changerVitesse(60);
MsTimer2::set(nbInterruptions, avancerUnPas); // période (ms)
MsTimer2::start(); // active Timer2
// initialise le port serie
}
// --------------------------------------------------------------------------------------------------------------------------
// BOUCLE INFINIE
// --------------------------------------------------------------------------------------------------------------------------
void loop() {
//avance de 1 pas
//avancerUnPas();
}
On notera que j’ai cherché à ne pas utiliser d’attente passible bloquante. La boucle principal ne fait finalement rien !
Le système gère des « interruptions » à la manière de Raspbian. Lorsque qu’une interruption apparaît, elle est traitée aussitôt que possible.
En utilisant ce code avec mon Arduino et le shield j’ai réussi à faire tourner le moteur pas à pas sur une Arduino.
Théorie du GPIO de la Raspberry Pi
J’ai décidé de regarder de près les curieux connecteurs GPIO de ma framboise (Raspberry Pi).
En fait les ports GPIO sont directement reliés au processeur. Cela implique plusieurs choses :
- Ils sont très fragiles et on a vite fait d’en griller un de façon irrémédiable !
- Ils fonctionnent en 3,3V (et non 5V)
- Ils ont un courant TRES faible
- Ils sont susceptibles d’être utilisé pas plusieurs applications (en parallèles) et donc avoir un comportement inattendu
- Ils sont utilisables uniquement en mode administrateur
Il existe 8 ports GPIO (de quoi piloter 2 moteurs pas à pas).
Assemblage de 4 LED pilotées par la Raspberry
Attention, les connecteurs permettent de sortir une tension de 5V mais il faut absolument utiliser la sortie 3,3V (pin 2)
Les GPIO ne distinguent pas naturellement les Entrées et les Sorties. Il est donc possible de brancher des LEDs en Sortie (avec des résistances de 270 ohms) et des boutons poussoir en Entrées (avec une résistance de 10 Kilo ohms).
Si j’ai bien compris cette dernière valeur de résistance n’est pas très importante mais si elle est trop faible le circuit aura tendance à consommer du courant lorsqu’on sollicite le bouton poussoir…
Pour faire pus « propre » j’ai fabriqué une nappe à partir d’une nappe pour floppy de vieux PC mais il existe également des nappes spécialement conçues pour les ports GPIO !
Allumer les LEDs avec un script en Python
Dans ma version de système d’exploitation (Occidentalis (Adafruit)) je n’ai pas eu besoin d’installer Python… mais sinon :
sudo mkdir GPIO
cd GPIO
sudo wget http://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.4.2a.tar.gz
(la version peut changer)
sudo tar –zxvf GPIO-0.4.2a.tar.gz
cd RPi. GPIO-0.4.2
sudo python setup.py install
Pour tester il faut lancer python :
sudo python
importer la bibliothèque GPIO :
import RPi.GPIO as GPIO
demander à utiliser le mode board
GPIO.setmode(GPIO.BOARD)
connecter le port GPIO 0 (pin11) à la LED (+) (puis résistance et GND comme dans schéma)
ouvrir le port GPIO 11 en sortie
GPIO.setup(11, GPIO.OUT)
Controler la LED (avec True ou False)
GPIO.output(11, False)
pour quitter
exit()
Utilisation de la bibliothèque WiringPi en C
Les tests étant concluants, je poursuis en chercher à coder en C (et non en python, pratique pour les tests mais lent).
J’ai suivi le tuto https://projects.drogon.net/raspber...
Installation de Git (si vous ne l’avez pas déjà)
sudo apt-get install git-core
En cas d’erreur, saisir
sudo apt-get update sudo apt-get upgrade
Télécharger wiringPi à partir de GIT
git clone git://git.drogon.net/wiringPi
cd wiringPi
git pull origin
Installer le tout
cd wiringPi
./build
Programmation du pilote en C
Voici le code
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int vitesse = 10; //constante
// données sous forme de Triplets (led, etat, duree)
// led, Etat (1=On / 0=Off) , duree
uint8_t data [] =
{ //A=0, B=2, C=3, D=4 (init --> A b c d S - N - )
1, 1, 1, // on allume B --> A B c d S S N N
0, 0, 1, // on éteint A --> a B c d - S - N
3, 1, 1, // on allume C --> a B C d N S S N
1, 0, 1, // on éteint B --> a b C d N - S -
4, 1, 1, // on allume D --> a b C D N N S S
3, 0, 1, // on éteint C --> a b c D - N - S
0, 1, 1, // on allume A --> A b c D S N N S
4, 0, 1, // on éteint D --> A b c d S - N - (début de la séquence)
9, 9, 9, // marque de fin de la séquence
} ;
void testLed (int n) {
int led, t;
for (t = 0 ; t < n ; ++t) { // on fait clignoter n fois
for (led = 0 ; led < 8 ; ++led) { // on allume les led
pinMode (led, OUTPUT) ;
digitalWrite (led, 1) ;
}
delay (100) ;
for (led = 0 ; led < 8 ; ++led) { // on éteint les led
pinMode (led, OUTPUT) ;
digitalWrite (led, 0) ;
}
delay (100) ;
}
}
void init () {
int led;
testLed (3); //on vérifie que toutes les led s'allument
pinMode (0, OUTPUT) ;// on allume A
digitalWrite (0, 1) ;
for (led = 1 ; led < 8 ; ++led) { // on éteint les autres
pinMode (led, OUTPUT) ;
digitalWrite (led, 0) ;
}
delay (vitesse) ;
//résultat : on a --> A b c d S - N -
}
int main (void)
{
int dataPtr ;
int led, etat, duree ;
printf ("Raspberry Pi wiringPi test program\n") ;
if (wiringPiSetup () == -1) exit (1) ;
init();
dataPtr = 0 ;
for (;;)
{
led = data [dataPtr++] ; // LED
etat = data [dataPtr++] ; // State
duree = data [dataPtr++] ; // Duration (10ths)
if ((led + etat + duree) == 27) // = End marker (9 + 9 + 9)
{
dataPtr = 0 ;
continue ;
}
digitalWrite (led, etat) ;
delay (duree * vitesse) ;
}
return 0 ;
}
Ce code (test_led0.c) reprend les grandes lignes du code pour l’Arduino.
Pour le compiler, placez-le dans GPIO/wiringPi/examples
cd / GPIO/wiringPi/examples
Compiler
make test_led0
A présent il faut adapter le shield initialement fait pour l’Arduino.
- brancher les 2 Enable au +5V (pas besoin dans ce test)
- branchez la sortie +5V pour alimenter le CI L293D
- connectez les port GPIO 0,1,3 et 4 (j’ai grillé mon GPIO 2 ;( )
Lancer (exécuter) test_led0
sudo ./test_led0
IL TOURNE !
ici