Go Down

Topic: Compter le temps qui s'écoule dans notre vie sans utiliser millis() et micros() (Read 998 times) previous topic - next topic

TTTTT

Salut, une question qui peut paraître stupide dans la vie de tous les jours, mais dans le loop d'arduino, c'est un peu le bordel.. (pour moi, abruti, qui écoute les playoff de la NFL).

En gros, je veux seulement adresser à des variables les temps suivants;

#1 le temps qui s'est écoulé pendant que digitalRead(13) recevait quelque chose (on).
#2 le temps qui s'est écoulé pendant que digitalRead(13) ne recevait rien (off).

Merci pour le temps passé à lire ce questionnement.  :smiley-sweat:

J-M-L

Jetez un œil éventuellement à pulseIn() sinon si ça bouge lentement et que la précision n'est pas au dizieme de millisecondes près enregistrez millis ou micros avant que la pin 13 ne commence à recevoir quelque chose et quand elle a terminée et faites la différence
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

TTTTT

Quote
Jetez un œil éventuellement à pulseIn()
Merci! Je n'avais pas connaissance de cette fonction.. Si on parle d'un temps de 15 minutes, j'ai fait un truc comme ça en m'inspirant d'un post précédent de kamill;

Code: [Select]
void setup () {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(7, INPUT);
}

void loop () {
  static unsigned long temps=0;
  static unsigned long mSdebut=millis();
  static byte dR7=0;                                         

  if (digitalRead(7) == 1) { digitalWrite(13, 1); }
  if (digitalRead(7) == 0) { digitalWrite(13, 0); }
 
  if (digitalRead(7) == 1 && dR7 == 0) { mSdebut=millis(); }
  else if (digitalRead(7) == 0 && dR7 == 1) { temps=millis()-mSdebut; }
 
  dR7=digitalRead(7);
  Serial.print("temps :");
  Serial.println(temps);
   
}


Ça semble fonctionner, mais il faut maintenant banker ces valeurs pour les utiliser plus tard, pour d'autres fonctions! Une piste?


68tjs

#3
Jan 07, 2018, 11:47 pm Last Edit: Jan 07, 2018, 11:59 pm by 68tjs
Je le fais avec le registre compteur d'un timer.
Le timer0 est déconseillé car il est configuré pour les fonctions arduino.
N'importe quel autre timer convient. Le plus simple est de prendre un timer 8 bits puisque qu'avec un micro 8 bits un registre 16 bits se lit en deux temps ( 16 bits = 2 octets) et que la capacité de comptage d'un 8 bits est suffisant.

Tu règle le pré-diviseur du timer sur le rapport qui va bien pour ta mesure.
Un pré-diviseur de 1 permet de lire directement en cycles horloge.

Il suffit de lire le registre du compteur du timer avant et après l'opération à mesurer.

Pour éviter la gestion des débordement il suffit de mettre  le compteur à zéro.
TCNT2 = 0 pour le timer 2.

Code: [Select]
TCCR2B = 1 ; //Timer 2 lancé avec diviseur = 1
TCNT2 = 0 ; // Raz du compteur
digitalWrite(13,1);
nombre_cycle = TCNT2 ;


Si le temps à mesurer est plus grand il suffit de modifier le pré-diviseur.
le Timer 2 accepte N=1, 8, 32, 64, 128, 256, 1024.
Les registres à modifier sont indiqués dans la datasheet du micro.

Je n'ai pas de source exemple immédiatement sous la main mais si la lecture de la datasheet te parait obscure je ferais des fouilles dans mon disque dur.

TTTTT

Bien, sauf que je ne vois pas comment je pourrais placer le timer2 pour compter le temps à l'intérieur du code...

si je fais un truc comme ça;

