Timer interrupt pour métronome

Vous connaissez un logiciel ou site pour pouvoir simuler les timers ? Je ne possède pas mon kit d'électronique sur moi

ça fonctionne dans tinkercad

Ok , j'ai réussi à trouver des valeurs pour faire clignoter ma Led selon le potentiomètre mais maintenant je fais comment pour le faire avec le buzzer ?

le plus simple c'est de faire le beep dans l'interruption (mais on est un peu limité car les interruptions sont désactivées). Avez vous un buzzer qui requiert une oscillation ou si vous lui envoyez un HIGH il fait du bruit ?

Oui , j'ai trouvé entre temps j'ai utilisé ceci

#define Buzzer 5//
#define Buzzer1Toggle PORTD^=(1<<Buzzer)

ISR(TIMER1_COMPA_vect)
{

  Buzzer1Toggle;
}

ça bascule la valeur de la pin D5 lors de l'interruption

c'est comme si vous aviez écrit digitalWrite(Buzzer, (digitalRead(Buzzer) == HIGH) ? LOW : HIGH);ou plus simplePIND = bit(Buzzer);

la question est: est-ce que ça buzz au bon moment ?

J-M-L:
ça bascule la valeur de la pin D5 lors de l'interruption

c'est comme si vous aviez écrit

digitalWrite(Buzzer, (digitalRead(Buzzer) == HIGH) ? LOW : HIGH);

ou plus simple

PIND = bit(Buzzer);

la question est: est-ce que ça buzz au bon moment ?

Oui , en changeant un peu la formule de l'0CR1A (j'ai mis 35000 à la place de 15625) et en mettant des valeurs de BPM précise , je comptais pendant 15s le nombre de bip et le multiplier par 4 et c'était parfait pour les BPM au alentours de 0 à 125 mais il commence à avoir un petit décalage de 1 à 3 entre 125 et 200 mais c'est si petit que je ne pense pas que cela soit un vrai problème

un musicien vous dirait que c'est important!!

J-M-L:
un musicien vous dirait que c'est important!!

Oui probablement ahaha. J'ai juste une dernière question , en fait notre projet consiste à avoir plusieurs parties pour faire un synthétiseur , un vco qui créer les fréquences à l'aide de résistance en série et d'un ne555 , un filtre rc pour sculter la fréquence sortant du vco et un sélectionneur avec un multiplexeur qui permet de choisir entre le son 'brut' du vco ou par le circuit rc , et enfin le métronome qui permet de chosir le bpm de la musique, on a aussi une interface processing qui montre les notes qui sont joués à l'aide des boutons . Notre seul problème actuellement c'est de récupérer la fréquence pour l'envoyer sur processing. On nous a conseillé de la calculer en comptant la période sur les fronts montant sur 500ms

volatile int prev_time = 0;
volatile int now_time = 0;
volatile int periode1 = 0;
volatile int periode = 0;
volatile int frequence = 0;
volatile int compteur=0;
int tempsactuel=millis();


int donnee = 2;

void setup() 
{
  Serial.begin(115200);
  pinMode(donnee, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(donnee), rising, RISING);
}

void loop() 
{
  if(tempsactuel>500)
  {
    periode=periode/compteur;
   frequence=1/periode;
   compteur=0;
   periode=0;
   tempsactuel=0;
  }
  else
  {
  periode=periode1+periode;
  
  }
 
  Serial.println(periode);
 Serial.println(frequence);
  
}

void rising()
{
  now_time = micros();
  periode1 = now_time - prev_time;
  prev_time = now_time;
  compteur++;
}

On a essayé ceci mais on à que 0 en fréquence

Votre code est louche... par exemple qui fait évoluer tempsactuel? (Qui devrait être un unsigned long). Les variables partagées entre l’interruption et le code principal doivent être en volatile et lues en section protégée

Sur ce site vous avez plusieurs façons de capturer une fréquence en utilisant les timers

J-M-L:
Votre code est louche... par exemple qui fait évoluer tempsactuel? (Qui devrait être un unsigned long). Les variables partagées entre l’interruption et le code principal doivent être en volatile et lues en section protégée

Sur ce site vous avez plusieurs façons de capturer une fréquence en utilisant les timers

Merci pour le site , mais du coup si j'utilise ce code je vais devoir utiliser une autre carte arduino non puisque j'utilise déjà timer1 pour le métronome

Avez vous vraiment l’obligation d’utiliser le timer pour le métronome ? Même à 200bpm avec millis on s’en sort hyper simplement...ça permettrait d’utiliser les timers pour autre chose de plus gourmand

Quel ordre de fréquence devez vous mesurer ? Hz, KHz, MHz ?

