[Résolu]Coexistence de tone() et analogWrite()

Bonjour,

Avec un arduino "nano" je souhaite faire émettre simultanément un signal PWM avec la sortie D3 et un signal carré avec la sortie A3 (mode digital)
Tout se passe bien dans le cas d'émission séparées, pas dans le cas d'émission simultanées. (sans doute que le même timer2 est utilisé pour les deux cas.

Pour info :
... le réglage se fait grâce à une conversion A/D et un potentiomètre relié à l'entrée A2; (réglage du rapport cyclique du PWM ou réglage de la fréquence pour le signal carré); la fréquence PWM étant par ailleurs changée par l'intermédiaire de modifications des bits de pré division sur le registre TCCR2B....

  while (!TOUCHE(BpSuite))
     {ValeurPot=analogRead(PotPin);
      lcd.setCursor(0,1);lcd.print(ValeurPot>>2);lcd.print(" ");
      lcd.setCursor(6,1);lcd.print((ValeurPot*10)+40);lcd.print(" ");
      tone(TonePin,(ValeurPot*10)+40);delay(2000); noTone(TonePin);delay(500);
      analogWrite(PwmPin,ValeurPot>>2);// fréquence PWM vers 500 Hz
      delay(200);
       if (TOUCHE(BpChange))// changement de fréquence
         {BUZZER(10);
          TCCR2B=TCCR2B&0XF8;// blocage prescaler timer
          TCCR2B=TCCR2B|0X03;//vers 1kHz
         }
     }
  TCCR2B=TCCR2B&0XF8;// blocage PWM pour la sortie
  BUZZER(10);lcd.clear(); 
}

Mes questions aux spécialistes :

-1 Quelle serait la solution pour émettre simultanément le signal PWM et le signal "carré" en conservant le même PWM de la sortie 3 si possible, et aurais-je le même problème avec l'autre mode PWM ?

  • Comment faire pour stopper l'émission PWM de façon plus élégante ? (sans bloquer la pré division du timer2 avec TCCR2B)

J'ai bien conscience d'aborder un sujet un peu "pénible" et je remercie par avance ceux qui pourront m'aider.

Serge .D

sans doute que le même timer2

Bonjour,
bingo !

la bibliothèque tone utilise les timers dans l'ordre suivant, (au début de la bibliothèque) :
const uint8_t PROGMEM tone_pin_to_timer_PGM = { 2, 1, 0 };

tu peux faire un test en changeant l'ordre, ce qui libère ton timer 2
const uint8_t PROGMEM tone_pin_to_timer_PGM = { 1, 2, 0 };

je ne réponds pas à ta 2ème question, car on est déjà profondément dans l’inélégance en écrivant directement dans les registres et en retotochant les bibliothèques ...

