PB avec frequence variable avec Arduino Micro

Bonjour,

Je veux générer deux fréquences sur mon Arduino Micro :

L'une variable, j'ai trouvé se code qui fonctionne très bien pour les fréquence élevés entre 1 et 12KHz.

Programmer un signal PWM normal (lent), symétrique et non inversé sur la sortie OC1B/PB6 (timer 1/comparateur B) correspondant à la pin 10 ,
les valeurs peuvent être :
- configuration sortie : DDRB[6] = 1
- prédiviseur : CS1[2:0] = 1 (f/1)
- mode : WGM1[3:0] = 8 ("PWM, Phase and Frequency Correct" avec ICR1)
- fréquence : ICR1 = 800 (fout = 16MHz/(2·prédiviseur·ICR1) = 10 kHz)
- polarité : COM1B[1:0] = 2 (signal non inversé)
- rapport cyclique : OCR1B (relativement à la valeur de ICR1)

*/

 int frequence ;    
 int freq1 ;        // fréquence = 8 kHz (800 pour 10KHz)

unsigned int  dutycycl ; // rapport cyclique rel. icr1

void setup() {

  Serial.begin(9600);  // initialize the serial 1 communication
  // pinMode(10, OUTPUT); 
 
  // OC1A et OC1C inutilisées
  unsigned char com1A = 0;
  unsigned char com1C = 0;
  
  // 8 kHz sur sortie 10 Micro (OC1B/PB6)
  unsigned char cs1   = 1;   // prédiviseur = f/1    
  unsigned char mode1 = 8;   // mode = 8 
  unsigned char com1B = 2;   // signal non inversé
  

  unsigned int  freq = 1000 ; // fréquence = (1000 pour 8 kHz et 800 pour 10KHz)
 
  unsigned int  dutycycl = freq/2 ; // rapport cyclique rel. icr1

// check if data has been sent from the computer:

  DDRB |= 1<<6;
  TCCR1A = (com1A<<6)|(com1B<<4)|(com1C<<2)|(mode1&3);
  TCCR1B = ((mode1>>2)<<3)|cs1;
  ICR1 = freq;
  OCR1B = dutycycl;

}

void loop() {
  if (Serial.available()) {
    // read the most recent byte (which will be from 0 to 255)
    freq1 = Serial.parseInt();
    frequence = (16000/(2*1*freq1));
    // set the brightness of the LED:
    // analogWrite(ledPin, frequence);
  Serial.print("Saisir la valeur de la frequence en KHz:");
  Serial.println(freq1);
 
     ICR1  = frequence ;
     OCR1B = frequence/2 ;
  }
}

Une seconde très faible de l'autre d'environ 20Hz à 80Hz, fixe pas besoin de la varié pendant exécution.

J'arrive en modifiant le prédiviseur : CS1[2:0] = 1 (f/1),

  unsigned char cs1   = 4;   // prédiviseur = f/1

Mais le souci c'est que les deux fonctionnes mais sur la même pin 10 Arduino Micro.

Ne n'arrive pas à modifier les registres pour utiliser par exemple pin 6.

ps : les pins 2, 3, 7 et 12 sont déjà utilisés.

Si quelqu'un peux m'aider à avoir une freq. fixe faible sur la pin 6 ou 5, j'ai essayé avec la fonction
void setPwmFrequency(int pin, int divisor)

http://playground.arduino.cc/Code/PwmFrequency

Mais y je pense un "conflit" car il ecrit déjà au dessus sur :

      TCCR0B = TCCR0B & 0b11111000 | mode;      // pour les pins 5 ,6

      TCCR1B = TCCR1B & 0b11111000 | mode;      // pour les pins 9 ,10

      TCCR1B = TCCR1B & 0b11111000 | mode;      // pour les pins 9 ,10

Sinon ( au pire ) si je peux avoir deux sorties avec une de Freq = 30Hz et 8KHz sur deux broches je suis preneur.

Merci d'avance pour votre aide...

Pour quelques dizaines d'hertz vous pouvez sans doute générer cela sans problème et sans timers particulier depuis la loop() sur la pin de votre choix avec un test du temps écoulé (et la fonction millis() ou micros() )

(Lisez un tutos sur les timers et le PWM et les pins pilotables)

La fonctions milis et micros crée un blocage dans le code, aussi j'ai lu que lorsqu'on utiles les tirmes il est déconseiller d'utiliser ses fonctions ...