Code: [Select]
void loop () {
  unsigned long  temps=0;
  unsigned long  Timer_TCNT2_debut=0;
  unsigned long  Timer_TCNT2_fin=0;
  int dR7=0;

  TCCR2B = 1;
  TCNT2 = 0;
 
  if (digitalRead(7) == 1) {
    digitalWrite(13, 1);
    Timer_TCNT2_debut=TCNT2;
    }
  if (digitalRead(7) == 0) {
    digitalWrite(13, 0);
    Timer_TCNT2_fin=TCNT2;
    }

  temps=Timer_TCNT2_debut-Timer_TCNT2_fin;
  Serial.print("temps : ");
  Serial.println(temps);


J'ai seulement un résultat style 189, 124 tout le temps, peu importe le temps que la LED reste allumé.

68tjs

Premièrement le timer 2 est un timer 8 bits soit un octet.
Utiliser un long pour le lire est une erreur. Les long c'est pour la fonction millis ou les fonctions qui utilisent millis.
Il faut utilser un type byte ou uint8_t c'est la même chose.

Deuxiemement il faut faire une raz du compteur avant chaque mesure :

TCNT2 = 0 ;
digitalWrite(13,1).
mesure_1 = TCNT2;
TCNT2=0 ;
resultat =digitalRead(13).
mesure_2 = TCNT2

On faisant ainsi j'"économise" :
une variable debut -> comme TCNT2 est forcé à 0 je n'ai pas à enregistrer cette valeur
une soustraction -> commme la valeur de départ est 0 la lecture est directe.
je n'ai pas à gérer le débordement du compteur (après la valeur 255 le registre passe à 0)

Capacité maximale de mesure :
Tout à une limite.
Un cycle horloge est égal à une période d'horloge soit T = 1/16MHz = 62,5 ns .
Un octet c'est 256 valeurs possibles.
Le prédiviseur max du timer 2 c'est 1024.
Capacité max = 256 *1024 cycles horloge -> attention la valeur pour TCCR2B que j'ai donné est pour un diviseur de 1, pour avoir 1024 il faut changer cette valeur (voir datasheet du micro).

Le timer 8 bits est adapté à lamesure des temps courts.
Si les temps à mesurer dépassent sa capacité il est préférable d'utiliiser un timer 16 bits (la capacité de mesure sera multipliée par 256) -> attention avec un registre16 bits il faut utiliser un unsigned int (ou uint16_t c'est la même chose).

Si les temps sont encore trop longs  la méthode proposée par J-M-L sera la plus appropriée


TTTTT

Ok, ça fait environ 36 heures que j'apprends à gérer les timers. Oui, j'aurais pu m'en foutre et utiliser la commande millis()... Mais non. Je veux apprendre sur la vie des Timers. Ils sont peut-être vivants et ils méritent qu'on prenne le temps de s'en occuper. Peut-être qu'au paradis, ils seront là près de moi, en me remerciant d'avoir pensé à eux. Dans la vie, je prône la reconnaissance.

Le résultat est simple; compter les secondes qui s'écoulent quand on appuie sur un bouton. C'est simple à faire? Non.

Il fallait passer 36 heures pour comprendre comment on fait sans utiliser millis().

Je ne veux pas porter un commentaire négatif sur le conseil de 68tjs, pour ce qui est du calcul mathématique, c'est raisonnable, mais concernant cette piste de code;

Code: [Select]
TCNT2 = 0 ;
digitalWrite(13,1).
mesure_1 = TCNT2;


Avec respect, ce n'est pas quelque chose qui m'a été utile de faire! Ce code prend la mesure de comptage pure du timer (avec surement un millier de "reset"). Ça ne sert absolument pas à mesurer quelque chose.

En fait, je me demandais dans quelle optique ce code pourrait servir??

Sinon, pour le résultat de mes recherches, j'ai réussi à faire ce que je voulais à la base, c'est à dire simplement compter les secondes en utilisant un des timers disponibles;

Code: [Select]

int Cycles = 0;                 // La variable qui compte les cycles 

void setup () {
  Serial.begin(9600);
  pinMode (13, OUTPUT);
 
  TCCR2A = 0b00000000;          // Configuration en mode normal
  TCCR2B = 0b00000110;          // Pprescaler 256
  TCNT2 = 6;                    // Chargement du timer à 6 (256-250).. aurait pu être à zéro, pas important??.
}