Merci beaucoup pour les tuyaux, (et les remarques que j'attendais un peu)

  • Bon, OK, pour le blocage du signal PWM, le plus simple et le moins tendancieux est sans doute un analogWrite(PwmPin,0)
  • Pour le reste, je pourrais éventuellement utiliser les sorties PWM correspondant au timer 0 ..... sauf que ce n'est pas le chemin le plus facile....et qu'alors je risque des effets de bords si j'utilise millis() ou delay()

Merci encore.

Serge .D

Bon, OK, pour le blocage du signal PWM, le plus simple et le moins tendancieux est sans doute un analogWrite(PwmPin,0)

Non pas d'accord.

La LOI ce n'est pas arduino c'est ATMEL.
Heureusement pour Atmel qu'il n'a pas attendu l'arrivée d'arduino, pour programmer ses micros (d'ailleurs l'original c'est Wiring, arduino c'est de la copie).
Atmel dit que pour mettre en service un timer il faut lui fournir une horloge et que pour l'arréter il faut bloquer l'horloge.
Donc tu faisais très bien en écrivant dans le bon registre.
Que font d'autres les fonctions Wiring/arduino : rien elles écrivent dans les registres sauf que tu ne le vois pas.

Ensuite faire ce que tu dis ne bloquera nullement le timer qui est lancé automatiquement par analogWrite.
Tu envera seulement un signal constant avec possibilité d'impulsions parasites (glitch).

Dans ton cas tu ne maîtrise pas bien les timers utilisés : ce sont les fonctions Wiring/arduino qui décident en grande partie.
C'est une limite du système Wiring/arduino. Et quand le projet atteint les limites il faut les contourner.

La PWM se gère facilement avec les registres.
Je me rappelle l'avoir essayé et en comparant avec la fonction analogWrite j'ai eu la surprise de constater à l'oscillo qu'avec les registres le changement de rapport cyclique était instantané et parfaitement propre alors qu'avec la fonction analogWrite le signal balbutie, hoquette et bégaie pendant plusieurs périodes de PMW (au moins une dizaine).

Toujours avec les registres la génération d'un signal périodique se fait en utilisant un timer en mode CTC.

En faisant ainsi c'est toi et toi seul qui choisi le ou les timers utilisés.

à aligote,
si tu veux bien fais l'essai en modifiant la bibliothèque tone comme je l'ai suggéré

Bonsoir et merci.

J'ai bien tout noté :

Ensuite faire ce que tu dis ne bloquera nullement le timer qui est lancé automatiquement par analogWrite.
Tu envera seulement un signal constant avec possibilité d'impulsions parasites (glitch).

Je m'en doutais.

Toujours avec les registres la génération d'un signal périodique se fait en utilisant un timer en mode CTC.

je vais m'en occuper si je veux éviter tone()

à aligote,
si tu veux bien fais l'essai en modifiant la bibliothèque tone comme je l'ai suggéré

Là aussi je vais essayer.

Pour l'instant mon projet est presque entièrement abouti, c'est un appareil d'atelier pour mesurer, envoyer les données sur RS232 vers Windows tout en stockant sur SD.

les grandeurs mesurées sont la tension, l'intensité ( à partir de la sortie analogique de ma pince ampère métrique) et la température. (destination mesures pour l'aéromodélisme électrique)
Comme il me restait environ 7 à 8kO de mémoire programme libre, je voulais l'occuper à greffer un petit générateur de signaux "basiques".

Perso, je suis plus "électrique qu'informatique"

A la suite de vos remarques constructives qui m'orientent, j'aurais aussi les éléments pour filtrer les infos abondantes disponibles sur le Net.

Merci

Serge .D

ReBonjour,

Bon, je commence à mieux voir ce qui se dessine :

  • Je ne dispose que de 3 timers (Atmel 328) : timer0, timer1 et timer2
  • Si je veux gérer simultanément tone() et un signal PWM il faut que ces fonctions soient controlées par des timers différents
  • Si de plus je ne veux pas perturber le fonctionnement arduino, il faut que les autres fonctions que je compte employer ne perturbent pas les timers retenus pour le PWM et pour tone()

Alors je n'ai pas beaucoup de solutions, voila ce que je retiens :

  • Je ne touche pas à timer0 qui est prévu pour millis() et PWM bornes 5 et 6; je pourrai donc continuer à utiliser delay() et millis()
  • Je ne touche pas à timer2 car il est employé par tone()
  • Je réserve timer1 pour mon signal PWM ..... en conséquence mon signal PWM ne pourra être sorti que par les bornes 9 et 10 et je m'interdit l'usage de la bibliothèque servo qui l'utiliserais.

La procédure serait la suivante :

  • Réserver analogWrite pour les sorties 9 ou 10; comme je compte utiliser le bus SPI pour ma carte SD; alors j'ai besoin des bornes 10,11,12 et 13.
    En conséquence je dois impérativement mettre ma sortie PWM à la borne 9; (Sortie OC1A pour le timer1)
    Pour l'instant j'avais prévu la sortie 3 car la sortie 9 était utilisée par mon afficheur LCD.
    J'ai l'obligation de décaler les bornes pour mon LCD afin de libérer la borne 9 pour le signal PWM issu du timer1.
  • Affecter tone() à une borne quelconque libre

il y a encore une question dont je ne suis pas sur de la réponse :

Pour le fonctionnement du bus SPI, un timer est-il nécessaire ou bien la fonction est directement totalement câblée dans le µC (ce que je pense)
Il faut que je regarde le datasheet ....