J-M-L:
Avez vous vraiment l’obligation d’utiliser le timer pour le métronome ? Même à 200bpm avec millis on s’en sort hyper simplement...ça permettrait d’utiliser les timers pour autre chose de plus gourmand

Quel ordre de fréquence devez vous mesurer ? Hz, KHz, MHz ?

Oui malheureusement je suis obligé , pour les valeurs de fréquence ça dépend du potentiomètre, je n'ai pas le montage complet sur moi mais voilà à quoi ressemble le montage du vco par exemple

Du coup selon le potentiomètre on à des valeurs entre 411 et 626 si il est à 10k ou 960 à 4800 si il est à 0.

float prev_time = 0;
float now_time = 0;
float periode = 0;
float total=0;
float frequence=0;
int donnee = 2;

unsigned long avant=0;
volatile int compteur=0;


void setup() 
{
  Serial.begin(115200);
  pinMode(donnee, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(donnee), rising, RISING);

}
void loop() 
{
unsigned long maintenant=millis();
  if((maintenant-avant)>500)
  { 

    total=total/compteur;
    frequence=1.00/total;
    compteur=0;
    avant=maintenant;

 Serial.println(frequence);
  Serial.println(total);

  total=0;
  }
 
}

void rising()
{
  now_time = micros();
  periode = now_time - prev_time;
  prev_time = now_time;
  total=total+periode;
  compteur++;

}

Voici notre code actuel , on à la période mais toujours impossible de diviser la fréquence

pourquoi pinMode(donnee, OUTPUT); ? n'est-ce pas plutôt une entrée ?

J-M-L:
pourquoi

pinMode(donnee, OUTPUT);

? n'est-ce pas plutôt une entrée ?

Oui pardon , on avait fait des tests pour voir si la pin marchait bien ou non donc on avait mis en sortie mais le problème reste le même , on ne peut pas diviser la période pour avoir la fréquence.

essayez un truc comme cela

const byte pinFrequence = 2;
volatile unsigned long compteur;

void frontMontant() {compteur++;}

unsigned long lireFrequence(uint32_t duree) {
  compteur = 0;
  attachInterrupt(digitalPinToInterrupt(pinFrequence), frontMontant, RISING);
  delay(duree);
  detachInterrupt(digitalPinToInterrupt(pinFrequence));
  // on eu a compteur fronts pendant duree ms
  return (1000ul * compteur) / duree;
}

void setup()
{
  Serial.begin(115200);
  pinMode(pinFrequence, INPUT);
}

void loop() {
  unsigned long f = lireFrequence(500); // appel bloquant
  Serial.print(F("Fréquence = ")); Serial.print(f); Serial.println(F(" Hz"));
  delay(1000);
}

dans ce code l'appel à lireFrequence est bloquant.
on active les interruptions
on attend un moment (durée)
on désactive les interruptions
on regarde combien de fronts on a obtenus pendant cette durée (en ms)
pour avoir la fréquence (le nombre de fronts en 1s (1000ms)) c'est donc juste une règle de trois
--> (1000 * compteur / durée)

En pratique il faudrait lancer la mesure et utiliser un timer pour arrêter précisément la capture des fronts tout en ayant pris soin de désactiver millis() qui va générer aussi des interruptions donc potentiellement influencer la lecture...

bien sûr la durée d'attente dépend de la précision de l'horloge de votre Arduino qui n'est pas géniale... donc la fréquence lue est toute approximative, surtout si la période d'échantillonnage est courte

Suivant votre besoin de précision et de timing, ça peut peut-être aller....

sinon pour faire la gestion du BPM sans timer, c'est un truc comme ça

const byte buzzerPin = 6;
const byte potPin = A1;

const unsigned long minBPM = 0;
const unsigned long maxBPM = 200;
unsigned long periode = 60000; // en ms

void beep() {
  static unsigned long lastChrono;
  if (millis() - lastChrono >= periode) {
    digitalWrite(buzzerPin, HIGH);
    delayMicroseconds(50);
    digitalWrite(buzzerPin, LOW);
    lastChrono = millis();
  }
}