void loop () {
  if (digitalRead(7)==1){
    digitalWrite(13,1);
    if (TIFR2) {                // Si le flag TIFR2 (bit TOV2) est "ON" (à 1)
      TCNT2 = 6;                // Rechargement du timer à 6 pour qu'il recompte 250 coups.
      Cycles++;                 // Incrémentation du cycle de 1
    }
  }
  else digitalWrite(13,0);

  static long Seconde=0;
  Seconde=Cycles/250;           // (16Mhz /16) = 62 500Hz (coups de timer). 62 500 / 250 = 250Hz. Donc 250 / 250 = 1 pour 1Hz; une seconde.
  Serial.println(Seconde);      // Affiche les secondes qui ce sont écoulés pendant que digitalRead(7) recevait du courant.
}


Maintenant, si je fais un genre d'"Hello World" avec une superbe LED qui s'instille, tel mon coeur rempli de joie, en utilisant ce même timer2, configuré de la même façon que pour compter les secondes, je cherchais à comprendre un truc; à savoir pourquoi il faut remettre le flag à "1" au lieu de zéro dans un loop pour qu'il "reset" !!? Je ne comprends pas.


voir TIFR2=TIFR2;

Code: [Select]

int Cycles = 0;                 // La variable qui compte les cycles 

void setup ()
{
  Serial.begin(9600);
  pinMode (13, OUTPUT);
 
  TCCR2A = 0b00000000;          // Configuration en mode normal
  TCCR2B = 0b00000110;          // Prescaler 256
  TCNT2 = 6;                    // Chargement du timer à 6 (256-250).. aurait pu être à zéro, pas important??.
}
 


void loop () {
 
 
  if (TIFR2) {                  // Si le flag TIFR2 (bit TOV2) est "ON" (à 1)
    TCNT2 = 6;                  // Rechargement du timer à 6 pour qu'il recompte 250 coups.
    Cycles++;                   // Incrémentation du cycle de 1
    TIFR2=TIFR2;                // ?????????
     
    if (Cycles > 250) {
      Cycles = 0;               // Cycles remis à zéro.
      PINB |= 0x20;             // équivalent de digitalWrite (13, !digitalRead(13))
    }
  }
}

J-M-L

#7
Jan 13, 2018, 09:34 am Last Edit: Jan 13, 2018, 09:37 am by J-M-L
Quote
Avec respect, ce n'est pas quelque chose qui m'a été utile de faire! Ce code prend la mesure de comptage pure du timer (avec surement un millier de "reset"). Ça ne sert absolument pas à mesurer quelque chose.

En fait, je me demandais dans quelle optique ce code pourrait servir??
Comme je l'ai dit plus haut utiliser les timers directement pour mesurer un temps "long" ne presente aucun intérêt. La suggestion de 68tjs n'était là que parce qu'ils avait compris d'après le titre de votre post que vous souhaitiez mesurer le temps écoulé entre deux instructions un peu haut niveau au micro-processeur - genre voire combien de temps prends un digitalWrite(), un analogRead() ou un petit calcul mathématique par exemple.

Pour mesurer une externalité  représentée sur une pin par une tension - comme je le dis plus haut - Si c'est fugace et qu'il faut être précis en quelques micro-secondes alors pulseIn() ou des interruptions et le timer système qui compte en microSecondes sont là pour cela, et si c'est du temps long (genre bouton appuyé par l'utilisateur et plusieurs dizaines ou centaines de millisecondes et pas besoin d'une précision micro-seconde) alors jouer juste avec millis() ou micros() est largement suffisant.

Faire autrement et prendre un second timer pour cela - alors qu'il y a déjà le timer0 qui compte pour vous et est prévu pour cela dans le système c'est un peu idiot à mon sens puisque ça fait double emploi...

Maintenant si c'est pour votre karma au paradis des programmeurs et des micros-contrôleurs alors c'est une autre histoire :)

Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

68tjs

#8
Jan 13, 2018, 09:46 am Last Edit: Jan 13, 2018, 09:59 am by 68tjs
Si ce que je t'ai proposé ne te sert à rien et bien tu l'oublies.

