Petit jeu avec Stepper.cpp, bonus

Bonjour,

La bibliothèque Stepper est largement utilisée depuis plusieurs années sans anicroches. Passionné par les pas à pas, je vous propose ce petit jeu avec la fonction Stepper::step celle qui permet d'avancer d'un ou plusieurs pas. J'utilise la version 1.1.3 (mais les fichiers sont spécifiés 1.1.1).

Première manche: un des commentaires de cette fonction présente une erreur (comme c'est un commentaire et que c'est dans le Stepper.cpp, cela ne se voit pas par 99,99% des utilisateurs). Quel est le commentaire faux et quel serait le bon commentaire. Pour laisser les autres chercher sans leur donner la solution, envoyez-moi exceptionnellement la solution par mail perso (clic sur mon avatar et clic sur message en haut à droite de la fenêtre qui s'ouvre).

Ont trouvé:

Le code de la fonction Stepper::step est le suivant:

/*
 * Moves the motor steps_to_move steps.  If the number is negative,
 * the motor moves in the reverse direction.
 */
void Stepper::step(int steps_to_move)
{
  int steps_left = abs(steps_to_move);  // how many steps to take

  // determine direction based on whether steps_to_mode is + or -:
  if (steps_to_move > 0) { this->direction = 1; }
  if (steps_to_move < 0) { this->direction = 0; }


  // decrement the number of steps, moving one step each time:
  while (steps_left > 0)
  {
    unsigned long now = micros();
    // move only if the appropriate delay has passed:
    if (now - this->last_step_time >= this->step_delay)
    {
      // get the timeStamp of when you stepped:
      this->last_step_time = now;
      // increment or decrement the step number,
      // depending on direction:
      if (this->direction == 1)
      {
        this->step_number++;
        if (this->step_number == this->number_of_steps) {
          this->step_number = 0;
        }
      }
      else
      {
        if (this->step_number == 0) {
          this->step_number = this->number_of_steps;
        }
        this->step_number--;
      }
      // decrement the steps left:
      steps_left--;
      // step the motor to step number 0, 1, ..., {3 or 10}
      if (this->pin_count == 5)
        stepMotor(this->step_number % 10);
      else
        stepMotor(this->step_number % 4);
    }
  }
}

Note:
this->direction est initialisé à 0, modifié que dans Stepper::step
this->last_step_time est initialisé à 0, modifié que dans Stepper::step
this->step_delay donne le temps d'attente en µs) entre 2 pas (voir Stepper::setSpeed)
this->number_of_steps est le nombre de pas du moteur
this->step_number est le N°Step comme indiqué ci-dessous, initialisé à 0, modifié que dans Stepper::step

 * The sequence of control signals for 5 phase, 5 control wires is as follows:
 *
 * Step C0 C1 C2 C3 C4
 *    1  0  1  1  0  1
 *    2  0  1  0  0  1
 *    3  0  1  0  1  1
 *    4  0  1  0  1  0
 *    5  1  1  0  1  0
 *    6  1  0  0  1  0
 *    7  1  0  1  1  0
 *    8  1  0  1  0  0
 *    9  1  0  1  0  1
 *   10  0  0  1  0  1
 *
 * The sequence of control signals for 4 control wires is as follows:
 *
 * Step C0 C1 C2 C3
 *    1  1  0  1  0
 *    2  0  1  1  0
 *    3  0  1  0  1
 *    4  1  0  0  1
 *
 * The sequence of controls signals for 2 control wires is as follows
 * (columns C1 and C2 from above):
 *
 * Step C0 C1
 *    1  0  1
 *    2  1  1
 *    3  1  0
 *    4  0  0

Éventuellement code complet de Stepper.cpp :

/*
 * Stepper.cpp - Stepper library for Wiring/Arduino - Version 1.1.0
 *
 * Original library        (0.1)   by Tom Igoe.
 * Two-wire modifications  (0.2)   by Sebastian Gassner
 * Combination version     (0.3)   by Tom Igoe and David Mellis
 * Bug fix for four-wire   (0.4)   by Tom Igoe, bug fix from Noah Shibley
 * High-speed stepping mod         by Eugene Kozlenko
 * Timer rollover fix              by Eugene Kozlenko
 * Five phase five wire    (1.1.0) by Ryan Orendorff
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *
 * Drives a unipolar, bipolar, or five phase stepper motor.
 *
 * When wiring multiple stepper motors to a microcontroller, you quickly run
 * out of output pins, with each motor requiring 4 connections.
 *
 * By making use of the fact that at any time two of the four motor coils are
 * the inverse of the other two, the number of control connections can be
 * reduced from 4 to 2 for the unipolar and bipolar motors.
 *
 * A slightly modified circuit around a Darlington transistor array or an
 * L293 H-bridge connects to only 2 microcontroler pins, inverts the signals
 * received, and delivers the 4 (2 plus 2 inverted ones) output signals
 * required for driving a stepper motor. Similarly the Arduino motor shields
 * 2 direction pins may be used.
 *
 * The sequence of control signals for 5 phase, 5 control wires is as follows:
 *
 * Step C0 C1 C2 C3 C4
 *    1  0  1  1  0  1
 *    2  0  1  0  0  1
 *    3  0  1  0  1  1
 *    4  0  1  0  1  0
 *    5  1  1  0  1  0
 *    6  1  0  0  1  0
 *    7  1  0  1  1  0
 *    8  1  0  1  0  0
 *    9  1  0  1  0  1
 *   10  0  0  1  0  1
 *
 * The sequence of control signals for 4 control wires is as follows:
 *
 * Step C0 C1 C2 C3
 *    1  1  0  1  0
 *    2  0  1  1  0
 *    3  0  1  0  1
 *    4  1  0  0  1
 *
 * The sequence of controls signals for 2 control wires is as follows
 * (columns C1 and C2 from above):
 *
 * Step C0 C1
 *    1  0  1
 *    2  1  1
 *    3  1  0
 *    4  0  0
 *
 * The circuits can be found at
 *
 * http://www.arduino.cc/en/Tutorial/Stepper
 */

#include "Arduino.h"
#include "Stepper.h"

/*
 * two-wire constructor.
 * Sets which wires should control the motor.
 */
Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2)
{
  this->step_number = 0;    // which step the motor is on
  this->direction = 0;      // motor direction
  this->last_step_time = 0; // time stamp in us of the last step taken
  this->number_of_steps = number_of_steps; // total number of steps for this motor

  // Arduino pins for the motor control connection:
  this->motor_pin_1 = motor_pin_1;
  this->motor_pin_2 = motor_pin_2;

  // setup the pins on the microcontroller:
  pinMode(this->motor_pin_1, OUTPUT);
  pinMode(this->motor_pin_2, OUTPUT);

  // When there are only 2 pins, set the others to 0:
  this->motor_pin_3 = 0;
  this->motor_pin_4 = 0;
  this->motor_pin_5 = 0;

  // pin_count is used by the stepMotor() method:
  this->pin_count = 2;
}


