Méthodes de mesure de fréquence et résultats imprécis

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"

inryjo:
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...

kamill, quel framework? :slightly_smiling_face:

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

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:

#define _SREG (*(volatile unsigned char *)(0x5f))

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

#define monculsurlacommode (*(volatile unsigned char *)(0x5f))

Et ensuite:

const unsigned char SREG_SAVE = monculsurlacommode;
	
monculsurlacommode &= ~0b10000000;

//blabla code

monculsurlacommode = SREG_SAVE;

Désolé pour le hs

Je répondais dans le cadre de la question posée par inryjo. Tu utilises ton propre framwork c'est bien et en passant bravo pour le travail.

Cependant ça ne change pas le problème le framework DOIT sauvegarder sreg car ce registre contient (entre autre) le flag de carry, le flag de zéro, et le flag négatif qui ont toutes les chances d'être modifiés dans l'interruption.

Ah ok désolé kamill :slight_smile:

J'ai conscience qu'un certain nombre de personnes ici ont des connaissances pointues au-delà d'arduino, c'est vraiment une bonne chose je trouve, vous en faites partie.

La question qui se pose à présent c'est, est ce que inryjo dois commencer à lire les datasheet des microcontrôleurs afin de passer un petit peu la barrière d'arduino, et arriver à ses fins, ou est ce qu'il doit utiliser un code tout fait pour y arriver également.

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!

Bonjour, je reviens suite à cette suggestion qui m'intrigue.
En cherchant sur le web je trouve très peu d'applications de cette méthode.
Peut-être cet exemple mais qui est en programmation "dure" et qui ne donne rien quand je l'exporte d'ans l'IDE arduino.
En tant que débutant, je dois avouer que cette gestion directe des timers est plutôt indigeste...

voir ici en français ...

Est-ce que tu as essayé de lire (à petites doses) la datasheet du micro ?

Ce n'est indigeste qu'au début puis, cela ne deviens pas limpide il ne faut pas exagérer, mais exploitable.

La difficulté est tu a choisi de faire des choses complexes pour un débutant.
C'est bien connu que la meilleure façon de faire est de commencer par des exemples simples avant d'attaquer le plus délicat mais ce n'est pas pour autant hors de portée.

L'important est de comprendre que les timers (comme l'I2C, comme l'UART et le SPI) sont des modules électroniques indépendants de la zone de programmation. L'avantage est que l'électronique est plus rapide que la programation et qu'elle vit sa vie indépendamment du programme donc elle ne le ralentit pas.
Le seul contact avec le programme se fait par les registres.

C'est quoi un registre ?
Dans un programme quand tu déclares une variable le compilateur la place en mémoire là où cela l'arrange, généralement là où il y a de la place libre.
Cela convient très bien à un programme mais pas du tout aux modules électroniques (timer, etc ) qui ne peuvent fonctionner qu'avec des adresses connues à l'avance donc fixes.
Un registre c'est une adresse mémoire FIXE, située physiquement en dehors de la zone de programmation mais accessible par la programmation .

Cela s'utilise comme une mémoire parce que Atmel à défini des #define dans des fichiers livrés avec l'avr-libc que l'IDE arduino utilise obligatoirement.
Exemple :
Le fichier iom328p.h situé (pour moi avec linux) dans
arduino-1.6.5/hardware/tools/avr/avr/include/avr/iom328p.h

On y trouve ce genre de définition

#define DDRB _SFR_IO8(0x04)
#define DDB0 0
#define DDB1 1
#define DDB2 2
#define DDB3 3
#define DDB4 4
#define DDB5 5
#define DDB6 6
#define DDB7 7

Qui défini l'adresse mémoire du registre DDRB et qui défini la valeur des mnémoniques DDB0 à DDB7.

Pour apprivoiser les registres facilement je te conseille de ne pas commencer par les timers où il sera difficile de voir d'où viennent les erreurs mais par les Entrées/Sorties.
Avec les E/S cela sera facile de vérifier ce que tu fais et tu pourra comparer avec les fonctions Wiring/arduino digitalRead ou Write.