bellahcene:
La fonctions milis et micros crée un blocage dans le code, aussi j'ai lu que lorsqu'on utiles les tirmes il est déconseiller d'utiliser ses fonctions ...

non c'est la fonction delay() qui crée un blocage. millis() ou micros() vous donnent juste une mesure du temps qui passe

voici un exemple pour faire clignoter la LED (pin 13 sur un UNO) à une certaine fréquence

const unsigned long frequence = 10; // 10 Hz
const unsigned long demiPeriode = (500000 / frequence ); // 500000 =>  1000000 (µs) / 2


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}


// Peut être largement optimisée, écrit comme cela pour la lisibilité
// mais pour quelques dizaines d'Hertz cela fonctionnera sans problème
// si vous ne voulez pas une super précision

void genererFrequence()
{
  static unsigned long topChrono = 0;
  static boolean ledLOW = true;

  if (micros() - topChrono >= demiPeriode) {
    if (ledLOW) {
      digitalWrite(LED_BUILTIN, HIGH);
      ledLOW = false;
    } else {
      digitalWrite(LED_BUILTIN, LOW);
      ledLOW = true;
    }
    topChrono += demiPeriode;
  }
}


void loop() {
  genererFrequence();
  // ici faire autre chose de pas "trop long" par rapport à la demi période
}

Bonjour,

J'ai essayé cette méthode mais pas assez précise, néanmoins elle ma permis de faire avancé mon code.
Je souhaite utilisé une frequence entre 8khz et une autre à 32Hz.
Sur deux broche distincte de arduino micro.

J'ai essayé avec le TimerThree, mais en faite celui ci utilise la pin 10.

Donc pour avoir 8Khz sur les autres pin sur micro, je trouve pas...

Avec un micro 32U2 (Léonardo/Micro) Il reste 3 timers de libres.
Et avec ceux là tu peux faire ce que tu veux sauf s'ils sont déjà pris par un bibliothèque.

Dans l'architecture avr chaque timer peut contrôler DEUX pins et non pas une seule --> voir datasheet du micro.

Bonjour,

Je suis dans la data sheet mais j'ai beaucoup de mal à trouver, elle parle de registre et non de pin.

J'ai bien compris que les timers sont sur deux pin.

timer 0 sur 5 et 6
timer 1 sur 9 et 10
timer 2 sur 3 et 11

Je n'arrive pas a changer les registres pour avoir une freq variable (entre 7 à 10KHz) sur une pin et une fixe à environ 30 Hz sur une autre ou interne car pour générer des interruptions précises a temps fixe.

Mon code pour le moment il fonctionne que sur la pin 10.

Dans la data sheet on parle pour des registres TCCR0B, TCCR1B et TCCR3B, mais j'ai du mal a tout comprendre (j'au aussi lu cet article LOCODUINO - Les Timers (I) mais c pour ATmega328P)

Si vous pouvez m'aider, ce serait sympa ... :slight_smile:

/*
Programmer un signal PWM normal (lent), symétrique et non inversé sur la sortie OC1B/PB6 (timer 1/comparateur B) correspondant à la pin 10 ,
les valeurs peuvent être :
- configuration sortie : DDRB[6] = 1
- prédiviseur : CS1[2:0] = 1 (f/1)
- mode : WGM1[3:0] = 8 ("PWM, Phase and Frequency Correct" avec ICR1)
- fréquence : ICR1 = 800 (fout = 16MHz/(2·prédiviseur·ICR1) = 10 kHz)
- polarité : COM1B[1:0] = 2 (signal non inversé)
- rapport cyclique : OCR1B (relativement à la valeur de ICR1)

*/

 int frequence ;    
 int freq1 ;        // fréquence = 8 kHz (800 pour 10KHz)

unsigned int  dutycycl ; // rapport cyclique rel. icr1

void setup() {

  Serial.begin(9600);  // initialize the serial 1 communication
  // pinMode(10, OUTPUT); 
 
  // OC1A et OC1C inutilisées
  unsigned char com1A = 0;
  unsigned char com1C = 0;
  
  // 8 kHz sur sortie 10 Micro (OC1B/PB6)
  unsigned char cs1   = 4;   // prédiviseur = f/1  (mettre à 1 pour fonctionner en Hz)
  unsigned char mode1 = 8;   // mode = 8 
  unsigned char com1B = 2;   // signal non inversé
  

  unsigned int  freq = 1000 ; // fréquence = (1000 pour 8 kHz et 800 pour 10KHz)
 
  unsigned int  dutycycl = freq/2 ; // rapport cyclique rel. icr1

// check if data has been sent from the computer:

  DDRB |= 1<<6;
  TCCR1A = (com1A<<6)|(com1B<<4)|(com1C<<2)|(mode1&3);
  TCCR1B = ((mode1>>2)<<3)|cs1;
  ICR1 = freq;
  OCR1B = dutycycl;

}

