Go Down

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

kamill

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.

sylvainmahe

Ah ok désolé kamill :)

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.
www.sylvainmahe.site

inryjo

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


68tjs

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
Code: [Select]
#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.

inryjo

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 ? :D

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 :-)

68tjs

Quote
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.

68tjs

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.

kamill

Bonjour 68tjs,

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

inryjo

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é:
Code: [Select]
#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 ?


68tjs

Bonjour 68tjs,

Je pense que tu t'es mélangé les pinceaux avec ce sujet  :)
C'est pas beau de vieillir :smiley-kiss:

68tjs

Quote
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.

kamill

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.

sylvainmahe

#43
Sep 09, 2016, 12:12 pm Last Edit: Sep 09, 2016, 12:18 pm by sylvainmahe
Si tu veux savoir en temps réel la quantité de sram qu'il te reste (code posté par Adafruit):

Code: [Select]
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).
www.sylvainmahe.site

Go Up