[RÉSOLU]Aide pour un programme signal PWM

Bonjour a tous,

Je voudrais réaliser un programme que quand j'appuie sur un bouton poussoir j'ai un signal PWM qui démarre progressivement et s'arrête progressivement.
Pour le signal PWM j'ai aucun problème le problème vient du bouton poussoir je sais pas comment le mettre dans mon programme.

Si quelqu'un pourrais m'aider merci

Voici le programme.

int ledpin = 9;
int luminosite = 0;
int variation = 5;

boolean etat = false;

void setup()
{
  pinMode (ledpin, OUTPUT);
}

void loop()
{
 
  analogWrite(ledpin, luminosite);
  luminosite = luminosite + variation;
  if ( luminosite == 0 || luminosite == 25){
    variation = - variation;
  }
    
    delay(1000);
}

Edit de Jean-François : merci de mettre les balises "code"

Bonjour et bienvenue parmis nous :wink:

Ma religion, c'est la machine d'état.
C'est une méthode pour gérer plusieurs taches en même temps sans se bloquer entre elles.

Je te suggère de connecter un bouton poussoir à la broche 8 de la façon suivante :

5V -----/\/\/\/\/\-----+------ broche 8
         Res ~4K7      |
                       |    -+-
                       +----* *-----GND

Quand le bouton n'est pas appuyé, digitalRead() renvoi HIGH, quand le bouton est appuyé digitalRead() renvoi LOW.

Le code devient quelque chose comme

int ledpin = 9;
int pushpin = 8;
int luminosite = 0;
int variation = 5;

unsigned long heure_dernier_changement = 0;

enum {
  REPOS,          // Attente pression poussoir
  EN_COURS     // gradateur up & down
} etat = REPOS;

void setup()
{
  pinMode (ledpin, OUTPUT);
  pinMode (pushpin, INPUT );
}

void loop()
{
  switch( etat )
  {
  case REPOS:
    // on attend le bouton
    if ( digitalRead( pushpin ) == LOW )
      etat = EN_COURS;
    break;
  case EN_COURS:
    if ( (millis() - heure_dernier_changement) > 1000 )
    {
      analogWrite(ledpin, luminosite);
      luminosite = luminosite + variation;
      if ( luminosite == 0 || luminosite == 25)
      {
        variation = - variation;
      }
      heure_dernier_changement = millis();
      break;
    }
}

Tu avais, je pense, commencé à prévoir le coup avec ta variable 'bool etat' que j'ai remplacé par un enum.
L'avantage c'est que avec l'enum tu donnes des noms en clair aux états de ta tache et c'est plus facile d'en rajouter ultérieurement.

J'ai aussi supprimé l'appel a delay() qui est une fonction bloquante.
Je préfère enregistrer la date du dernier événement et tester si le temps s'est écoulé.
Attention à la façon dont le test est écrit.
Il ne faut jamais écrire :

if ( (date_avant + TEMPS) > millis() )

mais

if ( (millis() - date_avant) > TEMPS) )

Car millis() reboucle et repasse pas 0 toutes les 49 jours environ.
C'est comme des cyclistes qui se courent après sur un vélodrome.
La distance ce le vélodrome part à 0, augmente jusqu'au km maxi puis repasse à 0.
Il faut calculer la distance entre le premier et le suiveur par une différence modulo la valeur maxi.
Ici c'est pareil sauf que le modulo est implicite parce qu'il est égal à la valeur max que peut prendre un "unsigned long".

Tu verras avec l'usage que c'est plus pratique de faire comme cela plutot qu'avec delay() car cela permet à loop() de tourner rapidement sans bloquer.
Dans le futur, tu pourras ainsi facilement ajouter d'ajouter d'autre tâches/traitements qui ne seront pas pénalisée par le rythme de ton gradateur... si elles aussi procèdent de même, elle ne pénaliseront pas le gradateur non plus.

Il faut apprendre à partager le temps sur Arduino car tu n'as pas de système multi-tâche.
Il faut donc que tous les tâches coopèrent entre elles pour accorder du temps aux autres sans le monopoliser pour soi.