Si tout va bien, mon problème va se résoudre avec les seules fonctions arduino, je n'aurai à toucher que les bits de prédivision pour les registres de contrôle du timer1 dans le but de changer la fréquence PWM.

Serge .D

Bonjour,
le spi n'a pas besoin de timer pour fonctionner
par contre, les pins clk, mosi, et miso sont obligatoires
à la rigueur on peut faire un spi logiciel avec des pins quelquonques

Bonjour,

Voilà, tout fonctionne comme je le souhaitais.

Je vous remercie tous pour vos contributions qui m’ont permis d’atteindre mon objectif.

Ci dessous le code de la fonction qui met en œuvre la générations des signaux.

Serge .D

void SIGNAUX()

{ static int ValeurTone=0; static int ValeurPwm=0; static byte  Select=Choix1; static byte Masque=3;
  static boolean Tonalite=false; static boolean Pwm=false; byte i=0;
  
  TCCR1B=TCCR1B|Masque;// masque pour prédivison de fréquence
  BUZZER(10);lcd.clear();delay(600);
      // Affichage ligne sup de Menu
  lcd.print("%Pwm");lcd.setCursor(5,0);lcd.print("Frect");
  lcd.setCursor(11,0);lcd.print("Fpwm");
  delay(400);
  while (!TOUCHE(BpSuite))
     {ValeurTone=analogRead(PotTonePin);ValeurPwm=analogRead(PotPwmPin);
     //******* Affichage ligne inf de Menu *********
      lcd.setCursor(0,1);
      if (Pwm)lcd.print(0.0977*ValeurPwm,0);
             else lcd.print("***");
      lcd.print(" ");
      lcd.setCursor(5,1);
      if (Tonalite) lcd.print(0.01*(ValeurTone+4));
             else lcd.print("***");
      lcd.print(" ");    
      // Affichage fréquence Pwm selon bits TCCR1B
      lcd.setCursor(11,1);
      switch (Masque)
          {case 0 :{lcd.print(" -0- ");break;}
           case 1 :{lcd.print("31,3k");break;}
           case 2 :{lcd.print("3,92k");break;}
           case 3 :{lcd.print("490  ");break;}
           case 4 :{lcd.print("122  ");break;}
           case 5 :{lcd.print("30,7 ");break;}
          default :  break;  
          }
// ******* clignotement de la sélection une fois sur 2 **    
      if (i%2==0)// 
          {lcd.setCursor(Select*5,1);delay(50);lcd.print("     ");delay(150);}        
// ******** Activation d'un Menu ***********************         
      if(TOUCHE(BpChange))
         { BUZZER(10);delay(2);
          // Acquisition rapport cyclique Pwm et fréquence signal carré       
           ValeurTone=analogRead(PotTonePin);ValeurPwm=analogRead(PotPwmPin);
           // Activation du Menu sélectionné
           if (Select==Choix1)
              {Pwm=!Pwm;// Bascule Pwm /pas Pwm
               if (Pwm) analogWrite(PwmPin,ValeurPwm>>2);
                   else analogWrite(PwmPin,0);
              }
           if (Select==Choix2) 
              { Tonalite=!Tonalite; // Bascule Tone() /pas Tone()    
               if (Tonalite) tone(TonePin,(ValeurTone*10)+40);
                   else noTone(TonePin);
              }       
           if (Select==Choix3)
           { BUZZER(100); // Sélection fréquence Pwm : Masque=0 blocage Pwm
             if (Masque>0) Masque=Masque--; else Masque=5; 
             TCCR1B=TCCR1B&0XF8;TCCR1B=TCCR1B|Masque;              
           }
         delay(200);         
       }
       // ********* Changement de sélection 
      if (TOUCHE(BpHorz))
           {BUZZER(10);
            if (Select<Choix3) Select++; else Select=Choix1;
            delay(200);
           }         
    i++; 
  }
  // Sortie fonction SIGNAUX()
  analogWrite(PwmPin,0);//mettre niveau bas signal Pwm
  TCCR1B=TCCR1B&0XF8;noTone(TonePin);//bloquer horloge timer1 et tone()
  delay(200);BUZZER(10);lcd.clear(); 
}

Bonjour,
bravo, et merci pour le retour !