Détection de seuil de tension

Bonjour,

Je suis novice alors désolé si mon sujet contient des énormités*

Mon projet consiste à détecter un seuil de tension. J'ai mon GBF réglé sur 1kHz, 1V et mon oscillo qui a une sinusoïde sur la voie 1 (variant de 0 à 1V) et la deuxième voie sert au déclenchement d'un pulse lorsque la tension dépasse X volte.

Je travaille sur une RedBoard. J'ai donc 2 entrées: GBF sur le pin A0 et la voie 2 de l'oscilloscope sur le pin 5.

J'aimerai tout simplement avoir ma sinusoïde en voie 1 et juste en dessous, la voie 2 qui déclenche le pulse au bon moment, or avec mon code actuel, les pulses semblent totalement aléatoires et ne sont pas alignés avec la valeur seuil X indiquée.

Outre une amélioration qui manque certainement au code suivant, est-ce également un problème de "vitesse" de détection?
Merci d'avance pour votre aide.

float seuil = 0.5; 
const byte rapportCyclique = 20 ;
int pin = 5;

void setup() 
{
  Serial.begin(9600); 
  analogReference(INTERNAL);
  pinMode(pin,INPUT);
  pinMode(A0,INPUT);
}

void loop() 
{
  int val = analogRead(A0);
  float tension = val*(1.1/1024);
  
  if(tension > seuil)
  {
    analogWrite(pin, rapportCyclique);
  }
}

analogWrite génère un signal PWM qui est asynchrone de ton signal d'entrée.
Si tu veux générer une impulsion unique lorsque le seuil est franchi il faut faire un digitalWrite. Le problème c'est que cette fonction n'est pas très rapide. Tu pourrais peut-être regarder ici
Pour rendre ton système plus réactif il serait peut-être plus malin de calculer au début (dans le setup) le seuil en LSB soit 1023*0.5/1.1. Tu prends ensuite l'entier le plus proche et dans loop tu compares directement cette valeur avec celle retournée par analogRead. Les calculs en flottant sont gourmands en ressources.

Il y a aussi une autre solution plus optimisée qui consisterait à utiliser le comparateur qui est intégré dans l'ATmega. Le problème c'est qu'il n'est pas directement supporté par Arduino donc il faudra te plonger dans la doc du processeur.

hello
je viens de tester, à 1000 Hz ça suit bien

float seuil = 1023*0.5/5; //pour sinusoide en 5V d'amplitude
const byte rapportCyclique = 1 ;
int pin = 5;
float tension;
int val;
void setup() 
{
  Serial.begin(115200); 
  //analogReference(INTERNAL);//commentée car sinusoide en 5V d'amplitude
  pinMode(pin,OUTPUT);
  pinMode(A0,INPUT);
}

void loop() 
{
  tension = analogRead(A0);
  if(tension >= seuil)
  {
    digitalWrite(pin, HIGH);
  }
  else
  {
    digitalWrite(pin, LOW);
  }
}

Merci pour vos réponses

En effet avec digitalWrite(); c'est mieux, mais pour ma part cela ne suit pas très très bien (avec une amplitude d'1,1V dans mon cas).. Cela fait des va et vient, impossible de lire à quelle tension le pulse à lieu.

Je pense c'est là le problème de vitesse (et du coup de précision) que vous aviez mentionné.