Salut!, merci pour ta réponse très complète et motivante :slight_smile:

Je débute dans les arduinos comme tu as pu le voir je me doute. . .

Par contre je n'est pas comprit le système avec les heures et date_avant tu pourrais m'expliquer ou tu aurais un lien ou je pourrais trouver une explication ??

Mais il a un problème j'ai fait le montage j'appuie sur le BP la séquence démarre mais ne s'arrête jamais il faudrait une impulsion pour faire démarrer la séquence a chaque fois.

Un grand merci

  1. Gestion du temps

Je suis pas sur de quelle partie tu n'as pas compris alors je reprend au début

delay( MON_DELAI ); est une fonction bloquante.
C'est à dire que tu ne va rien faire d'autre que d'attendre. C'est balot.
Quand ton programme va devenir plus complexe, tu voudra faire d'autre choses.
Or avec delay() ton programme est bloqué à rien faire pendant une seconde pour rien, temps que les autres tâches (que tu rajouteras certainement plus tard) auraient bien besoin pour s'exécuter à leur tour.

L'idée est donc plutot que de se bloquer pendant 1 seconde dans delay() de regarder si 1 seconde s'est écoulée depuis la dernière action. Si ce n'est pas le cas, on ne fait rien et on continue à tourner dans loop(). Si 1 seconde s'est écoulée depuis la dernière action on fait la nouvelle action et on re-mémorise la date du moment pour la prochaine comparaison.

millis() est une fonction qui retourne le temps écoulé en millisecondes depuis le lancement du programme.
Il s'agit d'un compteur sur un "unsigned long" auquel la bibliothèque de base "core" de l'Arduino ajoute 1 toute les milliseconde.
Un entier "unsigned long" sur Arduino est un entier 32 bits non signé pouvant prendre les valeurs entre 0 et (2^32)-1 (2 puissance 32 moins 1 soit 4294967295).

Que ce passe t'il au bout de 49 jours et des pouillèmes quand le compteur atteint 4294967295 ?

=> Le compteur repasse à 0.

Supposons que tu mémorises comme dans mon code la date de la dernière action dans "heure_dernier_changement"
Et que tu teste si les 1 secondes se sont écoulées par

if ( millis() > (heure_dernier_changement + MON_DELAI) )

Ce code semble bon puisque tu regarde si l'heure de maintenant est plus grande que l'heure précédente + le délai.
Or que se passe t'il si au moment où tu fais

heure_dernier_changement = millis();[code]
la valeur de millis() renvoi 4294966296
Ton test deviens temporairement
[code] if ( millis() > (4294966296 + 1000) )

Mais comme on utilise des "unsigned long" limité à 4294967295 on obtient 4294966296 + 1000 = ...... 0 !
Donc le test devient :

 if ( millis() > (0) )

Donc on va considérer pratiquement immédiatement que la durée s'est écoulée et on va passer à l'étape suivante immédiatement, ce qui est faux.

Maintenant si on écrite le code ainsi

if ( (millis() - heure_dernier_changement) > MON_DELAI )

Le processeur va faire une difference entre 2 entiers non signés.
Le calcul se fait modulo 4294967296 (2^32) et donc la différence sera bonne.

C'est comme toi si tu regarde une horloge à aiguilles.

