Go Down

Topic: Réduire le temps d'itération d'une boucle sur arduino  (Read 196 times) previous topic - next topic

Timeparadox

Bonjour dans le cadre d'un projet d'étude je souhaite mesurer la vitesse d'un clou passant dans un tube éclairé, grâce a deux capteur de lumière  séparés de 20 cm (la luminosité diminue lorsque le clou passe proche des capteurs ce qui permet la mesure.


Je travail sur Arduino Uno, l'acquisition des mesures des capteur fonctionnent (j'utilise la fonction Analogue.read)

Mon probléme est que le temps d'éxécution entre chaque mesure affiché sur le moniteur série est d'environ 100 micros secondes ce qui est trop grand car sur cet interval le clou qui se déplace relativement vite peut passer sans se faire mesurer par les capteurs(le clou passe donc entre deux meusures d'un capteur).


Existe un moyen d'auguementer la vitesse d'éxécution de cette boucle
?


Je suis débutant sur arduino et vous remercie de votre temps, je fourni mon code en piéce jointe.

Cordialement Timeparadox



68tjs

Quelques pistes :
Je n'ai pas encore compris pourquoi tous les exemples fixent le débit de la liaison série à 9600 bauds.
Configure à 115200 ou plus si le moniteur le permet.
Dans le micro de la Uno la liaison série est constituée par une zône d'électronique pure ce qui ne devrait pas ralentir le programme sauf quand le début du message n entre en collision avec la fin du message n-1.

Mesure analogique :
Je vais dire des "méchantes choses" -> lis ce putain de manuel !
Et le manuel ne se trouve pas chez arduino mais chez Microchip le concepteur du microcontrôleur : c'est la datasheet du microcontroleur Atmega 328p.
Dans cette datasheet il est expliqué :
- qu'il n'y a qu'un convertisseur analogique/digital
- que ce CAD est précédé par un multiplexeur analogique sur le lequel sont raccordé A0 à A5.
- que dès que l'on change quoi que soit à l'ensemble "multiplexeur+ CAD" il faut rejeter la première mesure.
Donc changer d'entrée sans rejeter la première mesure n'est pas bon :

Code: [Select]
int a1 = analogRead(A1);
int a2 = analogRead(A2);



Fonction analogRead().
Les fonctions arduino ne sont pas optimisées ni en place mémoire ni en vitesse. Ce n'est pas le but recherché.
Le but recherché par l'univers Wiring/arduino c'est de se mettre à la portée des débutants afin que tout le monde puisse programmer quasi immédiatement.

Ton cas n'entre pas dans l'univers Wiring/arduino mais absolument rien n'est perdu.
Les microcontrôleurs Atmel n'ont pas attendu qu'Ernesto Barragan publie le projet Wiring pour être programmés.

Si la fonction analogRead() est trop lente pour ton application (cela reste à déterminer formellement) il reste la solution de lire la datasheet du micro atmega328p et de programmer le micro par ses registres. Note que c'est ce que fait la fonction analogRead() mais en plus elle passe un temps fou à vérifier que le programmeur n'a pas fait d'erreur. En utilisant directement les registres tu élimines tout ces contrôle couteux en cycles horloge. Et tu constatera que le convertisseur analogique digital a bien plus de possibilité que ce qu'en fait analogRead().

PS : pour le code utilise les balises code " </> "
De plus en plus d'utilisateurs utilisent tablettes ou téléphones, les balises sont la seule solution pour ton code soit lu.
N'oublie pas de faire régulièrement "control T" dans l'IDE pour que le code soit bien indenté ce qui améliore la lecture  et aide à trouver des erreurs de syntaxe.




Timeparadox

Merci énormément de votre réponse rapide 68tjs, comme vous avez pu le voir je débute sur Arduino et la programmation en générale. Dans votre second paragraphe, dois-je comprendre que je ne peut pas exploiter deux mesures analogique simultanément?
Ensuite pourriez-vous s'il vous plait m'éclairer sur l'utilisation des registres,mon expérience sur arduino se limite a l'utilisation de fonction pour l'instant.

cordialement timepardox



dbrion06

"Si la fonction analogRead() est trop lente pour ton application (cela reste à déterminer formellement)  il reste la solution de lire la datasheet du micro atmega328p et de programmer le micro par ses registres"

une solution intermédiaire est d'aller voir chez gammon, qui illustre chaque point de la note d'application de dessins en couleurs et d'un programme arduino (qui teste les temps d'éxécution) https://www.gammon.com.au/adc

