Plus d'info sur l'utilisation des Timer par l'environnement Arduino

Zenol:
Et si tu n'utilise pas les fonctions delay/ms et que tu fait tout à la main, tu peux utiliser tout les timers :wink:

Effectivement! Il faut savoir que chaque port série a son propre timer, le WD aussi, et l'utilisation des trois timers du UNO n'affectera personne d'autre que les fonctions pompeuses analogWrite(), delay(), millis(), etc du core arduino... que l'on n'utilise finalement que très peu et dont on peut se passer surtout si on a un DS1307 en RTC externe. Il en va de même pour les INT et PCINT.

En utilisant judicieusement les INT externes et timers, le core arduino peut retourner se coucher.

Super_Cinci:
Effectivement! Il faut savoir que chaque port série a son propre timer, le WD aussi, et l'utilisation des trois timers du UNO n'affectera personne d'autre que les fonctions pompeuses analogWrite(), delay(), millis(), etc du core arduino... que l'on n'utilise finalement que très peu et dont on peut se passer surtout si on a un DS1307 en RTC externe. Il en va de même pour les INT et PCINT.

Et quand on découvre <util/delay.h> on n'as même plus besoin de ce casser la tête :wink:
http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

Super_Cinci:
En utilisant judicieusement les INT externes et timers, le core arduino peut retourner se coucher.

Des fois je me dit que coder en "arduino" est vraiment pas plus compliqué que coder en Avr-C ...
Franchement la team arduino ferait une doc des registres/macro/fonctions les plus utilisé ça passerai comme une lettre à la poste

Ouaaouu,

Le sujet passionne les foules !!

Zenol:
À quel fréquence veut tu utiliser ton interruption?

Je souhaite mesurer les signaux d'un recepteur de modelisme (6 voies), ce sont des pseudo PWM avec un état haut qui varie entre 1000µs et 2000µs (point milieu à 1500µs), et j'aimerai les mesurer avec une précision de 1µs.

Je dois générer 4 voies de sortie de même type (signal servo).

Plus de précision sur mon projet ici, but in Anglishe :

(j'aimerai l'héberger ailleurs mais je ne sais pas encore où...)

skywodd:
Des fois je me dit que coder en "arduino" est vraiment pas plus compliqué que coder en Avr-C ...
Franchement la team arduino ferait une doc des registres/macro/fonctions les plus utilisé ça passerai comme une lettre à la poste

On peut uiliser une Adruino duemilanove sans l'environnement arduino ?

skywodd:
Et quand on découvre <util/delay.h> on n'as même plus besoin de ce casser la tête :wink:
avr-libc: <util/delay.h>: Convenience functions for busy-wait delay loops

J'ai pas encore tout bien compris mais je sens que cette librairie pourrait être l'étape intermiédiaire pour moi entre la librairie servo et le codage direct avec les registres...

Super_Cinci:
...sur la MEGA où on a accès à 4 timers 16 bits + 2 x 8 bits complètement indépendants

Indépendants oui et non, ils dépendent quelque par un peu les uns des autres.
L'utilisation du timer0 par l'environnement Arduino "consomme" du temps de proc par le biais d'interruption... si mon interruption intervient simultanément mais arrive après dans la file, je vais devoir attendre et ma mesure de temps sera faussée, je ferai donc mieux de "démonter" l'utilisation du timer0 de l'environnement Arduino... non ?

Sev

UniseV:
On peut uiliser une Arduino duemilanove sans l'environnement arduino ?

Bien sur, une carte arduino c'est avant tout un bête ATmega :wink:
Il suffit de savoir utiliser les outils en ligne de commande, comme avrdude par exemple :wink:

UniseV:
J'ai pas encore tout bien compris mais je sens que cette librairie pourrait être l'étape intermiédiaire pour moi entre la librairie servo et le codage direct avec les registres...

Ce n'est pas vraiment une librairie c'est juste une série de fonctions permettant de créer des delais de quelque us à quelques ms.
Un peu comme delay() et delayMicroseconds() mais en plus léger et en moins usine à gaz.