Tu as pris ton paracétamol (parce que l'Arduino çà donne mal à la tête) à 20H et tu dois attendre 6H pour la prochaine prise.
Si tu calcule l'heure de la prochaine prise c'est 20+6 = 26 modulo 24 = 2H du matin
Au bout d'un moment tu regardes l'horloge et tu vois marqué 20H10
Ah 20H10 > 2H donc je reprend un médoc et je calcule l'heure de la prochaine prise : 20h10 + 6H = 2h10
AU bout d'un moment tu regardes et tu vois 20h15.
20h15 c'est plus grand que 2h20, et au bout d'un moment tu finis a l'hopital :frowning:

Evidemment toi tu sais que 2h du matin c'est le jour suivant.

Mais si tu considère le calcul uniquement sur les valeurs ca ne marche pas !

Refais le même raisonnement avec un calcul de différence.

Tu te rappelle que t'a dernière prise était a 20H
Donc a 20h10 tu calcule le temps écoulé : 20h10 - 20H00 = 0h10 et tu ne fait pas de bétise
A 1h45 du matin, tu recommence : 01h45 - 20H00 = 5h45 parce que tu fais une différence MODULO 24H
Et a 2H01 tu trouve : 02H01 - 20H00 = 6H01 : ok, tu peux prendre un autre paracétamol parce que les explications de Barbudor c'est pas du gâteau.....

Compris ?

  1. Je vais pas me fatigué parce que c'est le week-end.
    Alors il va falloir que tu réfléchisses :smiley:
    Si tu veux que ça s’arrête après la décroissance, il faut que ton automate revienne dans l'état REPOS après la décroissance.
    Revois les tests effectués dans l'état EN_COURS pour déterminer la condition qui te fait revenir dans l'état REPOS.

Tic-tac, relevé des copies dans 1H.......
[/code][/code]

Je pense avoir comprit avec ta méthode on ne bloque pas le programme.

si j'ai bien comprit après ma décroissance donc après l'arrêt du signal je dois remettre un repos.

Je dois juste mettre le code "case REPOS;" non ?

non, tu dois écrire

etat = REPOS;

Voila ou je l'ai mise mais le problème cest que la je dois faire une impulsion pour ajouter une variation, j'espère que je me suis bien exprimer :confused:

[/quote]

int ledpin = 9;
int pushpin = 8;
int luminosite = 0;
int variation = 5;

unsigned long heure_dernier_changement = 0;

enum {
  REPOS,          // Attente pression poussoir
  EN_COURS     // gradateur up & down
} etat = REPOS;

void setup()
{
  pinMode (ledpin, OUTPUT);
  pinMode (pushpin, INPUT );
}

void loop()
{
  switch( etat )
  {
  case REPOS:
    // on attend le bouton
    if ( digitalRead( pushpin ) == LOW )
      etat = EN_COURS;
    break;
  case EN_COURS:
    if ( (millis() - heure_dernier_changement) > 1000 )
    {
      analogWrite(ledpin, luminosite);
      luminosite = luminosite + variation;
      if ( luminosite == 0 || luminosite == 25)
      {
        variation = - variation;
      }
      heure_dernier_changement = millis();
      break;
      
    }
    etat = REPOS;
}


}

[/quote]

Pas tout a fait cela.
Déjà mon break était mal placé.

Voici une proposition de modification

void loop()
{
  switch( etat )
  {
  //---------------------------------------------
  case REPOS:
    // on attend le bouton
    if ( digitalRead( pushpin ) == LOW )
    {
      etat = EN_COURS;
      // reinitialiser les variables pour etre sur
      luminosite = 0;
      variation = 5;
    }
    break;
  //---------------------------------------------
  case EN_COURS:
    if ( (millis() - heure_dernier_changement) > 1000 )
    {
      analogWrite(ledpin, luminosite);
      if ( variation > 0 )
      {
        luminosite = luminosite + variation;
        if ( luminosite == 25)
          variation = - variation;
      }
      else
      {
        if ( luminosite == 0 )
          etat = REPOS;
        else        
          luminosite = luminosite + variation;
      }
      heure_dernier_changement = millis();
    }
    break;
  //---------------------------------------------
  }  // switch
} // loop

Comme tu le vois, le code de EN_COURS commence a s'alourdir avec des tests imbriqués un peu compliqués.
On peut prendre avantage de la notion d'automate en renommant EN_COURS comme UP et ajoutant un nouvel état DOWN :

enum {
  REPOS,          // Attente pression poussoir
  START,         // 1ere etape  
  UP,               // gradateur up
  DOWN           // et down
} etat = REPOS;

void loop()
{
  switch( etat )
  {
  //---------------------------------------------
  case REPOS:
    // on attend le bouton
    if ( digitalRead( pushpin ) == LOW )
      etat = START;
    // pas de break : on enchaîne directement sur START sans repasser par loop();
  //---------------------------------------------
  case START:
    // 1ere étape du gradateur avec luminosite = 0
    analogWrite(ledpin, luminosite);
    // on continue après avec UP
    etat = UP;
    // on mémorise l'heure
    heure_dernier_changement = millis();
    break;
  //---------------------------------------------
  case UP:
    if ( (millis() - heure_dernier_changement) > 1000 )
    {
      // maintenant je pré-calcule la valeur avant de l'écrire
      luminosite = luminosite + variation;
      analogWrite(ledpin, luminosite);
      // si la dernière écriture était 0, on passe en DOWN
      if ( luminosite == 25)
          etat = DOWN ;
      // on mémorise l'heure
      heure_dernier_changement = millis();
    }
    break;
  //---------------------------------------------
  case DOWN:
    if ( (millis() - heure_dernier_changement) > 1000 )
    {
      luminosite = luminosite - variation;  // note que je change le signe ici plutot que la valeur
      analogWrite(ledpin, luminosite);
      // si la dernière écriture était 0, on passe en REPOS
      if ( luminosite == 0)
          etat = REPOS ;
      // on mémorise l'heure
      heure_dernier_changement = millis();
    }
    break;
  //---------------------------------------------
  }  // switch
} // loop

Ce code garantit parfaitement que l'on passe par 0 ... 25 ... 0

Voila le programme rentré, sa fonctionne pas la séquence démarre toute seule et ne s'arrête jamais. je pense qu'il a un problème au niveau " if ( digitalRead( pushpin ) == LOW )" je chipote mais je n'arrive a rien :confused:

int ledpin = 9;
int pushpin = 8;
int luminosite = 0;
int variation = 5;

unsigned long heure_dernier_changement = 0;

enum {
REPOS, // Attente pression poussoir
START, // 1ere etape
UP, // gradateur up
DOWN // et down
} etat = REPOS;
void setup()
{
pinMode (ledpin, OUTPUT);
pinMode (pushpin, INPUT );
}

void loop()
{
switch( etat )
{
//---------------------------------------------
case REPOS:
// on attend le bouton
if ( digitalRead( pushpin ) == LOW )
etat = START;
// pas de break : on enchaîne directement sur START sans repasser par loop();
//---------------------------------------------
case START:
// 1ere étape du gradateur avec luminosite = 0
analogWrite(ledpin, luminosite);
// on continue après avec UP
etat = UP;
// on mémorise l'heure
heure_dernier_changement = millis();
break;
//---------------------------------------------
case UP:
if ( (millis() - heure_dernier_changement) > 1000 )
{
// maintenant je pré-calcule la valeur avant de l'écrire
luminosite = luminosite + variation;
analogWrite(ledpin, luminosite);
// si la dernière écriture était 0, on passe en DOWN
if ( luminosite == 25)
etat = DOWN ;
// on mémorise l'heure
heure_dernier_changement = millis();
}
break;
//---------------------------------------------
case DOWN:
if ( (millis() - heure_dernier_changement) > 1000 )
{
luminosite = luminosite - variation; // note que je change le signe ici plutot que la valeur
analogWrite(ledpin, luminosite);
// si la dernière écriture était 0, on passe en REPOS
if ( luminosite == 0)
etat = REPOS ;
// on mémorise l'heure
heure_dernier_changement = millis();
}
break;
//---------------------------------------------
} // switch
} // loop

TU as raison, j'ai réfléchit trop vite
Il faut le break sinon on enchaine à tous les coups sur le code de START

  //---------------------------------------------
  case REPOS:
    // on attend le bouton
    if ( digitalRead( pushpin ) == LOW )
      etat = START;
    break;
  //---------------------------------------------

D'ailleurs c'est ce que dans ma grande bétise j'avais écrit sans m'en rendre compte :blush:

Super ça fonctionne !!

Une petite dernière pour la route, comme je te l'ai dit je débute avec les arduinos, donc surement une questions stupide.

Je voudrais aussi que avec la même impulsion un relais change d'état avec le même bouton poussoir,
je peut mettre le code a la suite de tout ce qui a déjà ça n'a pas t'importance ?

Merci

Soit tu considère que ta sortie relai est une sortie de la même tâche que celle qui joue le gradateur sur les leds.
Par exemple tu considère que le relai se ferme au moment où tu commence le cycle UP et s'ouvre au moment où tu retourne au repos.

.......
  //---------------------------------------------
  case START:
    // 1ere étape du gradateur avec luminosite = 0
    analogWrite(ledpin, luminosite);
    // on continue après avec UP
    etat = UP;
    // fermeture du relai
    digitalWrite( pin_relai, HIGH );                                        // <<<< FERMETURE RELAI
    // on mémorise l'heure
    heure_dernier_changement = millis();
    break;
  //---------------------------------------------
.....
  //---------------------------------------------
  case DOWN:
    if ( (millis() - heure_dernier_changement) > 1000 )
    {
      luminosite = luminosite - variation;  // note que je change le signe ici plutot que la valeur
      analogWrite(ledpin, luminosite);
      // si la dernière écriture était 0, on passe en REPOS
      if ( luminosite == 0)
      {
          etat = REPOS ;
          // ouverture du relai
          digitalWrite( pin_relai, LOW );                                        // <<<< OUVERTURE RELAI
      }
      // on mémorise l'heure
      heure_dernier_changement = millis();
    }
    break;
  //---------------------------------------------
  }  // switch