Vu le flou pas artistique des temps que tu voulais mesurer il me semble bien avoir précisé que l'utilisation directe du compteur d'un timer était pour avoir de la précision avec des temps courts.
J'ai beau relire ton premier message il n'y est nulement fait mention de temps de l'ordre de la seconde.
C'est pourtant une information de première importance.
Donc quand on est pas précis il ne faut pas se plaindre de ne pas avoir une réponse adaptée.

Pour des temps plus long, la précision au cycle d'horloge près n'est pas necessaire et il existe d'autres méthodes dont millis() et  celle que t'a proposé J-M-L.

J'ajouterai simplement que si tu avais ouvert le fichier qui contient le code de millis() tu aurai vu que millis utilise le compteur du timer 0. Millis ne fait en plus que gérer les débordements du compteur jusqu'à une certaine liimite, liée au type de variable que renvoie millis, après millis déborde lui même.
Donc utiliser un compteur de timer ou utiliser milli() c'est la même chose.

NB : Au depart du programme la configuration générale est effectuée par la fonction init().
Tel que configuré "en mode" arduino le timer 0 ne permet pas d'avoir une précision au cycle d'horloge prés.
Pour cela il faudrait modifier son prédiviseur. C'est pour cela que j'estime que quand un autre timer est disponible (pas de PWM active entre autre) autant ne pas modifier le timer 0.
C'est un choix personnel.
Dans tout les cas il est prudent de lire et de sauvegarder le registre concerné afin de le rétablir en configuration arduino à la fin de la phase de mesure.

aligote

Coup de Gueule ON !


Voila le cahier des charges du départ :

Quote
En gros, je veux seulement adresser à des variables les temps suivants;

#1 le temps qui s'est écoulé pendant que digitalRead(13) recevait quelque chose (on).
#2 le temps qui s'est écoulé pendant que digitalRead(13) ne recevait rien (off).

Voila la réponse pour remercier  68tjs et J-M-L du temps passé :

.....
Ok, ça fait environ 36 heures que j'apprends à gérer les timers. Oui, j'aurais pu m'en foutre et utiliser la commande millis()... Mais non. Je veux apprendre sur la vie des Timers.
.....................
Je ne veux pas porter un commentaire négatif sur le conseil de 68tjs, pour ce qui est du calcul mathématique, c'est raisonnable, mais concernant cette piste de code;......
En ce qui me concerne, je considère que  ce type de remerciement est une honte !

Coup de gueule off



Serge .D

TTTTT

Mesurer le temps écoulé entre deux instructions un peu haut niveau au microprocesseur - genre voire combien de temps prends un digitalWrite(), un analogRead() ou un petit calcul mathématique par exemple.
J'ai beau relire ton premier message il n'y est nulement fait mention de temps de l'ordre de la seconde. C'est pourtant une information de première importance.
J'ai quand même spécifié pendant que digitalRead(13) recevait..., j'avoue que c'est difficile de faire un titre convenable, je voulais que ça soit simple. Pour quelle raison on pourrait avoir envie de savoir le temps que ça prend au microcontrôleur pour exécuter une fonction brute qui consiste à juste faire allumer une led?  Surtout quand la vitesse est 16Mhz, on le sait que c'est hyper rapide.  Je cherche à obtenir le temps durant laquelle la led est allumé et non l'action de le faire. Je croyais que c'était simple comme problématique... Je ne m'attendais pas à tout un charabia qui ne fonctionne même pas.  :smiley-cry:

Mais ce n'est pas grave, parce qu'on est ici pour s'amuser, boire un bon café devant son écran d'ordinateur, aider les ignorants, passer le temps et se faire à croire (confirmer) qu'on est un bon programmeur Arduino.

Faire autrement et prendre un second timer pour cela...
Une des raisons est que je ne veux pas seulement me fier aux librairies d'Arduino pour programmer un microcontrôleur, comprendre la gaffe et pouvoir le faire sur d'autres (qui ont des timers internes). Je veux aussi utiliser les timers pour avoir du PWM à une fréquence xyz et exécuter des tableaux à une vitesse xyz etc...

Je m'étais dit que de commencer par réussir à compter les secondes serait un bon début. En fait, si on pouvait changer le titre, ça serait "Compter le temps qui s'écoule dans notre vie sans utiliser millis() et micros()" soit en configurant soit même un timer pour le faire.  8)





