Go Down

Topic: Méthodes de mesure de fréquence et résultats imprécis (Read 3243 times) previous topic - next topic

68tjs

Quote
J'ai simplement utilisé analogWrite pour définir une PWM sans passer par les timers, aspect sur lequel je dois encore bûcher...
On ne s'est pas compris parce que tu manquais de connaissances dans la PWM sur 328p

1) analogWrite manipule les timers pour toi
2) sur un 328p 1 timer peut contrôler 2 sorties (bien définies)

Donc le timer 0 qui sert au fonctions millis, micro et delay contrôle aussi 2 sorties pour faire de la PWM.
Au passage il peut faire autre chose que de la PWM . Mais n'est pas géré par les fonctions Wiring/arduino, c'est un autre sujet.

En pj je te mets une anti-sèche perso qui, par timer, donne les sorties contrôlées avec leur nom officiel Atmel et le pseudo donné par Wiring/arduino.

Donc je te propose de refaire la manip en utilisant 1 des deux sorties contrôlée par le timer 0.

inryjo

On ne s'est pas compris parce que tu manquais de connaissances dans la PWM sur 328p

1) analogWrite manipule les timers pour toi
2) sur un 328p 1 timer peut contrôler 2 sorties (bien définies)

Donc le timer 0 qui sert au fonctions millis, micro et delay contrôle aussi 2 sorties pour faire de la PWM.
Au passage il peut faire autre chose que de la PWM . Mais n'est pas géré par les fonctions Wiring/arduino, c'est un autre sujet.

En pj je te mets une anti-sèche perso qui, par timer, donne les sorties contrôlées avec leur nom officiel Atmel et le pseudo donné par Wiring/arduino.

Donc je te propose de refaire la manip en utilisant 1 des deux sorties contrôlée par le timer 0.

J'obtiens 980Hz sur mes sorties liées au timer 0, ce qui d'après la littérature est normal. Où veux-tu en venir ?

68tjs

Quote
J'obtiens 980Hz sur mes sorties liées au timer 0, ce qui d'après la littérature est normal. Où veux-tu en venir ?
Puisque tu trouvais 500 Hz au lieu de 490 avec millis()
Quote
Si c'est le cas, pourquoi avec millis j'ai un 500Hz tout rond ?
et comme tu n'avais pas répondu précisément à ma proposition d'utiliser le même timer pour la fonction millis et la PWM et que la documentation Arduino est obscure et incomplète  j'en ai remis une couche avec un peu de doc personnelle.

Par contre j'ai des doutes sur la mesure de 980 Hz, avec une sortie liée au timer 0 c'est normalement 490 Hz. Je pense plutôt à une sortie liée au timer 1.              

Super_Cinci

Salut à tous.

Sans trop rentrer dans les détails (je vais être en retard au boulot), le 328 a une pin faite pour la mesure de fréquence : ICP1 (la N°8 si je ne me trompe). Cela utilise le timer 1 (comptage en 16 bits) en hard, et la mesure du delta t devient donc une simple soustraction 16 bits à faire dans une interruption (TIMER1_ICP_vect). qui dit interruption dit donc traitement "caché".

Bref, la variable delta_t sera mise à jour automatiquement à chaque impulsion, avec une excellente précision, et accessible tout le temps. Le calcul de la vitesse de rotation restera quand même une division entière à faire dans le loop(), mais j'aurais tendance à dire que c'est la solution la plus économique, puisque cette pin a été mise là pour ça.

Se référer au datasheet, voire même sur le site d'ATMEL qui propose (en cherchant bien) des AN (Application Notes) très explicites sur l'utilisation des ressources internes de leurs processeurs...

Bonne journée!


inryjo

Merci encore de vos réponses toutes plus utiles les unes que les autres !

68tjs, si je me réfère à ça 980Hz semble normal.

Super_Cinci, si j'ai bien compris l'utilisation de cette méthode permet donc de libérer les entrées d'interruptions ?

sylvainmahe

"la mesure du delta t devient donc une simple soustraction 16 bits à faire dans une interruption (TIMER1_ICP_vect)"

inryjo, non car il faut quand même exécuter du code dans une interruption. Ceci étant dit, Super_Cinci à raison, c'est la meilleure façon de mesurer une fréquence.


Quelques précisions:
Quand une interruption est créée, le microcontrôleur s'arrête de lire ton programme la ou il est, il exécute juste le bout de code écrit dans l'interruption, puis continue à lire ton programme la ou il s'est arrêté.

Le temps qu'il lise ce qu'il y a dans l'interruption, sera du temps en moins non comptabilisé et une source d'erreur et de précision dans le reste de ton programme si il est mal conçu. Mais en général une interruption avec quelques lignes ne pose pas de problème.

Il y a différentes méthodes évoqués dans le datasheet du 328p si vraiment il y a problème, notamment sauvegarder la valeur du registre 0x5f (sreg) avant l'interruption, et lui restituer sa valeur après l'interruption. Pour ma part je n'ai jamais eu besoin de faire cela, sauf lorsque c'est obligatoire et précisé dans le datasheet, notamment pour la lecture/écriture dans la mémoire eeprom...
www.sylvainmahe.site