Soit le relai doit vivre sa vie de manière différente. Par exemple, il démarre bien avec le bouton poussoir comme l'automate des leds mais il ne reste fermé que 300ms par exemple.

Dans ce cas, il vaut mieux créer un nouvel automate ce qui veut dire :
Nouvelle variable d'état enum { REPOS_RELAI, START_RELAI } etat_relai;
Et un switch différent : switch( etat_relai )

Si tu choisit cette option, pour commencer à ajouter un peu de lisibilité dans le code, je te suggère de commencer à créer des sous fonctions telles que

  • void automate_led() qui contiendras le code du switch sur la variable d'état de la led (a renommer peut être etat_led pour lever l'ambiguïté)
  • void automate_relai() idem pour le switch sur etat_relai.
    La fonction loop devient alors
void loop()
{
  automate_led();
  automate_relai();
}

Ca devient un exemple typique de la bonne idée d'écrire les tâches sous la forme de machines d'états non bloquantes.

Au fait tu sais faire pour piloter un relai par Arduino ?
On ne branche pas un relai en direct sur un broche de l'arduino surtout ci ce n'est pas un relai 5V.
On utilise une résistance qui attaque la base d'un transistor en collecteur ouvert e ton n'oublie pas la diode de roue libre;
Voir page 27 du 1er document indiqué par Osaka dans ce post : http://arduino.cc/forum/index.php/topic,102618.0.html
Suivant le relai, tu n'est pas obligé d'aller chercher un TIP120. Un transistor plus petit peut suffir.