UniseV:
L'utilisation du timer0 par l'environnement Arduino "consomme" du temps de proc par le biais d'interruption... si mon interruption intervient simultanément mais arrive après dans la file, je vais devoir attendre et ma mesure de temps sera faussée, je ferai donc mieux de "démonter" l'utilisation du timer0 de l'environnement Arduino... non ?

Regarde du côté des macro sei() et cli() qui permettent d'activer / désactiver les interruptions durant une autre interruption ou dans le code principal.

Justement, ces commandes (sei et cli), dont j'utilise le pendant en C dans mon main loop pour empecher mes fonctions d'interruption de mettre à jour les données que je lis (notament pour les données plus grandes que 8bits) me posent des interrogations internes :

Ces "pause" concernent quel types interruptions ?
Les compteurs continuent de s'incrémenter normalement ? (l'incrémentation des compteurs c'est bien fait par une interruption ?)
Et les interruption de type counter overflow, elle peuvent intervenir ?
Toutes les interruptions qui auraient du arrivent pendant cette période sont-elle jetées à la poubelle ou certaines sont-elles mises en file d'attente ?

Je relis régulièrement la datasheet du 328P sur ces points, et je ne suis pas toujours éclairé.

Sev

UniseV:
Ces "pause" concernent quel types interruptions ?

Toute les interruptions quels quelle soit, de type ISR (ou anciennement SIGNAL).

UniseV:
Les compteurs continuent de s'incrémenter normalement ?