J-M-L

#11
Jan 14, 2018, 07:05 am Last Edit: Jan 14, 2018, 07:28 am by J-M-L
Quote
pendant que digitalRead(13) recevait
Certes, mais ça ne veut absolument rien dire.... la pin 13 aura une valeur HIGH ou LOW en permanence - Elle « reçoit » tout le temps, de l'extérieur si elle est configurée en Input ou de l'interieur si Elle est configurée en OUTPUT (pas la meilleure pin vue la LED accrochée dessus sur votre arduino sans doute). De plus vous n'avez pas précisé si ça variait rapidement ou pas, d'où ma réponse #1 avec différentes options. [au passage adresser à des variables ça ne veut rien dire, on dit affecter une valeur à une variable]

Quote
Pour quelle raison on pourrait avoir envie de savoir le temps que ça prend au microcontrôleur pour exécuter une fonction brute qui consiste à juste faire allumer une led?
les membres du forum viennent ici avec toutes sortes de questions. Celle ci pourrait être de la curiosité intellectuelle ou cette pin pourrait être au bout d'une fibre optique de transmission de données et on pourrait vouloir affiner le timing, il peut y avoir 100 bonnes raisons

Quote
Surtout quand la vitesse est 16Mhz, on le sait que c'est hyper rapide.
Non c'est super lent par comparaison à l'utilisation directe des registres. tout dépend de votre besoin. Si vous devez mettre simultanément 2 pins à HIGH ou LOW avec cette digitalWrite() ou lire l'état de 2 pins simultanément avec digitalRead() - Il y aura plusieurs microSecondes d'ecart Entre les deux, ça peut créer des soucis de synchro avec un système lié par exemple.

 
Quote
Je cherche à obtenir le temps durant laquelle la led est allumé et non l'action de le faire. Je croyais que c'était simple comme problématique... Je ne m'attendais pas à tout un charabia qui ne fonctionne même pas.  :smiley-cry:
tout ce qu'on vous a dit fonctionne - y compris ma réponse #1. Utilisez votre cerveau

Quote
Une des raisons est que je ne veux pas seulement me fier aux librairies d'Arduino pour programmer un microcontrôleur, comprendre la gaffe et pouvoir le faire sur d'autres (qui ont des timers internes). Je veux aussi utiliser les timers pour avoir du PWM à une fréquence xyz et exécuter des tableaux à une vitesse xyz etc...
pourquoi n'avez vous pas commencé par expliquer cela? Vous m'avez fait perdre mon temps alors en vous expliquant comment faire avec les services disponibles en standard...

Quote
Je m'étais dit que de commencer par réussir à compter les secondes serait un bon début. En fait, si on pouvait changer le titre, ça serait "Compter le temps qui s'écoule dans notre vie sans utiliser millis() et micros()" soit en configurant soit même un timer pour le faire.  8)
Il suffit de lire le code source de millis() et micros() et la doc et nombreux tutos sur les timers, ou d'une RTC... c'est ce qu'on vous aurait dit si vous aviez posé cette question... on vous aurait aussi dit que c'est tout à fait bien à titre de curiosité intellectuelle mais complètement idiot dans la majorité des cas de réinventer la roue puisque dans l'environnement Arduino - je rappelle que c'est le forum de programmation Arduino ici - ces services sont déjà implémentés pour vous, les ré-écrire ne ferait donc que double emploi et ralentirait un peu plus le système, tout en perdant un second timer qui peut être utile à autre chose, genre gérer un Servo... donc à moins d'avoir un besoin qui le justifie on vous aurait dit de ne pas aller dans cette direction...



Bref vous avez posé une question, la formulation était ce qu'elle était, vous avez eu des réponses précises pour cette question, avec des alternatives à explorer, de la part de bénévoles qui ont pris de leur temps et qui souhaitaient vous aider et vous ne semblez faire que peu de cas pour participer intellectuellement à une discussion suivie sur votre question...

Donc pour faire simple, je,quitte ce thread chronophage et inutile
 
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

TTTTT

Bien, mon but n'était pas que ça vire en chicane de programmeurs saouls..