Quelles infos as tu sur ton relai ?
Tension de commande ?
Impédance de la bobine ou courant de commande ?
Est-ce que tu as déjà des transistors qui trainent chez toi ?

Salut,

Je vais t'expliquer clairement a quoi va servir le montage. Comme ça tu sera tout :stuck_out_tongue:

Il va servir a faire démarrer et arrêter un train de modélisme, sur deux mètres de vois il aura un capteur pour le faire démarrer( c'est ce qui donne l'impulsion) et le relais doit changer a chaque passsage de ce capteur, pour le faire aller dans un sens puis dans l'autre.

Oui ne t'inquiète pas je sais comment on branche le relais, transistor etc, je suis étudiant dans l'électronique je chipote dedans depuis mes 12ans, mais en programmation je débute, je suis de formation sur les automate programmable de Siemens mais c'est pas du tout le même ...

Merci

Je suis pas sur d'avoir compris.
Donc je ne suis pas sur de voir comment t'aider.
Tu peux faire un petit dessin très schématique du principe ?
Le train passe dessus le capteur et là le relai change ?
Mais comment le relai change t'il de nouveau ? Si le train repart dans l'autre sens, comment le train repasse sur le capteur ?

C'est pas clair dans ma tête...

Salut,
Voici un schéma j'ai oublier de préciser un truc il manque de tempo, j'explique
on lance le cycle le train passe sur le bp2 il faudrait une tempo de 5 seconde pour laisser le train s'arrêter ensuite le relais change de position donc le train repart dans l'autre sens et le signal PWM commence augmente puis diminue.
Le train passe sur le bp1 la même chose tempo de 5 secondes pour laisser le train s'arrêter ensuite le relais change de position pour permettre au train de repartir dans l'autre sens et le sigbal PWM commence augment et diminue.