Les compteurs TCNxx sont directement relié à l'horloge du microcontrôleur.
Ils s'incrémentent tant que leurs prescaler les reliant à l'horloge du µc est différent de 0 (0 = déconnecté de l'horloge).

UniseV:
(l'incrémentation des compteurs c'est bien fait par une interruption ?)

Non c'est matériel, il n'y as pas d'interruptions.
Si il devait y avoir une interruption à chaque tick de l'horloge cpu on ne pourrais rien faire :wink:

UniseV:
Et les interruption de type counter overflow, elle peuvent intervenir ?

Non, l'interruptions sera généré mais le morceaux de code lié ne sera pas appelé par le cpu.
SEI et CLI permettent d'autorisé ou d'interdire toute les interruptions au niveau du cpu.

UniseV:
Toutes les interruptions qui auraient du arrivent pendant cette période sont-elle jetées à la poubelle ou certaines sont-elles mises en file d'attente ?

Elles sont ignoré comme si elles n'avait jamais existé, les compteurs continue de s'incrémenter sans rien faire d'autre, etc ...

Merci à tous, la brume se dissipe.

Finalement, au niveau des timer/counter, le "core arduino" ne consomme pas de ressource, il ne fait que paramétrer le timer0 pour ses besoins.

Je peux donc disposer à ma guise des 3 timers à partir du moment ou je n'utilise pas les fonctions suivantes :
micros(), millis(), delay(), analogWrite() qui utilise le timer0
La librairie Servo qui utilise le timer1

Sev

analogWrite() qui utilise le timer0

Je n'avais pas compris comme cela , (mais c'est pas pour autant que j'ai raison) :
Merci d'avoir lancé ce sujet il est très intéressant.

• INT1/OC2B/PCINT19 – Port D, Bit 3
INT1, External Interrupt source 1: The PD3 pin can serve as an external interrupt source.
OC2B, Output Compare Match output: The PD3 pin can serve as an external output for the
Timer/Counter0 Compare Match B. The PD3 pin has to be configured as an output (DDD3 set
(one)) to serve this function. The OC2B pin is also the output pin for the PWM mode timer
function.
PCINT19: Pin Change Interrupt source 19. The PD3 pin can serve as an external interrupt
source.

• MOSI/OC2/PCINT3 – Port B, Bit 3
MOSI: SPI Master Data output, Slave Data input for SPI channel. When the SPI is enabled as a
Slave, this pin is configured as an input regardless of the setting of DDB3. When the SPI is
enabled as a Master, the data direction of this pin is controlled by DDB3. When the pin is forced
by the SPI to be an input, the pull-up can still be controlled by the PORTB3 bit.
OC2, Output Compare Match Output: The PB3 pin can serve as an external output for the
Timer/Counter2 Compare Match. The PB3 pin has to be configured as an output (DDB3 set
(one)) to serve this function. The OC2 pin is also the output pin for the PWM mode timer
function.

A ce que je comprend de la datasheet j'ai l'impression que la pmw obtenue à partir du port B utilise le timer 2 et que celle obtenue à partir du port C utilise le timer 0.
En fait il faudrait un timer différent par port.
Les deux sont des timers 8 bits, seul le timer 1 (16bits) semble totalement libre.

Aurais-je raison ou me mettais-je le doigt dans l'oeil jusqu'au coude ?

skywodd:

UniseV:
Toutes les interruptions qui auraient du arrivent pendant cette période sont-elle jetées à la poubelle ou certaines sont-elles mises en file d'attente ?

Elles sont ignoré comme si elles n'avait jamais existé, les compteurs continue de s'incrémenter sans rien faire d'autre, etc ...

Sauf si je me trompe, mais il me semble que le flag "interrupt" de chaque interruption est mis à 1 lorsque se produit l'évènement correspondant, ce flag n'est mis à 0 que si on écrit un 1 dedans ou dès le traitement de l'INT, ce qui fait que quand on fait un SEI() (ou en sortie d'ISR), toutes les interruptions intervenues pendant le CLI() sont traitées les unes après les autres, dans l'ordre d'importance (et non d'arrivée).

Pour ton projet, ça fait un paquet de choses à compter, je te conseillerais la mega, car les PCINT ne sont utiles que si on les traite tout de suite (il se peut que le signal ait disparu entre l'interruption et la lecture du port, dans ce cas, tu n'auras pas moyen de savoir qui a déclenché la PCINT).

Je maintiens que les timers sont indépendants, la seule chose qui les relie est le prescaler. lorsqu'on reset le prescaler, tous les timers auront un temps de retard ou d'avance sur le départ de comptage suivant (le prescaler est un bête compteur 10 bits avec l'horloge CPU en entrée (16MHz)...). Tu peux même configurer un timer pour compter les impulsions sur une pin (Tn ou n est le numéro du timer), ça se fait matériellement, et ça permet de déclencher des int (COMPA, CMPB, OVF...)au bout d'un certain nombre d'impulsions... ou encore de relever la valeur du comptage au bout d'un temps précis pour connaître la fréquence de tes impulsions. pour le comptage d'impulsion, c'est super intéressant! mais seul la broche T5 du mega est routée, les autres entrées des timers sont pas accessibles, c'est vraiment dommage, voir nul.

68tjs:

analogWrite() qui utilise le timer0

Je n'avais pas compris comme cela , (mais c'est pas pour autant que j'ai raison) :
Merci d'avoir lancé ce sujet il est très intéressant.

...Datasheet Atmega328P...

Aurais-je raison ou me mettais-je le doigt dans l'oeil jusqu'au coude ?

Je pense que nous ne parlons juste pas de la même chose, je parlais de l'utilisation faite des timer par le "core arduino", tu ne trouveras rien à ce sujet dans la Datasheet de l'Atmega, parce que Atmega ne sait pas ce que Arduino fait avec son controlleur :wink:

Super_Cinci:
Sauf si je me trompe, mais il me semble que le flag "interrupt" de chaque interruption est mis à 1 lorsque se produit l'évènement correspondant, ce flag n'est mis à 0 que si on écrit un 1 dedans ou dès le traitement de l'INT, ce qui fait que quand on fait un SEI() (ou en sortie d'ISR), toutes les interruptions intervenues pendant le CLI() sont traitées les unes après les autres, dans l'ordre d'importance (et non d'arrivée).

Ok il faudra que je sois vigilant sur ce point.

Super_Cinci:
Pour ton projet, ça fait un paquet de choses à compter, je te conseillerais la mega, car les PCINT ne sont utiles que si on les traite tout de suite (il se peut que le signal ait disparu entre l'interruption et la lecture du port, dans ce cas, tu n'auras pas moyen de savoir qui a déclenché la PCINT).

Je ne compte interrompre les interruptions ( :roll_eyes:) que une fois par trame (après la réception du dernier front), et ce uniquement dans le but de relever les valeurs, je pense sincèrement que cette "coupure" peut être légère, courte et bien positionnée.... mais ça reste bien sûr à verifier.

Je crains beaucoup plus pour la partie de recopie des canaux avec 5ms d'ecart, parce que la seule solution que j'ai trouvé pour le moment c'est qu'une interruption "ordonance" une autre interruption... tout ça avec une précision de 1 µs. :sweat_smile:

Sev

Atmega ne sait pas ce que Arduino fait avec son controlleur

Ma remarque fait suite a tes propos qui laissaient à penser que la pwm ne dépend que du core arduino qui aurait choisi d'utiliser uniquement le timer 0.

A titre personnel j'avais compris différemment : le core arduino utiliserait les fonctions hardware cablées en dur à l'intérieur du micro-controleur.
Et par conséquence le choix des timers pour la pwm ne serait pas modifiable.

La réponse que j'espère est : qu'elle est la bonne réponse ?

J'en profite pour demander comment peut-on identifier avec certitude dans la datasheet du micro-controleur si une sortie de port peut ou non faire de la pwm.

Je rejoins ce que dit 68jts,

les pin OCxn de Atmel correspondent aux pins PWM de l'arduino, on peut donc penser que pour faire de la PWM ils utilisent les fonctions cablés prévues dans le microcontrôleur: c'est plus simple (et plus logique)

Mais les 6 pins PWM arduino correspondent aux sorties PWM pour les TROIS timers (OC0A,OC0B,OC1A,OC1B,OC2A,OC2B). donc en plus de gérer la PWM, ces timers servent pour les fonctions millis() et dérivées?

Comment s'assurer que ces timers sont dispo pour créer des ISR d'overflow ou autres?

(ces remarques sont pour les arduino basés sur un ATmega328)

MrAlexis44600:
Comment s'assurer que ces timers sont dispo pour créer des ISR d'overflow ou autres?

il suffit de savoir sur quelles pins on fait un analogWrite(), si on utilise la pin OC1B par exemple, alors le timer1 n'est plus dispo, à moins de générer la PWM soit-même et il reste possible alors d'utiliser OCR1A pour des int internes, mais faut super bien réfléchir à la chose!

Je trouve qu'arduino est trop éloigné de l'atmega, car utiliser le datasheet atmel et la carte arduino en même temps demande une réflexion double (penser arduino et atmega en même temps, chaque fonction de l'un est différente chez l'autre), c'est une gymnastique pas facile. Mais quand on y arrive, alors on obtient une certaine fierté perso (du vécu)...

les pin OCxn de Atmel correspondent aux pins PWM

C'est ce que j'avais compris en lisant la datasheet, merci de ta confirmation.

Pour info hier soir j'ai trouvé un site donnant des explications très claires :
http://aquaticus.info/pwm

68tjs:
J'en profite pour demander comment peut-on identifier avec certitude dans la datasheet du micro-controleur si une sortie de port peut ou non faire de la pwm.

Il suffit de regarder le "pinout" du composant et chercher l'emplacement des broches annoté OCxn.
Puis de faire un tour dans les différentes rubriques timer/pwm et regarder le schéma de principe de celles ci.

MrAlexis44600:
les pin OCxn de Atmel correspondent aux pins PWM de l'arduino, on peut donc penser que pour faire de la PWM ils utilisent les fonctions cablés prévues dans le microcontrôleur: c'est plus simple (et plus logique)

La PWM "arduino" est généré en utilisant les générateurs de signaux PWM matériel, seul la librairie Servo utilise les timers "en timer" seul pour faire de la PWM logiciel.

MrAlexis44600:
Mais les 6 pins PWM arduino correspondent aux sorties PWM pour les TROIS timers (OC0A,OC0B,OC1A,OC1B,OC2A,OC2B). donc en plus de gérer la PWM, ces timers servent pour les fonctions millis() et dérivées?

Oui, cependant il ya une GROSSE astuce pour cela :wink:

Un signal PWM à toujours une fréquence fixe, en général dans les alentours de 500Hz ou 1KHz pour bien faire.
Le timer 0 est configuré pour générer les "tick" (interruption "on compare match") nécéssaire au bon fonctionnement de millis(), ...

En parallèle le générateur de signal PWM est indépendant, il est juste basé sur la fréquence/registre OCRxn du timer.
La team arduino n'as donc pas chercher bien loin, ils génèrent tout simplement le signal PWM sur OC0A et OC0B sans se soucié dans la fréquence du signal.
Ce qui laisse la partie interruption fonctionner normalement, et de faire de la pwm "pas trop mal" en même temps.

MrAlexis44600:
Comment s'assurer que ces timers sont dispo pour créer des ISR d'overflow ou autres?

Il n'existe pas de fonction hardware ou software pour cela.
Il faudrait lire la doc du core arduino en profondeur ... si elle existait ...

Je n'ai pas compris les dernières explications: le timer0 est utilisé pour appeler des ISR pour la fonction millis... et la PWM? elle est fabriquée dans l'ISR du coup? car on ne peut pas utiliser le fonctionnement interruption et pwm en même temps, il y une configuration bien précise des registres pour chaque utilisation...

Donc si je n'ai pas tout faux jusque là (c'est à dire si la PWM est fabriqué dans la routine d'interruption) pourquoi est elle associée aux pins correspondants OC0A et 0COB alors qu'ils auraient pu les laisser parfaitement libre...

(D'ailleurs je trouve qu'il est dommage de pas pouvoir générer plus de PWM / de signaux de servos, si on utilise les 3 timers on doit bien pouvoir dépasser les 12 signaux... et atteindre les 19:le nombre d'I/O!)

Bon en relisant ta réponse je viens de voir que je n'avais pas compris :sweat_smile: Donc si tu peux réexpliquer l'astuce, ça m'intéresse XD

Un signal PWM à toujours une fréquence fixe, en général dans les alentours de 500Hz ou 1KHz pour bien faire.

Attention de ne pas généraliser, ce que tu dis n'est valable que si on reste dans l' "univers de programmation arduino".
Comme il est écrit sur le site http://aquaticus.info/pwm-frequency le micro-controleur permet différentes fréquences en fonction du choix du préscaller (dans l'exemple du site la fréquence peut varier de 30 kHz à 30Hz).

En résumé il ressort de ces échanges très constructifs que mixer les univers arduino/avr-libc est super casse cou.

  • il faut bien identifier les ressources matérielles utilisées par le micro lui même, ce n'est pas simple
    --> outil : lire et relire la datasheet
  • il faut bien identifier les timers utilisés par les bibliothèques arduino, c'est malheureusement encore moins simple
    --> outil : le site arduino mais il est atrocement déficient.

Si on est obligé d'utiliser un timer déjà utilisé par les bibliothèques arduino il faut marcher sur des oeufs avant de modifier sa configuration.

Je vois là une idée d'un beau travail collectif pour créer un document qui préciserait l'état et la configuration de tout les timers pour l'ATMega328p et l'ATMega2560.

Ok après relecture de la datasheet et un peu de réflexion, je pense avoir compris commen les dev ont fait pour gérer les millis() en même temps que la pwm...

mode FAST pour la PWM, ce qui donne 2 sorties; et peut importe le mode de PWM choisie, ils ont configuré une routine d'interruption qui se déclenche au TOP (=MAX) du timer pour incrémenter des variables qui stocke le temps écoulé depuis la mise sous tension du bouzin. Et pour avoir une résolution à 4 µs près, ils regardent en plus où en est rendu le timer (entre 0 et 255)

Et comme le dis 68tjs, si on touche à la fréquence de découpage, on touche (au moins?) aux fonctions millis, micros et delay; et je trouve dommage que le site Arduino ne donne pas plus de détails, je n'ai absolument pas le niveau pour relire le code source de analogWrite() et millis().

Pour la coupe de robotique l'année prochaine je compte mélanger code Arduino et AVR, j'essaierai de penser à faire un retour d'expérience.

68tjs:
Attention de ne pas généraliser, ce que tu dis n'est valable que si on reste dans l' "univers de programmation arduino".

Oui je reste dans le cadre "arduino", pour de la "vrai" PWM (fiable et bien conçu) on préférera des fréquences de plusieurs KHz pour quelle ne soit pas audible.
(Du reste en bricolant un peu dans le sens inverse il est possible de transformer n'importe quel moteur en instrument de musique ;))

68tjs:
En résumé il ressort de ces échanges très constructifs que mixer les univers arduino/avr-libc est super casse cou.

En résumé rien ne vaut eclipse, la toolchain avr-gcc et la doc de la avr-libc :wink:
Arduino c'est bien, mais dés que l'on veut faire quelques chose de plus poussé avr-c c'est bien mieux (et plus simple parfois).

MrAlexis44600:
Ok après relecture de la datasheet et un peu de réflexion, je pense avoir compris comment les dev ont fait pour gérer les millis() en même temps que la pwm...

Regarde dans wiring.c -> tu auras le fonctionnement de millis() et micros()
Regarde dans wiring_analog.c -> tu auras le fonctionnement de analogWrite()
Le principe est simple, le générateur de PWM est toujours en marche avec une interruption de type timer_overflow (disponible en mode FastPWM).

L'interruption incrémente le compteur de millis() et un compteur de "fraction de temps", millis() ne fait que lire son compteur, micros() lui se base sur le compteur de fraction de temps et l'état courant (à l'appel de micros()) du compteur du timer pour déduire un nombre de cycles, précis à 4µs prés.

AnalogWrite() ne fait qu'une seule chose, elle connecte la sortie du générateur de PWM à la broche OCxn en plaçant le "1" dans la case du registre qui va bien.
Et elle modifie le registre du générateur de PWM OCR0x en conséquence.

L'interruption de type 'overflow est donc toujours au même moment (à l'overflow du timer) quelque soit la valeur du générateur de PWM.
Une pierre, deux coups.

MrAlexis44600:
Et comme le dis 68tjs, si on touche à la fréquence de découpage, on touche (au moins?) aux fonctions millis, micros et delay; et je trouve dommage que le site Arduino ne donne pas plus de détails, je n'ai absolument pas le niveau pour relire le code source de analogWrite() et millis().

Le site arduino a un gros défaut, la documentation n'est que superficielle.
Pour un débutant c'est parfait, pour un utilisateurs expérimenté c'est insupportable.

En plus le code source en lui même contient des choses incohérente, des commentaires à faire pâlir un dév (du genre "this should works"), et mériterai une refonte complète sur certains points ...

MrAlexis44600:
Pour la coupe de robotique l'année prochaine je compte mélanger code Arduino et AVR, j'essaierai de penser à faire un retour d'expérience.

Mauvaise idée :wink:
Si le code doit être publié, soit tu écrit tout en arduino, soit tout en avr-c/c++ ne t'amuse pas à mélangé les genres ça ferait pas bonne figure.

skywodd:
Mauvaise idée :wink:
Si le code doit être publié, soit tu écrit tout en arduino, soit tout en avr-c/c++ ne t'amuse pas à mélangé les genres ça ferait pas bonne figure.

Mouais, mais certaines fonctions arduino comme le Serial sont bien pratiques pour débugger... et de toutes manières quand on achète les Arduino MEGA le Bootloader est installé, je voulais savoir si de base les timers étaient pré configurés.

Et puis si j'annonce du 100% avr-c/c++ je vais faire fuir tous les membres du club :stuck_out_tongue_closed_eyes: