voilà une semaine que je galère pour faire un chronomètre, j'ai épluché tous tutos, libraries en rapport avec ça mais aucun résultat.. =( bref !
Donc mon projet, un chrony pour ceux qui connaissent en airsoft, appareil qui permet de mesurer la vélocité d'une bille ( 100m/s de moyenne ), le moyen, très simple : deux photo-transistors et deux diodes IR, le projectile traverse le premier laser qui lance le chrono et puis coupe le second qui arrête celui-là. J'ai déjà fais plusieurs codes, en vain...
const int APPUI=1;
const int PAS_APPUI=0;
const int BEGIN=A1;
const int END=A4;
int timedebut = 0;
int timefin = 0;
int time;
double FPS = 0.3078 ;
double distance = 0.005;
double vitesse;
double Joules;
double FPS1;
int ETAT_BP1;
int ETAT_BP2;
void setup() {
Serial.begin(9600);
pinMode(BEGIN, INPUT);
pinMode(END, INPUT);
Serial.print("Okay");
}
void loop() {
do {
ETAT_BP1=digitalRead(BEGIN);
}
while(ETAT_BP1 == PAS_APPUI);
timedebut=micros();
ETAT_BP1 == PAS_APPUI;
do {
ETAT_BP2=digitalRead(END);
}
while (ETAT_BP2 == PAS_APPUI);
timefin=micros();
time = timefin-timedebut;
vitesse = distance/(time*0.000001);
FPS1 = vitesse / FPS;
Joules = 0.5*sq(vitesse)*0.2*0.001;
Serial.print(vitesse);
Serial.print("\n");
ETAT_BP2 == PAS_APPUI;
}
Le code était à la base au lieu des photo-transistors, des boutons poussoirs, de là les noms.
Si j'ai bien compris, il faut faire des interruptions, perso j'y comprends rien j'en ai fais ça marchait toujours pas !
Donc si je pourrais avoir une aide sur le programme, j'en serais reconnaissant
bon bon ^^ alors ton histoire n'est pas si mal barré il manque pas grand chose, voila ce qu'il faudrait changer:
const int APPUI=1;
const int PAS_APPUI=0;
const int BEGIN=A1; // pour des capteur laser je sais pas si c'est pas plutot 1 quelque chose 0 rien donc pas une sortie analogique
const int END=A4;
int timedebut = 0;
int timefin = 0;
int time;
double FPS = 0.3078 ;
double distance = 0.005;
double vitesse;
double Joules;
double FPS1;
int ETAT_BP1;
int ETAT_BP2;
void setup() {
Serial.begin(9600);
pinMode(BEGIN, INPUT);
pinMode(END, INPUT);
Serial.print("Okay");
}
void loop() {
ETAT_BP1=digitalRead(BEGIN);
while(ETAT_BP1 == 0); //rien si il y a passage => sortie while
ETAT_BP1=digitalRead(BEGIN);
timedebut=micros();
}
while (ETAT_BP2 == 0){
ETAT_BP2=digitalRead(END);
timefin=micros();
}
time = timefin-timedebut;
vitesse = distance/(time*0.000001);
FPS1 = vitesse / FPS;
Joules = 0.5*sq(vitesse)*0.2*0.001;
Serial.print(vitesse);
Serial.print("\n");
}
voila, tant que bp1 n'est pas la on part pas, une fois BP1 détecté on prend le temps, on attend un appui sur BP2 et a l'appui on prend le deuxième temps et passe au calcule !
Voila monsieur !
reste a remplacer les sortie Analogique par celle ou tu câblera ton capteur et adapter le while ==0 si tes capteur t'envoie des valeurs différentes de 0 et 1.
ps: toujours original de voir des do{ }while{ } lol ^^
Mais la première question a se poser c'est : quelle est la durée moyenne que tu cherche à mesurer avec cette précision de 1 µs ?
Est-ce 100 µs, 1000 µs, 1 secondes ?
Skizoh, la durée de ta boucle est trop longue : copier la valeur de la lecture dans une variable globale et lire le temps dans la boucle ne sert à rien.
Si l'impulsion est trop courte tu ne la vois pas...
La durée d'exécution de la boucle introduit une incertitude sur la lecture du temps qui est supérieure à la microseconde.
Ça s'est mieux, et çà serait encore mieux en utilisant digitalReadFast ou la lecture direct des ports.
C'est déjà mieux. Et c'est encore mieux en utilisant la lib digitalReadFast.
Avec des interruptions c'est pas mal non plus :
Impulsion de début sur pin D2 (obligatoire)
Impulsion de fin sur pin D3 (idem)
On suppose que le délai de traitement de l'interruption est le même donc le délai de prise en compte de l'interruption jusqu'à la lecture du temps est supposé constant - ce qui n'est pas prouvé...
Ainsi la différence des dates élimine le délai identique dans les 2 cas.
unsigned long tDebut, tFin;
volatile boolean Fait = false;
void itDebut()
{
tDebut = micros();
}
void itFin()
{
tFin = micros();
Fait = true;
}
void setup()
{
attachInterrupt( 0, itDebut, RISING ); // Sur pin D2, RAISING ou FALLING au choix
attachInterrupt( 1, itFin, RISING ); // Sur pin D3
}
void loop()
{
while( !Fait );
Serial.print(" temps en us=" );
Serial.println( tFin - tDebut );
}
Cela devrait être raisonnablement.
Sinon, il faut utiliser le mode capture du timer mais cela impose de rentrer les 2 impulsions sur la même PIN.
Dans le mode capture, l'impulsion sur la broche déclenche en hardware simultanément un copie de la valeur du timer dans le registre de capture et la génération d'une interruption.
Dans l'interruption tu viens lire la valeur capturée.
C'est beaucoup plus précis.
Si besoin je regarderais cette solution en détail.
Bon, j'avais envoie alors j'ai joué un peu avec le mode de capture du TIMER1.
Voici comment utiliser le TIMER1 pour capturer des temps.
L'entrée capture du TIMER1 est D8 (obligé, pas le choix).
Quand une impulsion (montante ou descendante suivant config de TCCR1B) arrive, cela déclenche :
la capture de la valeur du compteur du TIMER1 dans le registre de capture ICR1 et
une interruption TIMER1_CAPT.
Dans la routine d'interruption, on sauve la valeur du registre dans un tableau.
Dans l'exemple je ne garde que 2 valeurs.
Dans loop je génère une impulsion aléatoire sur D10 qui est rebouclée par un fil sur D8.
Je peux ainsi mesurer la durée réelle de l'impulsion générée.
Pour info, le code qui généère l'impulsion (digitalWrite HIGH, LOW, HIGH, LOW) prend du temps.
Même sans delayMicroseconds() cela prend 14.5 microseconds.
On voit que même si on applique cette correction, la fonction delayMicroseconds() est loin d'être parfaite.
J'ai vérifié de nombreuses mesures à l'analyseur logique : la mesure est bien exacte à 0,5µs près.
#include <avr/io.h>
#include <avr/interrupt.h>
#define MAX_CAPTURE 2
uint16_t Capture[MAX_CAPTURE];
int NbCapture = 0;
ISR(TIMER1_CAPT_vect)
{
uint16_t capture = ICR1;
if ( NbCapture < MAX_CAPTURE )
Capture[NbCapture++] = capture;
}
// Choix d'activer (conseillé) ou pas le débruiteur sur l'entrée de capture
#define CAPTURE_DEBRUITEUR_ON 0x80
#define CAPTURE_DEBRUITEUR_OFF 0x00
// Choix du front montant ou desscendant
#define CAPTURE_FRONT_MONTANT 0x40
#define CAPTURE_FRONT_DESCENDANT 0x00
// Le choix du prediviseur influe sur la précision de la mesure
// mais aussi sur la durée maximale entre 2 impulsions
#define PREDIVISEUR_OFF 0x01 /* Horloge 16MHz, precision 0.0625us, max 4ms */
#define PREDIVISEUR_8 0x02 /* Horloge 2MHz, precision 0.5us, max 32ms */
#define PREDIVISEUR_64 0x03 /* Horloge 250 kHz, precision 4us, max 260ms */
#define PREDIVISEUR_256 0x04 /* Horloge 62.5kHz, precision 16us, max 1sec */
#define PREDIVISEUR_1024 0x05 /* Horloge 15.6kHz, precision 64us, max 4.1sec */
double precision;
void setup()
{
Serial.begin( 9600 );
// Configuration du TIMER1 pour la capture
TCCR1A = 0x00;
TCCR1B = CAPTURE_DEBRUITEUR_ON + CAPTURE_FRONT_MONTANT + PREDIVISEUR_8;
precision = 0.5; // a mettre a jour en fonction du prediviseur choisit
TIFR1 = 0x20;
TIMSK1 = 0x20;
// on s'assure que la broche D8 (entrée capture) est bien en entrée
pinMode( 8, INPUT );
// Configuration de la broche 10 pour générer des impulsions de test
digitalWrite( 10, LOW );
pinMode( 10, OUTPUT );
}
unsigned int test;
void loop()
{
if ( NbCapture == 2 )
{
Serial.print( "Mesure: " );
Serial.print( precision*(Capture[1] - Capture[0]) );
Serial.println( "us" );
delay( 1000 );
NbCapture = 0;
}
else
{
// génération d'une impulsion de test sur la pin 10 reliée à la pin 8
test = rand()>>2 ; // 0..16383 - limite supportée par delayMicroseconds()
Serial.print( "Genere: " );
Serial.print( test );
Serial.println( "us" );
digitalWrite( 10, HIGH );
digitalWrite( 10, LOW );
delayMicroseconds( test );
digitalWrite( 10, HIGH );
digitalWrite( 10, LOW );
}
}
barbudor:
J'ai vérifié de nombreuses mesures à l'analyseur logique : la mesure est bien exacte à 0,5µs près.
Bonjour barbudor
un peu HS mais pas trop
Je ne sais plus où sur le forum tu a annocé que tu avais reçu un petit analyseur logique.
J'ai promis à un de mes neveux de lui en offrir un
Pour mes petites bidouilles perso, j'utilise un petit ikalogic, http://www.ikalogic.com/ikalogic-products/scanalogic-2/
j'en suis bien satisfait et ça a été développé par un petit jeune bien reactif.
le mode replay/generateur est aussi bien sympa
mais si le nombre de voies est suffisants pour les protocoles les plus courants, 4 voies est quand même quelquefois limite.
Perso si j'ai besoin de plus de voies je tape dans mon materiel pro, mais cela ne sera pas le cas pour mon neveu.
Tu peux faire un petit feedback de tes premières utilisations sur le tien ?
merci amplement d'avoir répondu, vous m'avez apporté une aide précieuse. Je n'ai pas eu encore le temps de tester mais d'après la lecture des codes je pense que c'est tout à fait bon.
Cependant par simplicité, j'ai songé à remplace le "capteur2" par un bouton poussoir sur lequel j'ai collé un disque rond simple : la bille passe dans le premier laser et active le timer, ensuite pour le couper elle appuie sur le bouton poussoir, la mise en oeuvre paraît beaucoup plus simple mais j'ai peur pour le BP au bout d'un moment XD.
C'est déjà mieux. Et c'est encore mieux en utilisant la lib digitalReadFast.
Avec des interruptions c'est pas mal non plus :
Impulsion de début sur pin D2 (obligatoire)
Impulsion de fin sur pin D3 (idem)
Je n'ai pas trop compris sur le fait de la librairie que je n'ai pas trouvé, pour les pin D2 et D3 : j'ai une duemilanove j'ai que pin 2 et 3 je pense que ces ceux-là que tu sous-entendais.
C'est une lib qui fournit des fonction digitalWriteFast et digitalReadFast() beaucoup plus rapide que celles d'origine
Surtout si tu définit les numéros de pin par un #define au lieu d'une variable
Donc dans le cas de la boucle
while ( digitalReadFast( 2 ) );
La boucle s'exécute beaucoup plus rapidement et tu diminue le risque de rater une impulsion très brève (je suppose qu'une bille qui passe à 100m/s devant un capteur laser ca fait une impulsion très très courte.
Si ta bille fait 15mm de diamètre, l'impulsion devrait faire en gros (15.E-3 / 100) * 1 = 150 microsec.
Bon, d'accord, pas si court que ca
Merci de ta réponse rapide, pour le 100 m/s dis toi que c'est un bon grosso modo parce-qu'en réalité une bille de 6mm en sortie de canon est dans les environs de 120m/s mais comme tu peux supposer il y en a pour 50µs.
Je vais essayer avec cette librairie et le code que tu m'as donné, je te tiens au jus !
#include <digitalWriteFast.h>
#define BEGIN 2
#define END 3
unsigned long tDebut, tFin;
volatile boolean Fait = false;
void itDebut()
{
tDebut = micros();
}
void itFin()
{
tFin = micros();
Fait = true;
}
void setup()
{
attachInterrupt( 0, itDebut, RISING ); // Sur pin D2, RAISING ou FALLING au choix
attachInterrupt( 1, itFin, RISING ); // Sur pin D3
Serial.begin(9600);
}
void loop()
{
while( !Fait );
Serial.print(" temps en us=" );
Serial.println( tFin - tDebut );
}
et j'obtiens :
temps en us=4294946788
temps en us=4294946788
temps en us=8428204
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=4294932244
temps en us=1766960
temps en us=4294940924
temps en us=4294940924
temps en us=4294940924
Déjà, si le sketch affiche des valeurs en continue c'est pas ce que tu ne remet pas à zéro la variable fait.
Donc une fois que çà a été fait une fois, çà continue...
De plus tu as peut être des rebonds sur les entrées.
je te suggère la modif suivante pour vérifier cela :
#include <digitalWriteFast.h>
#define BEGIN 2
#define END 3
#define LED_DEBUT 12
#define LED_FIN 13
unsigned long tDebut, tFin;
int compteurDebut = 0, compteurFin = 0;
void itDebut()
{
int t = millis();
if ( !compteurDebut )
tDebut = t;
++compteurDebut;
digitalWriteFast( LED_DEBUT, HIGH );
}
void itFin()
{
int t = millis();
if ( !compteurFin )
tFin = t;
++compteurFin;
digitalWriteFast( LED_FIN, HIGH );
}
void setup()
{
digitalWrite( LED_DEBUT, LOW );
digitalWrite( LED_FIN, LOW );
pinMode( LED_DEBUT, OUTPUT );
pinMode( LED_FIN, OUTPUT );
attachInterrupt( 0, itDebut, RISING ); // Sur pin D2, RAISING ou FALLING au choix
attachInterrupt( 1, itFin, RISING ); // Sur pin D3
Serial.begin(9600);
}
void loop()
{
Serial.println( "Pret..." );
while( !compteurFin );
Serial.print(" temps en us=" );
Serial.println( tFin - tDebut );
// Debug : vérification des rebonds
// On attend un peu pour être sur
delay( 1000 );
Serial.print( "Compteur debut = " ) ; Serial.println( compteurDebut );
Serial.print( "Compteur fin = " ); Serial.println( compteurFin );
compteurDebut = 0;
compteurFin = 0;
digitalWrite( LED_DEBUT, LOW );
digitalWrite( LED_FIN, LOW );
Serial.println( "\n----------------------------" );
}
Si tu n'as pas de rebond sur les entrées, les 2 compteurs, debut et fin, doivent valoir respectivement 1.
S'ils valent plus, tu as un problème de rebond.
Mais j'ai rajouté un filtrage soft qui devrait être suffisant sauf si tu as des parasites. Dans ce cas les mesures se font n'importe quand.
J'ai aussi ajouté 2 leds qui debraient s'llumer respectivement sur le passage à debut et a fin.
Si elles s'allument avant, c'est que tu as des parasites et que les mures sont donc fausses.
Comment captes tu le passage de la bille ?
Optique : ne devrait pas faire de rebonds
Microrupteur : risque de rebonds.
par rayon IR donc je pense que c'est du domaine de l'optique, je vérifie !
EDIT : ça ne marche pas, ça reste sur prêt et puis voilà aucune détection juste led de fin qui s'allume...
Je pense que c'est un probleme car quand un obstacle survient il envoie plusieurs 1, peut-être de là vient le problème
Sur ta première série on voit que le compteur de début reste à 0 et que seul celui de fin s'incrémente.
Ca veut dire que tu ton interruption sur le capteur de début ne marche pas.
Donc seul le temps de fin est mis à jour et la différence est calculé avec une valeur constante.
Donc normal que ca augment a chaque fois.
Et c'est fou le nombre de doublons que tu récupère. Ce n'est pas normale avec un capteur optique. Ca devrait être franc.
Dans la 2eme série, on a des nombres entre 44 et 48 us. Sur 5cm çà donne une vitesse de l'ordre de 1100m/s !!!!
Y'a un blème quelque part.
tu avais mis en millis() donc c'est tout à fait raisonnable je pense
Autre série après débogage :
Pret...
temps en us=42812
Compteur debut = 1
Compteur fin = 1
----------------------------
Pret...
temps en us=4294941824
Compteur debut = 1
Compteur fin = 1
----------------------------
Pret...
temps en us=41388
Compteur debut = 1
Compteur fin = 1
----------------------------
Pret...
EDIT : je suis un bléro, j'avais pas vu que tu avais mis en millis() ...
Blaireau moi-même, je savais que tu cherchais à mesurer des micros()
Il y a quand même un problème sur la mesure du milieu.
la grande valeur signifie que la mesure est fausse et qu'on soustrait un nombre plus grand.
En gros soit tu a inversé la position des capteurs soit il y a eu un parasite qui a fait prendre une mesure de FIN avant que la bille passe devant le capteur DEBUT.
Mais étrange car tu n'as pas eu de vrai mesure de FIN sinon le compteur de fin aurait été double.
Bon sinon je pense que tu as compris le principe et que ca roule (ta bille) maintenant