Lien de l'image en grand... : http://img684.imageshack.us/img684/2434/trainrk.png

Ok.
Donc tu as bien définit les étapes.
Donc essaye de traduire cela sous la forme d'un automate

  • les états
  • que fais tu dans chaque état
  • quand et pourquoi passes tu d'un état à un autre

Coté électronique, tes transistors sont mals cablés.
L'emetteur d'un NPN est à relier à la masse et le collecteur vers la charge.
Il faut une résistance (environ 220 à 470 ohms) dans entre la sortie Arduino et la base sous peine de cramer la sortie de l'arduino.

Voila je pense que c'est ce que tu ma démander

Le train passe devant le capteur n°2
tempo de 5 secondes pour laisser s'arrêter le train avec l'inertie
Le relais change de possition car le train dois repartir de l'autre coter
dépard générateur PWM
le train passe devant le capteur n°1
arrêt du signal PWM et tempo de 5 secondes
Changement de l'état du relais
dépard du générateur PWM

Oui mais si le signal PWM descend à 0, le train s'arrête.
D'ailleurs, le train s’arrêtera même avant d'atteindre 0. A mon avis en dessous de

Est-ce que tu ne souhaites pas plutôt être à pleine vitesse entre les 2 capteurs et ralentir du maximum à 0 jusqu'à la position d'arrêt.
De là, inverser le sens, accélérer, dépasser le 1er capteur jusqu'au 2eme capteur à partir duquel tu décélères jusqu'à l’arrêt, etc ...

Ça me semble plus logique, mais tu connais mieux ton circuit ferroviaire que moi.

Comment sont fait les capteurs ?
Est-ce que c'est le passage des roues ? ou de l'avant ou de l'arrière ? qui déclenche le capteur ?

Oui cela peut être une solution de faire comme sa, alors les capteur normalement ce sont des capteurs REED sur le train a l'avant ou au début je ne sais pas encore il aura un aiment coller a celui ci, il faudrait une place bien précise ?

Non pas vraiment.
Après il faut juste les placer correctement.

Sinon est-ce que ma suggestion de séquence correspond.

Je suggère de définir les états suivants :

  • DE_1_VERS_2 : le train roule a fond de 1 vers 2
  • RALENTIT_EN_2 : le train a atteint le capteur 2 et ralentit jusqu'à l'arrêt
  • CHANGE_DIR_2_VERS_1 : changer le relai pour aller de 2 vers 1
  • ACCELERE_2_VERS_1 : le train accélère jusqu'au maximum
  • DE_2_VERS_1 : le train roule a fond vers 1
  • RALENTIT_EN_1 : le train a atteint le capteur 1 et ralentit jusqu'à l’arrêt
  • CHANGE_DIR_1_VERS_2 : changer le relai pour aller de 1 vers 2
  • ACCELERE_1_VERS_2 : le train accélère jusqu'au maximum

Si cela correspond essaye d'écrire le code de l'automate.