Il y a 3 registres à gérer
DDRx (x= B,C,D pour un 328p) --> gère si sens Entrée ou Sortie
PORTx --> écrit un 0 ou un 1 en sortie (si configuré en entrée écrire un 1 (non ce n'est pas idiot) active les pull-ups)
PINx --> lit les entrées.

Dernier point : les E/S sont groupées par ports (B,C,D) qui regroupent au maximum 8 pins mais qui peuvent être incomplets. Tout est dans la datasheet, il n'y a pas de devinettes.
Note : c'est clair que l'écriture en utilisant les registres est moins simple que l'usage de digitalWrite/Read mais elle prend 10 fois moins de cycles horloge, dès fois cela peut être utile de l'avoir en tête.

Je pense qu'une fois que tu maîtrisera les registres qui commandent les E/S il sera simple d'utiliser ceux des timers.

rjnc38:
voir ici en français ...

Haaa oui c'est déjà plus clair mais ça suscite d'autres questions, sur mega2560, c'est selon eux le timer 5 qu'il faut gérer et timer 1 sur 328. Pour l'adaptation, dès que je vois un "5" je le remplace par un "1" ? Ca serait trop simple à faire non ? :smiley:

Et autre question, dans la section ICP justement il est écrit "Pour l'arduino Mega, on doit utiliser l'entrée ICP5".
Or d'après la datasheet il y a plusieurs entrées ICP sur le 2560, les autres sont-elles utilisables de la même manière ?

Merci beaucoup à 68tjs, tu apportes toujours des réponses claires et tu prends le temps de les rédiger c'est vraiment bien. Effectivement je m'attaque à quelque chose de pas forcément évident rien que via ce topic par exemple vous m'avez permis de faire la lumière sur pas mal de choses. J'ai encore pas mal de chemin à parcourir avant de tout maitriser mais les briques s'assemblent dans ma tête :slight_smile:

Or d'après la datasheet il y a plusieurs entrées ICP sur le 2560, les autres sont-elles utilisables de la même manière ?

Les autres ne sont pas forcément disponibles sur les connecteurs du circuit imprimé.

Je te propose un jeu simple :
Tu regarde dans la datasheet combien le micro 2560 possède de sorties.
Tu regardes sur la carte Méga combien il y en a effectivement sur les connecteurs.

C'est simple il n'y a pas assez de place sur les connecteurs pour tout sortir donc chez arduino ils ont fait des choix qui ne peuvent pas convenir à tout le monde malheureusement.

Pour ton appli il faut que tu télécharge le schéma électrique de la Méga, c'est la seule solution pour connaître le nom Atmel de ce qui est disponible sur les connecteurs.

RFID Complément :

Le CI communique en Serial ou SPI ou I2C
Datasheet :
http://www.nxp.com/documents/data_sheet/MFRC522.pdf

Mais la commande pour commuter en mode I2C est-elle disponible sur le Circuit imprimé ?
Il faudrait disposer d'un module pour s'en assurer.

D'un autre coté le protocole SPI est plus rapide que l'I2C.

Bonjour 68tjs,

Je pense que tu t'es mélangé les pinceaux avec ce sujet :slight_smile:

68tjs:
Les autres ne sont pas forcément disponibles sur les connecteurs du circuit imprimé.

Je te propose un jeu simple :
Tu regarde dans la datasheet combien le micro 2560 possède de sorties.
Tu regardes sur la carte Méga combien il y en a effectivement sur les connecteurs.

C'est simple il n'y a pas assez de place sur les connecteurs pour tout sortir donc chez arduino ils ont fait des choix qui ne peuvent pas convenir à tout le monde malheureusement.

Pour ton appli il faut que tu télécharge le schéma électrique de la Méga, c'est la seule solution pour connaître le nom Atmel de ce qui est disponible sur les connecteurs.

En effet, tout n'est pas relié et je constate que la localisation des pins ICP du microcontroleur sont situées près des timers qui les gèrent.

Grâce au lien de rjnc38, j'ai pu adapter l'exemple proposé:

#define BUF_SIZE 256
volatile uint16_t buf[BUF_SIZE];
volatile uint8_t buf_index;
float clock_period;
                 
void start_capture() {   
    buf_index = 0;
    cli();
    TCCR1A = 0; // normal counting
    TCCR1B = (1 << ICES1);
    TIMSK1 = (1 << ICIE1); // input capture interrupt enable interrupt 
    sei();    
    TCCR1B |= 1;
    clock_period = 1.0/F_CPU;
}
                 
ISR(TIMER1_CAPT_vect) {
   TCNT1 = 0;
   buf[buf_index] = ICR1; // input capture register
   buf_index++;
   
}   
                 
void setup() {
  Serial.begin(57600);
  pinMode(8,INPUT);
  pinMode(11,OUTPUT);
  analogWrite(11,128);
  start_capture();

}
                 
void loop() {
  int i;
  float m;
  delay(500);
  m = 0.0;
  for (i=0; i<BUF_SIZE; i++)  {
    Serial.println(buf[i]);
    m += buf[i];  
  }
  m /= BUF_SIZE;
  Serial.print("Moyenne = ");
  Serial.println(m);
  Serial.print("Période (ms) = ");
  Serial.println(m*clock_period*1000);

}

Mais sur nano, ça m'affiche 36% de mémoire dynamique occupée par juste ça. J'ai l'impression que cette solution qui allège la partie calculs a tendance à charger la mémoire en conséquence. Non ?

kamill:
Bonjour 68tjs,

Je pense que tu t'es mélangé les pinceaux avec ce sujet :slight_smile:

C'est pas beau de vieillir :kissing:

Mais sur nano, ça m'affiche 36% de mémoire dynamique occupée par juste ça. J'ai l'impression que cette solution qui allège la partie calculs a tendance à charger la mémoire en conséquence. Non ?

Et si tu optimise ?
L'optimisation n'est pas vraiment dans les gènes arduino.

Si ce que tu appelle la mémoire dynamique est la RAM tu peux essayer de diminuer le nombre de variables : plein de variables c'est plus clair mais cela bouffe de la place.
S'il n'y a pas de risque que cela se morde la queue le même emplacement mémoire peut servir à plusieurs choses.
Il y a aussi toutes les sorties Serial.print : 1 octet par caractère + le caractère de fin de chaine
Pense à la macro F qui déplace en flash.

inryjo:
Mais sur nano, ça m'affiche 36% de mémoire dynamique occupée par juste ça. J'ai l'impression que cette solution qui allège la partie calculs a tendance à charger la mémoire en conséquence. Non ?

Bonjour,

C'est ton buffer buf qui occupe 512 octets soit 25% de la mémoire.
Dans ton programme, il ne sert à rien, il suffit de faire directement la somme. Et c'est indépendant du fait que tu utilise le mode ICP ou une autre méthode de mesure.

De plus comme tu ne testes pas la valeur de l'index dans buf, ton programme va complètement planter à courte échéance.

Si tu veux savoir en temps réel la quantité de sram qu'il te reste (code posté par Adafruit):

signed int sram()
{
	extern signed int __heap_start;
	extern signed int *__brkval;
	signed int *value = 0;
	
	return (signed int)(&value) - (__brkval == 0 ? (signed int)(&__heap_start) : (signed int)(__brkval));
}

Quand le nombre par en négatif, c'est l'overflow 8)

Mais je pense que tu es loin du compte, pourquoi t’inquiètes-tu de ta mémoire?

Pour le reste:
Quand le compilateur optimise pour la vitesse, y en a + en mémoire flash, et quand il optimise pour la mémoire, y a moins de vitesse d’exécution (en général, tout dépend du code aussi).