void gestionBPM() {
  unsigned long nouvellePeriode;
  long potVal = map(analogRead(potPin), 0, 1023, minBPM, maxBPM);
  if (potVal == 0)
    nouvellePeriode = 4294967295;
  else
    nouvellePeriode = 60000ul / potVal;
  if (abs(nouvellePeriode - periode) > 10) {
    // on a bougé le potentiomètre, il faut changer le réglage du timer
    periode = nouvellePeriode;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(buzzerPin, OUTPUT);
  Serial.println(F("*** METRONOME ***"));
}

void loop() {
  gestionBPM();
  beep();
}

J-M-L:
essayez un truc comme cela

const byte pinFrequence = 2;

volatile unsigned long compteur;

void frontMontant() {compteur++;}

unsigned long lireFrequence(uint32_t duree) {
  compteur = 0;
  attachInterrupt(digitalPinToInterrupt(pinFrequence), frontMontant, RISING);
  delay(duree);
  detachInterrupt(digitalPinToInterrupt(pinFrequence));
  // on eu a compteur fronts pendant duree ms
  return (1000ul * compteur) / duree;
}

void setup()
{
  Serial.begin(115200);
  pinMode(pinFrequence, INPUT);
}

void loop() {
  unsigned long f = lireFrequence(500); // appel bloquant
  Serial.print(F("Fréquence = ")); Serial.print(f); Serial.println(F(" Hz"));
  delay(1000);
}



dans ce code l'appel à lireFrequence est bloquant. 
on active les interruptions
on attend un moment (durée)
on désactive les interruptions
on regarde combien de fronts on a obtenus pendant cette durée (en ms)
pour avoir la fréquence (le nombre de fronts en 1s (1000ms)) c'est donc juste une règle de trois
--> (1000 * compteur / durée)

En pratique il faudrait lancer la mesure et utiliser un timer pour arrêter précisément la capture des fronts tout en ayant pris soin de désactiver millis() qui va générer aussi des interruptions donc potentiellement influencer la lecture...

bien sûr la durée d'attente dépend de la précision de l'horloge de votre Arduino qui n'est pas géniale... donc la fréquence lue est toute approximative, surtout si la période d'échantillonnage est courte

Suivant votre besoin de précision et de timing, ça peut peut-être aller....

sinon pour faire la gestion du BPM sans timer, c'est un truc comme ça


const byte buzzerPin = 6;
const byte potPin = A1;

const unsigned long minBPM = 0;
const unsigned long maxBPM = 200;
unsigned long periode = 60000; // en ms

void beep() {
  static unsigned long lastChrono;
  if (millis() - lastChrono >= periode) {
    digitalWrite(buzzerPin, HIGH);
    delayMicroseconds(50);
    digitalWrite(buzzerPin, LOW);
    lastChrono = millis();
  }
}

void gestionBPM() {
  unsigned long nouvellePeriode;
  long potVal = map(analogRead(potPin), 0, 1023, minBPM, maxBPM);
  if (potVal == 0)
    nouvellePeriode = 4294967295;
  else
    nouvellePeriode = 60000ul / potVal;
  if (abs(nouvellePeriode - periode) > 10) {
    // on a bougé le potentiomètre, il faut changer le réglage du timer
    periode = nouvellePeriode;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(buzzerPin, OUTPUT);
  Serial.println(F("*** METRONOME ***"));
}

void loop() {
  gestionBPM();
  beep();
}

Merci beaucoup , on à juste une dernière question finalement ^^'. Normalement on est sensé mettre le signal de la sortie du vco et celle du filtre sur un multiplexeur. Il y a une bascule jk qui est actionné par un bouton qui permet de changer le bit du sélectionneur d'entrée mais malheureusement on arrive pas à bien brancher. Encore une fois j'ai pas de photos du schéma mais voici une simulation sur proteus en attendant le diagramme de branchement.Quand on appuie pas sur le bouton , la première entré marche mais pas la deuxième et quand on appuie sur le bouton la seconde marche mais pas la première

Du coup pour le multiplexeur c'est les branchements qu'on a fait(X0 représentant le vco et X1 le signal avec filtre) mais pour la bascule j'ai pas trouvé celle qu'on a sur proteus , on à la 74HC109: https://assets.nexperia.com/documents/data-sheet/74HC_HCT109.pdf

Et c'est probablement là où on a des difficultés , on à branché VCC , 1J et 1!SD sur 5V , GND !RD et 1!K sur ground ,CP vient du bouton et la sortie 1Q va sur l'entrée de sélection A. On ne sait pas si on doit branché ou non les autres entrées K , J et CP.

là je suis sur mon smartphone, difficile de voir tout cela

si la question est comment bien brancher une bascule JK, postez éventuellement une question à part sur le forum. attention aux rebonds de votre bouton.

mais pourquoi ne pas utiliser un simple interrupteur avec un pulldown au lieu du bouton et de la bascule ?

J-M-L:
là je suis sur mon smartphone, difficile de voir tout cela

si la question est comment bien brancher une bascule JK, postez éventuellement une question à part sur le forum. attention aux rebonds de votre bouton.

mais pourquoi ne pas utiliser un simple interrupteur avec un pulldown au lieu du bouton et de la bascule ?

C'est un projet d'école donc malheureusement c'est pas nous qui décidons mais clairement ça aurait été beaucoup plus simple