Néanmoins, merci pour vos pistes (68tjs et J-M-L), ils ont quand même été utiles  ;D

Pour conclure le thread, si jamais quelqu'un passe par là (c'est ça le but..), j'ai réussi à construire un code qui fait exactement ce que le titre dit. En bonus, j'ai aussi trouvé la façon de ne pas utiliser bitSet et bitRead;

TIFR2 >> 0 & 0x01 qui est l'équivalent de bitRead (TIFR2, 0) == 1)
C'est pour vérifier l'état du flag; s'il est on, le timer TCNT2 repart son décompte à 6.

TIFR2 |= (1 << 0); qui est l'équivalent de bitSet (TIFR2, 0);
Sers à faire redescendre le flag pour qu'il se relève un coup que le timer arrive à 255. Tout ça pour incrémenter la variable FlagCounter2 qui compte jusqu'à 250 le nombre de flags levés.

if (FlagCounter2 > 250)
250 flags levés est équivalent à une seconde, celle qui passe dans notre vie, placée dans la variable timer2 et ensuite affichée dans le Serial. Notez qu'un reset du Serial entraîne aussi un reset de la variable, c'est seulement pour visualiser que ça marche.

Code: [Select]

unsigned int FlagCounter2 = 0;

void setup ()
{
  Serial.begin(9600);
  pinMode (13, OUTPUT);
 
  cli();
  TCCR2A = 0b00000000;          // mode normal
  TCCR2B = 0b00000110;          // prescaler 256
  TIFR2 = 0b00000001;
  TCNT2 = 6;                    // 250 coups
  sei();
   
}

void loop () {
 
  static long timer2=0;
   
  if (TIFR2 >> 0 & 0x01) {     
    TCNT2 = 6;
    TIFR2 |= (1 << 0);     
    FlagCounter2++;
     
      if (FlagCounter2 > 250) {
        Serial.println(timer2);
        FlagCounter2 = 0;       
        timer2++;         
      }
  }
}


Merci à Locoduino pour l'apprentissage des bases des timers, pas évident par contre.  ::)
http://www.locoduino.org/spip.php?article84


J-M-L

Code: [Select]
TIFR2 >> 0 ça sert à quoi de décaler de 0 bit un octet??
Code: [Select]
TIFR2 |= (1 << 0); ça sert à quoi de décaler 1 de 0 bits ?

Notez qu'il y a un flag appelé OCF2A , qui est le bit 1 du registre TIFR2 (Timer/Counter 2 Interrupt Flag Register)


bien sûr comme millis et micros sont là, ce n'est pas la peine de réinventer la roue et on peut faire
Code: [Select]
const unsigned long uneSeconde = 1000000ul; // en microsecondes

void setup() {
  Serial.begin(115200);
}

void loop() {
  static unsigned long t0 = micros();
  static unsigned long n = 0;
 
  if (micros() - t0 >= uneSeconde) {
    t0 += uneSeconde;
    n++;
    Serial.print(n);
    Serial.println(F("s"));
  }
}







Quote
Bien, mon but n'était pas que ça vire en chicane de programmeurs saouls..


 :)  8)  :smiley-mr-green:
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

TTTTT

Je ne comprends pas exactement le pourquoi, mais j'essaie. Je me base sur le #define de bitRead et bitSet..

TIFR2 >> 0 & 0x01
C'est vérifier si le bit #0 de TIFR2 qui est à '1', ça aurait pu juste être TIFR2==1 aussi, mais ça ne spécifie pas son bit exactement je crois. Sinon, c'est quoi le deal? Pourquoi ça marche? C'est quoi ça fait? Pouquoi? Dieu existe?


TIFR2 |= (1 << 0);
D'après ce que j'ai pu comprendre, c'est que tu set à '1' le bit #0 du registre TIFR2. J'ai fait des tests, si tu fais le contraire, cad placer le bit à '0', eh bien le flag reste levé en permanence.

En gros, c'étais juste de comprendre la mécanique de bitRead et bitSet;

Code: [Select]

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))



OCF2A est le flag pour le mode CTC, en mode normal, il est désactivé.


Go Up