fdufnews

L'ATmega offre d'autres solutions:
Utiliser le comparateur intégré à l'ATmega. Il compare A0 avec n'importe laquelle des autres entrées analogiques. On pourrait mettre un seuil de référence sur A0. Les 2 capteurs connectés sur A1 et A2 par exemple.
Au début on aiguille A1 sur l'entrée du comparateur.
Lorsque le comparateur bascule (sur interruption ou par polling du registre associé). Tu échantillonnes micro(). et tu changes l'entrée du comparateur sur A2
Tu attends le second basculement. Tu échantillonne micro().

Avantage, pas d'ADC le temps de boucle ne dépend que du mode de scrutation de la sortie du comparateur.

68tjs

Quote
dois-je comprendre que je ne peut pas exploiter deux mesures analogique simultanément?
Ce n'est pas ce que j'ai écrit. De toute façon en programmation tu ne peux pas faire des actions simultanées, tu ne peux que mettre en série sauf avec plusieurs cœurs ce qui revient à utiliser plusieurs microcontrôleurs en parallèle.
Il n'y a qu'en électronique que l'on peut mener des actions en parallèle. Une zone d'électronique pure peut être la liaison série, I2C, etc ou simplement les étages d'entrée/sortie du microcontrôleur.

J'ai écrit qu'il faut jeter la première mesure.
soit :

Code: [Select]
int a1 = analogRead(A1);  // On ne sait pas si le CAD n'était pas déjà en train de faire une mesure
                          // mesure non conservée.
a1 = analogRead(A1) ;     // mesure conservée
int a2 = analogRead(A2);  // changement d'entrée --> mesure non conservée
a2 = analogRead(A2);      // mesure conservée


Le lien de dbrion06 est une reprise de la datasheet par Mike Gamon.
Il est plus "agréable" à lire  mais pas forcément aussi complet car par exemple le micro de la Uno est différent de celui de la Mega. Les différences sont minimes mais un nom de registre peut être différent et ce genre d'erreur n'est pas simple à détecter.
Tu peux apprendre avec Mike Gamon et contrôler avec la datasheet qui reste le juge de paix.

Les "registres" sont des zones mémoire particulières avec des adresses fixes qui permettent au programme de converser avec les zones d'électronique pure du micro.
Pour les micro Atmel architecture avr comme sur la UNO les registres sont sur 8 bits mais avec l'architecture ARM ( cartes haut de gamme arduino) les registres sont sur 32 bits


Pour te donner une idée du gain en rapidité écrire un 1 ou un 0 sur une sortie prend 8 cycles horloges avec les registres et plus de 60 cycles avec les fonctions arduino.
Les E/S à la mode arduino sont numérotés de 0 à n, mais c'est la mode arduino.

Les concepteurs de micro font différemment.  Les E/S sont groupées dans des PORT qui pour la UNO sont 8bits
Si on veut gérer les E/S directement il faut bien entendu utiliser la dénomination du concepteur.

Pour les entrées/sorties il y a 3 registres
Les E/S sont groupées dans ce qu'on appelle des PORT, les PORTS sont dénommés avec des lettres  (A, B, C,...)
x = la lettre du port
DDRx : régle le sens Entrée ou sortie. Le bit 0 correspond à l'E/S n:0, le bit 1 à l'E/S n:1 , etc...)
DDRD = 0b00110000;
aura pour effet de mettre les bits 4 et 5 à "1" et donc de placer les E/S correspondantes en sortie. Tous les autres bits étant à "0" les E/S correspondantes seront placées en entrée.

PORTx : sélectionne la sortie (bit 0 correspond à la sortie n:0, bit 1 ...)
PORTD = 0b00100000;
aura pour effet de mettre le bit 5 à "1" et donc d'écrire un état haut (5V) dans la sortie correspondante, tous les autres bits étant à "0" les sorties correspondantes seront placé à l'état bas (0V).

PINx : permet de lire une entrée, le principe est le même sauf que là on n'écrit pas mais on lit.

Et pour apprendre à manipuler les bits d'un registre le mot clé est "bitwise"


J'ai volontairement exploré un autre domaine que celui de la mesure analogique, et j'insiste, ce n'est pas une obligation de procéder avec les registres.
Il faut déjà faire la preuve que la vitesse d'exécution de la fonction analogRead() est perturbante.
et donc de voir si une meilleures organisation du programme ne résous pas le problème.

Points aussi sur lesquels j'insiste :
- la datasheet te permettra de découvrir la totalité des possibilités du convertisseur analogique digital.
- Les serialPrint() sont-il nécessaires ?


N'y a-t-il pas une autre façon de procéder/organiser qui serait plus astucieuse ?
Comme c'est un projet d'étude je ne donnerais que des pistes, j'ai un profond respect pour l'honnêteté de la note.

bidouilleelec

Bonjour Timeparadox,

Déjà :
1/ Supprimer ou commenter les Serial.print qui ne sont pas strictement nécessaires
2/monter la videsse de la liaison série à 1000 000  bauds.

En :
_ ne gardant que
    Serial.print("vitesse  ");
    Serial.println(vitesse);
_ montant la vitesse à 1.000.000 bauds,
_mais en doublant les analogRead comme le préconise 68tjs
_en effectuant 1000 boucles,
J'obtiens un temps d'execution de 914 mICRO-secondes par boucle.

Votre code contient des erreurs.

Cordialement,
bidouilleelec

kamill

Bonjour,

Existe un moyen d'auguementer la vitesse d'éxécution de cette boucle?
Oui, actuellement à chaque boucle tu fais trois actions même si elles sont inutiles (de plus ça ne va pas fonctionner puisque temps1 est réinitialisé à chaque boucle).
- test du capteur 1
- test du capteur 2
- calcul et affichage

Il faut réorganiser ton programme pour gérer trois état
- état 0: test du capteur 1, passe en état 1 si détection
- état 1: test du capteur 2, passe à l'état 2 si détection
- état 2: calcul et affichage puis retour à l'état 0

bidouilleelec

#8
May 22, 2019, 05:42 pm Last Edit: May 22, 2019, 10:25 pm by bidouilleelec
........
Je travail sur Arduino Uno, l'acquisition des mesures des capteur fonctionnent (j'utilise la fonction Analogue.read)

Mon probléme est que le temps d'éxécution entre chaque mesure affiché sur le moniteur série est d'environ 100 micros secondes

Cordialement Timeparadox

100 micros secondes ?
Etonnant : rien qu'un appel à analogRead, c'est 112µs

Cordialement,
bidouillelec

dbrion06

"Dans le micro de la Uno la liaison série est constituée par une zône d'électronique pure ce qui ne devrait pas ralentir le programme sauf quand le début du message n entre en collision avec la fin du message n-1."

La collision se produit dès que le buffer (de 16 ou 64 caractères normalement ) est saturé (i.e dès qu'on envoye trop vite des caractères qui s'écoulenttrop lentement  (il y a le choix entre perdre des informations de debugging, si elles servent, c'est affreux, ou bloquer....). A ce stade de saturation , dans le meilleur des cas, la vitesse de transmission devient un goulet d'etranglement.

La gestion de ce buffer par file circulaire n'est pas de l'electronique pure  : on trouve, s'il y a lieu,  le prochain octet à transmettre  qui est alors copié dans un registre de l'UART ; là, l'électronique prend effectivement le relais... (et signale, soit par une interruption, soit parce qu'elle est scrutée, quand il faut rechercher un autre caractère).

Go Up