kamill

Bonjour,

En C/C++ c'est le framework qui s'occupe de sauvegarder/restaurer sreg (entre autres).

inryjo

Je ne vois pas bien ce que tu veux faire en dévalidant les interruptions dans la loop. Tu va perdre des front et tu risque de mesurer plusieurs période à la fois.

Actuellement tu calcules la fréquence toutes les 2ms (à 500Hz), c'est sans doute inutile d'avoir un calcul si souvent.
Ce que tu peux faire c'est par exemple mesurer 10 périodes consécutives, ce qui nécessite des calculs beaucoup moins fréquent et en plus améliore la résolution.

Code: [Select]
void rpm_pulse()
{
 static int cpt=0;
 if (++cpt>=10)
 {
   unsigned long end=micros();
   elapsed = end - start;
   start = end;
   freq = 10000000.0 / elapsed;
   rpm = freq * 30;
   cpt=0;
 }
}


J'ai intégré ce code qui donne les résultats escomptés, mais je n'arrive pas à trouver une astuce pour remettre à 0 quand je déconnecte mon entrée à ma sortie PWM. Je comprends bien que c'est dû au fait que sans impulsion il n'y a pas de mise à jour de la variable elapsed. Comment fait-on ?

Pour la mesure ICP, j'y songerai dans un projet futur mais pour l'instant tout est intégré dans un PCB donc sans pontages dégueulasses, c'est pas possible de rediriger la sortie des composants de mise en forme du signal vers la pin 8.

kamill

C'est le problème de la mesure de la période. Quand le signal s'arrête, la période reste à la dernière période mesurée.

Dans la boucle principale il faut tester si la dernière mesure n'est pas trop ancienne. Par exemple:
Code: [Select]
if (rpm!=0 && micros()-start>1000000)  // ajuster en fonction de la période max
 rpm=0;

 

68tjs

Quote
68tjs, si je me réfère à ça 980Hz semble normal.
Exact, j'ai inversé : c'est la PWM du timer 1 qui est à la fréquence moîtié car elle n'est pas en mode "fast PWM" mais en mode "Phase correct PWM"

Super_Cinci

J'ai intégré ce code qui donne les résultats escomptés, mais je n'arrive pas à trouver une astuce pour remettre à 0 quand je déconnecte mon entrée à ma sortie PWM. Je comprends bien que c'est dû au fait que sans impulsion il n'y a pas de mise à jour de la variable elapsed. Comment fait-on ?
Si on prépare bien la config du timer 1, il est possible d'utiliser une autre interruption (TIMER1_OVF_vect) : quand le timer a compté jusqu'à 65535 et que sur ICP il ne s'est rien passé, alors on dit "Bon, là, ça fait trop longtemps qu'il ne s'est rien passé, alors la prochaine mesure comptera pour du beurre, on va considérer que le moteur est à l'arrêt".

La méthode ICP est très ancienne, elle est apparue le lendemain de l'intégration d'un compteur hard dans un µC, car ça tombait sous le sens... Ca permet de faire tourner un programme tout en ayant une variable qui se met à jour toute seule en temps réel, et au moment où on la lit, elle a la valeur la plus récente (C'est d'ailleurs la première utilité d'une interruption...). Une interruption, c'est la secrétaire du Boss : elle prend les appels et note sur des post-it. le patron (le programme principal) n'a plus qu'à relever les post-it de temps en temps, et tant pis s'il en rate quelques-uns...

Mais ça demande de lire quelques pages (en anglais  >:( ) si on ne connaît pas trop les µC, et là, ben on peut rien pour vous, à part vous conseiller de lire, apprendre... enfin voilà quoi...

sylvainmahe

www.sylvainmahe.site

kamill

Le framework Arduino/Wiring
Voir un bon article sur les interruptions ici. On voit que sreg est poussé dans la pile

sylvainmahe

#29
Sep 01, 2016, 11:13 am Last Edit: Sep 01, 2016, 11:18 am by sylvainmahe
Désolé pour le hors sujet mais, mon pti kamill, je n'utilise aucun framework:
http://sylvainmahe.xyz
Je programme mon atmega en c++ pur sans bibliothèque, donc effectivement c'est moi qui écrit:

Code: [Select]
#define _SREG (*(volatile unsigned char *)(0x5f))

Je l'ai appelé comme dans le datasheet du 328p, mais j'aurais pu écrire:

Code: [Select]
#define monculsurlacommode (*(volatile unsigned char *)(0x5f))

Et ensuite:

Code: [Select]
const unsigned char SREG_SAVE = monculsurlacommode;

monculsurlacommode &= ~0b10000000;

//blabla code

monculsurlacommode = SREG_SAVE;


Désolé pour le hs
www.sylvainmahe.site

Go Up