void loop() {
  if (Serial.available()) {
    // read the most recent byte (which will be from 0 to 255)
    freq1 = Serial.parseInt();
    frequence = (16000/(2*1*freq1));
    // set the brightness of the LED:
    // analogWrite(ledPin, frequence);
  Serial.print("Saisir la valeur de la frequence en KHz:");
  Serial.println(freq1);
 
     ICR1  = frequence ;
     OCR1B = frequence/2 ;
  }
}

Je suis dans la data sheet mais j'ai beaucoup de mal à trouver, elle parle de registre et non de pin.

C'est normal.
La seule dénomination qui compte c'est celle de la datasheet.
L' "autre" c'est une invention de l'IDE Wiring qui a été copié par Arduino.

Je n'ai jamais utilisé d'autre micro Atmel que le 328p, je n'ai pas de doc pour le 32U4.
Mais la conversion Atmel/Arduino doit se trouver sur internet.

Essayons !
Je tape "correspondance atmega32U4 arduino" dans Qwant.com (je ne supporte plus Google et ses vols de vie privée).

La première réponse me donne le lien :
https://www.arduino.cc/en/Hacking/PinMapping32u4

C'est juste sur le même site que le forum, fantastique non ?
Au boulot !

je connais déjà ça, merci comme même mais mon problème n'est pas de mettre du 328p en 32u4, mais d'utiliser un deuxième timers sur le 32u4.

Pour moi le code modifier pour utiliser le timers 3, donne

int frequence ;    
 int freq1 ;        // fréquence = 8 kHz (800 pour 10KHz)

unsigned int  dutycycl ; // rapport cyclique rel. icr1

void setup() {

  Serial.begin(9600);  // initialize the serial 1 communication
  // pinMode(10, OUTPUT); 
 
  // OC3B et OC3C inutilisées
  unsigned char com3B = 2;
  unsigned char com3C = 2;
  
  // 8 kHz sur sortie 5 Arduino Micro (OC3A/PC6)
  unsigned char cs1   = 1;   // prédiviseur = f/1  (mettre à 1 pour fonctionner en kHz)
  unsigned char mode1 = 8;   // mode = 8 
  unsigned char com3A = 2;   // signal non inversé
  

  unsigned int  freq = 1000 ; // fréquence = (1000 pour 8 kHz et 800 pour 10KHz)
 
  unsigned int  dutycycl = freq/2 ; // rapport cyclique rel. icr1

// check if data has been sent from the computer:

  DDRB |= 1<<6;
  TCCR3A = (com3A<<6)|(com3B<<4)|(com3C<<2)|(mode1&3);
  TCCR3B = ((mode1>>2)<<3)|cs1;
  ICR3 = freq;
  OCR3A = dutycycl;

}

void loop() {
  if (Serial.available()) {
    // read the most recent byte (which will be from 0 to 255)
    freq1 = Serial.parseInt();
    frequence = (16000/(2*1*freq1));
    // set the brightness of the LED:
    // analogWrite(ledPin, frequence);
  Serial.print("Saisir la valeur de la frequence en KHz:");
  Serial.println(freq1);
 
     ICR3  = frequence ;
     OCR3B = frequence/2 ;
  }
}

Mais ca marche pas ....

je cherche toujours comment configurer pour le timer 3 et donc une autre sortie les lignes ( registres) :

je crois que je dois modifier aussi cette ligne mais je ne trouve pas encore comment...
// configuration sortie : DDRB[6] = 1
DDRB |= 1<<6;

Ca y ai j'ai trouver, ca marche très bien :slight_smile: avec deux fréquences précises variables et réglables.

Il me reste encore deux inconnus :

la ligne
TCNT3 = 0; // a quoi sa sert ???

DDRB |= 1<<6; // configuration output PORT B c'est la pin 10 mais pourquoi ??

DDRC |= ((1 << 6) | (1 << 5)); // configuration output PORT C ?? et sur quel Port ???

Voici le code

/*
Programme avec deux timers :
    Timer 1 : Genere une freq variable entre 1Kh à 15Khz (changer cs1 à 4 pour fonctionner en basse fréquence).
    Timer 3 : Genere une période en ms pour générer les acquisistions par IT sur le capteur Tof  
*/
volatile int cont = 0;
const int led = 6;  // the pin to check interrupt
int ledState = LOW;
char mode = 0;

float  freq3 ;        
float  frequence3 ;  
float  PeriodeIT = 30 ;  

 int frequence ;    
 int freq1 = 10 ;        // fréquence = 8 kHz (800 pour 10KHz)
unsigned int  dutycycl ; // rapport cyclique rel. icr1

 
void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(led, OUTPUT);

  // Initialize Timer 3
    cli();          // disable global interrupts
  DDRC |= ((1 << 6) | (1 << 5));
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B =  60; //    20 for 800Hz    5208 for 3 Hz
  // turn on CTC mode:
  //TCCR3B |= (1 << WGM32);
  
  // Set CS30 and CS32 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);

  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();

  // Initialize Timer 1
  // OC1A et OC1C inutilisées
  unsigned char com1A = 0;
  unsigned char com1C = 0;
  
  // 8 kHz sur sortie 10 Micro (OC1B/PB6)
  unsigned char cs1   = 1;   // prédiviseur = f/1  (mettre à 1 pour fonctionner en kHz)
  unsigned char mode1 = 8;   // mode = 8 
  unsigned char com1B = 2;   // signal non inversé
  

  unsigned int  freq = 1000 ; // fréquence = (1000 pour 8 kHz et 800 pour 10KHz)
 
  unsigned int  dutycycl = freq/2 ; // rapport cyclique rel. icr1

// check if data has been sent from the computer:

  DDRB |= 1<<6;
  TCCR1A = (com1A<<6)|(com1B<<4)|(com1C<<2)|(mode1&3);
  TCCR1B = ((mode1>>2)<<3)|cs1;
  ICR1 = freq;
  OCR1B = dutycycl;
  
}

void loop()
{
 // Serial.println(cont); 
 // cont = 0;
 // delay(100);
    
          
          //  PeriodeIT = Serial.parseInt();    // en ms
              //***********************************************
              PeriodeIT = 40;              // Saisir la période de l'interruption en ms
              //***********************************************
              freq3 = 1/PeriodeIT ;             // en mHz
              frequence3 = (16/(2.0*1*freq3));
       

              OCR3B = frequence3 ;
              //800Hz 5; // 3 Hz          
       //    }
           
       //    else if (mode == '2')
         
             // read the most recent byte (which will be from 0 to 255)
      if (Serial.available()) 
            {
             Serial.print("Saisir la valeur de la frequence en KHz:");
             freq1 = Serial.parseInt();
             Serial.println(freq1);
             frequence = (16000/(2*1*freq1));
             Serial.print("Saisir la valeur de la frequence en KHz:");

             ICR1  = frequence ;
             OCR1B = frequence/2 ;
             }
       
             
}




ISR(TIMER3_COMPB_vect)
{
//*** Test freq de interuption  ***
       if (ledState == LOW) {
          ledState = HIGH;
        //  blinkCount = blinkCount + 1;  // increase when LED turns on
        } else {
          ledState = LOW;
        }
         digitalWrite(led, ledState);   // Sur la pin 6.
 //********************************
 
  TCNT3 = 0;
 // cont++;
}

TCNT3 = 0; // a quoi sa sert

A faire une RAZ sur le compteur du timer3
Pourquoi il faut une RAZ ?
C'est à toi de chercher à comprendre.

// configuration output PORT B c'est la pin 10 mais pourquoi ??

Quand on recopie un programme tout fait il y a de forts risques de recopier aussi les erreurs.
Une petite modif d'un fichier déjà publié.
Mais comme c'est une petite modif de rien du tout l'auteur juge inutile de vérifier et paf il y avait des fautes de frappes.
Il faut toujours se méfier et toujours vérifier.

Je pense que c'est pour éviter les overflow

A la base je suis électrotechnicien dans la hard, donc pour débuter je ne vais pas réinventer la roue, c'est bien pratique le partage. En plus a force de lire des codes j'apprends bcps de choses.

Je sais bien qu'un copié/coller de code ne suffit pas c'est pour cela que je poste des questions, il y a donc une érreur, ceci explique cela...

Maintenant je dois la comprendre pour la corriger.