Après m'être documenté sur le forum et sur l'ATMEGA 32, je devine brièvement que je devrais utiliser un comparateur analogique* (l'ACSR).

Est-ce que vous pourriez m'expliquer, (peut etre avec un autre exemple plus simple) comment cela se code ? j'ai lu comment l'initialiser mais en se qui concerne la fonction ISR(ANALOG_COMP_vect ) j'ai encore pas mal de zone d'ombre.. je ne comprends pas ce qu'elle doit contenir en fait.

Merci d'avance

Bonjour,

Effectivement avec cette méthode à 1kHz, le pulse de sortie dot avoir du jitter.

Tu peux utiliser le comparateur analogique de l'Atmega, mais si tu veux simplement générer un pulse, pourquoi ne pas utiliser simplement un ampli op monté en comparateur (ou mieux un comparateur spécialisé) pour générer ce pulse. Il fera la même chose sans soft.

Heuu ma mission de stage m'impose l'Arduino pour le moment, mais si je n'y arrive finalement pas je me pencherai sur ta solution kamill. Mon tuteur de stage, ne connaissant pas bien l'Arduino, ne peut pas me donner son avis sur la faisabilité. Merci*

Si quelqu'un peut me donner quelques pistes quant à l'utilisation du comparateur analogique de l'Atmega32, je suis preneur !

Tu as un exemple d'utilisation du comparateur analogique de l'atmega328 ici

Je vous remercie,

J'aurai probablement une dernière question pour mieux comprendre comment cela fonctionne avant de bien l'utiliser:
Par exemple, dans le code suivant (que j'ai récupéré sur ton lien), qu'est ce que le comparateur analogique compare ?? Quelles sont les 2 valeurs comparées svp ?

/*
  AnalogComparator
  Testing the Atmega328 built-in analog comparator
  For info and circuit diagrams see https://github.com/tardate/LittleArduinoProjects/tree/master/playground/AnalogComparator
 */

#define INDICATOR_PIN 8

volatile boolean triggered;
volatile boolean rising;
volatile byte acsr_value;

ISR (ANALOG_COMP_vect) {
  if (triggered) return;           // For testing purposes, ignore the interrupt if we haven't reported the last one yet.
                                   // This also effects some crude debouncing.
  cli();
  acsr_value = ACSR;
  rising = (acsr_value & bit(ACO)) > 0;
  triggered = true;
  sei();
}

void setup() {
  Serial.begin (115200);
  Serial.println ("Started.");

  pinMode(INDICATOR_PIN, OUTPUT);
  digitalWrite(INDICATOR_PIN, LOW);

  ADCSRB = ADCSRB & ! bit(ACME);   // disable Analog Comparator Multiplexer Enable
  ACSR =  bit(ACI)                 // clear Analog Comparator Interrupt Flag
        | bit(ACIE)                // set Analog Comparator Interrupt Enable
        | bit(ACIS1) | bit(ACIS0)  // select rising edge: ACIS1/ACIS0 Analog Comparator Interrupt Mode Select
        ;
}

void loop() {
  if (triggered) show_what_happened();
}

// just give an indication via Serial and LED that we handled an interrupt...
void show_what_happened() {
  digitalWrite(INDICATOR_PIN, HIGH);

  Serial.print ("Triggered! ACSR=");
  Serial.print (acsr_value, BIN);
  if (rising)
    Serial.println (" RISING EDGE (as expected)");
  else
    Serial.println (" FALLING EDGE .. wtf, didn't ask for this, but every so often one slips thru. Maybe a sync issue reading ACSR.");

  triggered = false;

  delay(100);
  digitalWrite(INDICATOR_PIN, LOW);
}

Ca compare la tension entre AIN0 (pin 6) et AIN1 (pin 7)

Merci, quelle partie du code t'a permis de savoir cela stp ??

Aucune.
C'est dans l'article et dans la datasheet du processeur.

Prl:
Merci, quelle partie du code t'a permis de savoir cela stp ??

Dans les premières lignes du code il y a un lien vers github avec le schéma du montage, une explication du fonctionnement et une vidéo qu'est-ce qu'il te faut de plus?

Oui ce lien m'a déjà été communiquée par kamill.

Je ne comprends pas quand est-ce qu'on entre dans la fonction ISR (ANALOG_COMP_vect) {}. On entre dedans lorsque le bit ACO passe à 1 (lorsque VAIN0 > VAIN1) ?

Ca dépend de la config de ACIS1 et ACIS0

Setting ACIS1, ACIS0 bits select the interrupt to trigger:

ACIS1 ACIS0 Trigger
0 0 Toggle
1 0 Falling
1 1 Rising
In practice, the comparator can be extremely bouncy. This can cause rising interrupts when only falling are expected, and vice versa. Some measure of debouncing appears to be essential.

J’aimerai s’il vous plaît vous solliciter une dernière fois concernant cela. J’ai l’impression d’avoir toutes les cartes en mains mais de ne pas savoir comment les jouer.

Voulant déclencher des impulsions lorsqu’une valeur (sinusoïde sur le pin A0) en dépasse une autre (seuil sur le pin A1), j’ai appliqué une tension de 5V sur le pin A1. À l’aide d’une résistance fixe et d’une résistance variable je peux avoir sur le pin A1 n’importe qu’elle seuil compris entre 0 et 1,37V max. (du coup le nombre entier varie entre 0 et 276)
En ce qui concerne la tension alternative, je branche directement mon GBF sur le pin A0 (comme ça le comparateur analogique compare bien ce que je veux).

Pour déclencher les impulsions variable en largeur je n’ai pas trouvé d’autre moyen que d’utiliser les PWM (j’utilise le pin ~5)

J’ai deux questions: Si pour le pin A1 je convertis le nombre entier avec valeurPotentiometre = valeurPotentiometre1.37/276;
Avec quelles valeurs je devrais convertir le nombre entier sur le pin A0 ?? valA0 = valA0
(?) / (?);
(Ou bien n’ai-je même pas à convertir ces nombres pour que la comparaison ait correctement lieu ??)

Et ensuite, est-ce que mon code actuel me permet bien, lorsque VA0 > VA1, d’entrer dans la fonction ISR (ANALOG_COMP_vect) {} et donc d’activer la fonction if dans ma loop qui, elle, déclenchera l’impulsion ?

Merci d’avance pour votre aide

#include <avr/interrupt.h>

const byte RapportCyclique = 40 ; // Largueur d'impulsion (0 - 255)
int AIN0 = A0; // AIN0
int AIN1 = A1; //AIN1
int pin = 5; // Pulse
volatile boolean triggered; 
float portPotentiometre = A1;
float valeurPotentiometre;
float valA0;

ISR(ANALOG_COMP_vect) // active lorsque VAIN0 > VAIN1?
{
    triggered = true; 
}

void setup() 
{
  Serial.begin(9600); 
  pinMode(pin,OUTPUT);
  
   ACSR =         //   INITIALISATION DU REGISTRE ACSR
 (0<<ACD) |   // Analog Comparator: Enabled
 (0<<ACBG) |   // Analog Comparator Bandgap Select: AIN0 is applied to the positive input
 (0<<ACO) |   // Analog Comparator Output: Off
 (1<<ACI) |   // Analog Comparator Interrupt Flag: Clear Pending Interrupt
 (1<<ACIE) |   // Analog Comparator Interrupt: Enabled
 (0<<ACIC) |   // Analog Comparator Input Capture: Disabled
 (1<<ACIS1) | (0<ACIS0);   // Analog Comparator Interrupt Mode: Comparator Interrupt on Rising Output Edge
}

void loop() 
{
  valeurPotentiometre = analogRead(portPotentiometre);
  valeurPotentiometre = valeurPotentiometre*1.37/276;//Convertion
  valA0 = analogRead(A0);
  valA0 = valA0* (?) / (?);  // Convertion
  if(triggered)  // seuil dépassé
  {
    digitalWrite(pin, RapportCyclique);//Impulsion
    triggered = false; // Reset
  }
}

Bonjour,

Je ne comprends pas ce que tu racontes avec A0 et A1.

Le comparateur utilises les AIN0 et AIN1 qui sont comme je te l'ai déjà dit les pins 6 et 7 de la carte uno.

Il n'y a pas d'histoire de conversion. Quand la tension sur AIN1 passe au dessus de la tension sur AIN0, ça génère une interruption (si les registres sont paramétrés en conséquence).

Très bien merci je me suis donc encore trompé: j'utilisais au tout début les pins 6 et 7 pensant ensuite que AIN0 et AIN1 était les pins Analog In 0 et 1.. Désormais j'utiliserai bien les pin Digital 6 et 7.

Désolé si pour toi cela te parait limpide mais, "interruption" = "entrer dans la fonction ISR" ??

Oui interruption c’est entrée dans la fonction d’interruption.

Je n’obtiens toujours pas ce que je veux. Je ne pense pas que cela vienne du câblage mais de mon code, as-tu une idée de ce que j’aurai pu mal faire stp ??

#include <avr/interrupt.h>

const byte RapportCyclique = 20 ; // Largueur d'impulsion (0 - 255)

int pin = 5; // PWM
volatile boolean triggered; 


ISR(ANALOG_COMP_vect) // active lorsque VAIN0 > VAIN1?
{
    triggered = true; 
}

void setup() 
{
  Serial.begin(9600); 
  pinMode(pin,OUTPUT);
  pinMode(6,INPUT);
  pinMode(7,INPUT);
   ACSR =         //   INITIALISATION DU REGISTRE ACSR
 (0<<ACD) |   // Analog Comparator: Enabled
 (0<<ACBG) |   // Analog Comparator Bandgap Select: AIN0 is applied to the positive input
 (0<<ACO) |   // Analog Comparator Output: Off
 (1<<ACI) |   // Analog Comparator Interrupt Flag: Clear Pending Interrupt
 (1<<ACIE) |   // Analog Comparator Interrupt: Enabled
 (0<<ACIC) |   // Analog Comparator Input Capture: Disabled
 (1<<ACIS1) | (1<ACIS0);   // Analog Comparator Interrupt Mode: Comparator Interrupt on Rising Output Edge
}

void loop() 
{

  if(triggered)  // seuil dépassé
  {
    digitalWrite(pin, RapportCyclique);//Impulsion
    triggered = false; // Reset
  }
}

Je n'arrive pas à comprendre ce que tu veux faire

Testes d'abord si ton interruption est bien appelée
Dans ton isr, fait basculer une sortie et regarde à l'oscillo. Dans le genre

digitalWrite(OUTPUT_PIN,!digitalRead(OUTPUT_PIN));

Pour OUTPUT_PIN, tu prends une pin libre (n'oublie pas de la mettre en sortie).

Ca sert à quoi ça?

 pinMode(pin,OUTPUT);
 pinMode(6,INPUT);
 pinMode(7,INPUT);

et ça ?

     digitalWrite(pin, RapportCyclique);//Impulsion