/*
 *   constructor for four-pin version
 *   Sets which wires should control the motor.
 */
Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2,
                                      int motor_pin_3, int motor_pin_4)
{
  this->step_number = 0;    // which step the motor is on
  this->direction = 0;      // motor direction
  this->last_step_time = 0; // time stamp in us of the last step taken
  this->number_of_steps = number_of_steps; // total number of steps for this motor

  // Arduino pins for the motor control connection:
  this->motor_pin_1 = motor_pin_1;
  this->motor_pin_2 = motor_pin_2;
  this->motor_pin_3 = motor_pin_3;
  this->motor_pin_4 = motor_pin_4;

  // setup the pins on the microcontroller:
  pinMode(this->motor_pin_1, OUTPUT);
  pinMode(this->motor_pin_2, OUTPUT);
  pinMode(this->motor_pin_3, OUTPUT);
  pinMode(this->motor_pin_4, OUTPUT);

  // When there are 4 pins, set the others to 0:
  this->motor_pin_5 = 0;

  // pin_count is used by the stepMotor() method:
  this->pin_count = 4;
}

/*
 *   constructor for five phase motor with five wires
 *   Sets which wires should control the motor.
 */
Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2,
                                      int motor_pin_3, int motor_pin_4,
                                      int motor_pin_5)
{
  this->step_number = 0;    // which step the motor is on
  this->direction = 0;      // motor direction
  this->last_step_time = 0; // time stamp in us of the last step taken
  this->number_of_steps = number_of_steps; // total number of steps for this motor

  // Arduino pins for the motor control connection:
  this->motor_pin_1 = motor_pin_1;
  this->motor_pin_2 = motor_pin_2;
  this->motor_pin_3 = motor_pin_3;
  this->motor_pin_4 = motor_pin_4;
  this->motor_pin_5 = motor_pin_5;

  // setup the pins on the microcontroller:
  pinMode(this->motor_pin_1, OUTPUT);
  pinMode(this->motor_pin_2, OUTPUT);
  pinMode(this->motor_pin_3, OUTPUT);
  pinMode(this->motor_pin_4, OUTPUT);
  pinMode(this->motor_pin_5, OUTPUT);

  // pin_count is used by the stepMotor() method:
  this->pin_count = 5;
}

/*
 * Sets the speed in revs per minute
 */
void Stepper::setSpeed(long whatSpeed)
{
  this->step_delay = 60L * 1000L * 1000L / this->number_of_steps / whatSpeed;
}

/*
 * Moves the motor steps_to_move steps.  If the number is negative,
 * the motor moves in the reverse direction.
 */
void Stepper::step(int steps_to_move)
{
  int steps_left = abs(steps_to_move);  // how many steps to take

  // determine direction based on whether steps_to_mode is + or -:
  if (steps_to_move > 0) { this->direction = 1; }
  if (steps_to_move < 0) { this->direction = 0; }


  // decrement the number of steps, moving one step each time:
  while (steps_left > 0)
  {
    unsigned long now = micros();
    // move only if the appropriate delay has passed:
    if (now - this->last_step_time >= this->step_delay)
    {
      // get the timeStamp of when you stepped:
      this->last_step_time = now;
      // increment or decrement the step number,
      // depending on direction:
      if (this->direction == 1)
      {
        this->step_number++;
        if (this->step_number == this->number_of_steps) {
          this->step_number = 0;
        }
      }
      else
      {
        if (this->step_number == 0) {
          this->step_number = this->number_of_steps;
        }
        this->step_number--;
      }
      // decrement the steps left:
      steps_left--;
      // step the motor to step number 0, 1, ..., {3 or 10}
      if (this->pin_count == 5)
        stepMotor(this->step_number % 10);
      else
        stepMotor(this->step_number % 4);
    }
  }
}

/*
 * Moves the motor forward or backwards.
 */
void Stepper::stepMotor(int thisStep)
{
  if (this->pin_count == 2) {
    switch (thisStep) {
      case 0:  // 01
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
      break;
      case 1:  // 11
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, HIGH);
      break;
      case 2:  // 10
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
      break;
      case 3:  // 00
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, LOW);
      break;
    }
  }
  if (this->pin_count == 4) {
    switch (thisStep) {
      case 0:  // 1010
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
      break;
      case 1:  // 0110
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
      break;
      case 2:  //0101
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
      break;
      case 3:  //1001
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
      break;
    }
  }

  if (this->pin_count == 5) {
    switch (thisStep) {
      case 0:  // 01101
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
        digitalWrite(motor_pin_5, HIGH);
        break;
      case 1:  // 01001
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, LOW);
        digitalWrite(motor_pin_5, HIGH);
        break;
      case 2:  // 01011
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
        digitalWrite(motor_pin_5, HIGH);
        break;
      case 3:  // 01010
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
        digitalWrite(motor_pin_5, LOW);
        break;
      case 4:  // 11010
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, HIGH);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
        digitalWrite(motor_pin_5, LOW);
        break;
      case 5:  // 10010
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, HIGH);
        digitalWrite(motor_pin_5, LOW);
        break;
      case 6:  // 10110
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, HIGH);
        digitalWrite(motor_pin_5, LOW);
        break;
      case 7:  // 10100
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
        digitalWrite(motor_pin_5, LOW);
        break;
      case 8:  // 10101
        digitalWrite(motor_pin_1, HIGH);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
        digitalWrite(motor_pin_5, HIGH);
        break;
      case 9:  // 00101
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, HIGH);
        digitalWrite(motor_pin_4, LOW);
        digitalWrite(motor_pin_5, HIGH);
        break;
    }
  }
}

/*
  version() returns the version of the library:
*/
int Stepper::version(void)
{
  return 5;
}

Deuxième manche: au démarrage, il se passe des trucs un peu bizarre. Avec le programme suivant:

#include "Stepper.h"

const int UN_TOUR = 48;

Stepper moteur = Stepper(UN_TOUR, 10, 11);

void setup()
{
}

void loop()
{
  delay(1000); 
  moteur.step(1);
}

(on peu difficilement faire plus simple), au démarrage, j'ai filmé:
‐ un pas en avant
‐ un pas en arrière
‐ tout rentre dans l'ordre, on a une successions de pas en avant

Pourquoi le deuxième pas est en arrière (c'est d'ailleurs le seul qui est en arrière)? Éventuellement, que faut-il faire pour ne pas avoir le pas en arrière?

Pour laisser les autres chercher sans leur donner la solution, envoyez-moi exceptionnellement la solution par mail perso (clic sur mon avatar et clic sur message en haut à droite de la fenêtre qui s'ouvre).

Ont trouvé:
<vide>

Note n°1: J'ai trouvé ce bug uniquement en lisant le code. Après j'ai passé du temps pour vérifier que j'avais bien compris et mettre le bug en évidence. Sans moteur, on peut donc trouver l'explication, comme je l'ai fait.

Note n°2: Voici la vidéo obtenue. J'ai pris un moteur 48 pas/tour parce qu'avec un 200 ou 2048 pas par tour le pas en arrière ne se voit pas.

Note n°3: En fait il fait
‐ DEUX pas en avant
‐ un pas en arrière
‐ tout rentre dans l'ordre, on a une successions de pas en avant
Mais c'est pas de la faute de la bibliothèque

Ce type de moteur est commandé avec 4 pas différents comme le montre la table de 4 valeurs:

 * Step C0 C1 C2 C3
 *    1  1  0  1  0
 *    2  0  1  1  0
 *    3  0  1  0  1
 *    4  1  0  0  1

ou

 * Step C0 C1
 *    1  0  1
 *    2  1  1
 *    3  1  0
 *    4  0  0

Il y a effectivement 4 pas N° de 1 à 4 (remarque: dans le code les pas sont numérotés 0 à 3)

Quand on arrête le moteur, il s'arrête sur n'importe quel pas, et à la la mise sous tension, on va démarrer toujours sur le même pas car le logiciel n'a aucun moyen de savoir quel est le pas qu'il y avait lors de l'arrêt (en plus il pourrait avoir tourné "à la main"). En conséquence à la mise sous tension, le moteur peut se "caler" sur le pas de démarrage et avancer, reculer ou ne pas bouger. Dans le cas de la vidéo, c'est un ou deux pas en avant, on ne voit pas bien.

Mais une fois sous tension, le logiciel fait un pas en arrière avant les pas en avant. Dans la bibliothèque que j'ai écrite, à la mise sous tension, le moteur peut avancer ou reculer, mais le premier vrai pas se fait dans le bon sens. Ici c'est le premier vrai pas qui va dans le mauvais sens.

En gros, ne pas tenir compte du premier pas apparent qui positionne le moteur. Si c'était cela, je n'aurais pas prétendu que pour moi, il y a un bug dans le programme.

Troisième et dernière manche: voici ce qu'il est possible de faire,avec le même setup (vide) et la même boucle loop que précédemment:

J'obtiens 6 pas en avant, un double pas en arrière. Pour moi il y a bien un bug, mais il n'y a guère que les possesseurs de ce moteur ci qui sont normalement concernés, mais comme il y a une démultiplication, il risquent de ne pas voir le défaut du pas en arrière.

Note 1: quand le moteur recule, il recule de 2 pas, mais il pourrait avancer aussi de 2 pas. Quand il est sur le pas n°3 et qu'on lui demande d'aller au pas n°1, il peut soit avancer soit reculer de 2 pas d'un coup.

Note 2: on peut aussi faire 4 pas en avant, un temps d'arrêt (le 4ème pas dure 2s au lieu d'une), ou 1 pas en avant, 1 pas en arrière...

D'où vient ce bug, comment le corriger...

Pour laisser les autres chercher sans leur donner la solution, envoyez-moi exceptionnellement la solution par mail perso (clic sur mon avatar et clic sur message en haut à droite de la fenêtre qui s'ouvre).

Question subsidiaire: Comment fait-on pour signaler ce bug?

Je ne pensais pas trouver encore un problème, mais j'ai relu le code dernièrement. Celui ci est plus mystérieux pour moi, je n'ai pas encore tout compris. J'ai l'impression d'une erreur de la bibliothèque qui se compense parfais par une erreur du compilateur...

Je voudrai faire tourner le moteur en "arrière" le plus longtemps possible, et j'ai donc écrit le programme:


const int UN_TOUR = 2048;

Stepper moteur = Stepper(UN_TOUR, A3, A1, A0, A2);

void setup()
{
  moteur.setSpeed(10); // 10tr/mn
}

void loop()
{
  moteur.step(-32768); // Environ 30 tours en une fois
}

Mon moteur ne tourne pas du tout. J'arrive à comprendre pourquoi (forcément, j'ai trouvé ce "bug" en lisant le code. Mais si je fais:

#include "Stepper.h"

const int UN_TOUR = 2048;

Stepper moteur = Stepper(UN_TOUR, A3, A1, A0, A2);

void setup()
{
  moteur.setSpeed(10); // 10tr/mn
}

void loop()
{
  moteur.step(327); // Environ 1/3 tour
  moteur.step(-32768); // Environ 30 tours en une fois
}

Le moteur avance bien d'1/3 tour puis recule d'environ 30 tours, en boucle, ce qui cette fois ci correspond bien à ce que je demande.

question: Y a-t-il un bug ou deux? Comment le savoir? Comment le signaler?