Arduino: meilleure résolution en microsecondes que micros ()?

11

La documentation de micros () note que la valeur de retour sera toujours un multiple de 4.

Existe-t-il un moyen d'obtenir un clic de microseconde de résolution plus élevée, de préférence jusqu'au niveau 1 microseconde?

La descente au niveau AVR est acceptable.

Mark Harrison
la source
1
Pourquoi insistez-vous pour commencer votre titre avec une balise alors que j'en ai fait une question parfaitement anglaise?
stevenvh
4
Excuses Steven, j'ai sincèrement pensé (et je pense) que c'est un bon moyen d'exprimer la question de manière succincte et claire. Il y a eu beaucoup de discussions à ce sujet, et Jeff a convenu dans certains cas que c'était une bonne façon de demander. La discussion est ici; Les heatmaps sont ce qui m'a convaincu que pour ce type de questions, il vaut la peine de mettre l'accent sur l'environnement d'exploitation. meta.stackexchange.com/questions/10647/writing-good-titles/… Mais, par respect pour vos excellentes réponses, je serais heureux de le modifier à nouveau si vous pensez que c'est le meilleur.
Mark Harrison
Est-il possible de l'utiliser pour la fonction pulsein ()? J'ai besoin de mesurer la longueur des impulsions dans une résolution inférieure à 1 usec.
Cet espace est réservé aux réponses à la question du haut. Si vous avez quelque chose à demander, commencez votre propre question.
Olin Lathrop

Réponses:

9

Oui, en fonction de la fréquence d'horloge de base de votre Arduino. Par exemple, voici les fréquences et les périodes d'entrée du temporisateur après la mise à l'échelle, pour le compteur 2 d'un ATMega2560, et une fréquence d'horloge de base de 16 MHz. Le temporisateur a intégré des options de valeur "pré-échelle" qui déterminent la fréquence / période, comme indiqué dans ce tableau:

    TCCR2B bits 2-0    Prescaler    Freq [KHz], Period [usec] after prescale
          0x0          (TC stopped)      --         --
          0x1                1        16000.        0.0625
          0x2                8         2000.        0.500
          0x3               32          500.        2.000
          0x4               64          250.        4.000
          0x5              128          125.        8.000
          0x6              256           62.5      16.000
          0x7             1024           15.625    64.000

Pour une meilleure résolution de synchronisation, vous utilisez une valeur appelée TCNT2. Il y a un compteur intégré qui va de 0 à 255 car le temporisateur est à 8 bits. Lorsque le compteur atteint la valeur attribuée par TCNT2, il déclenche une interruption. Cette interruption est appelée TIMER2_OVF_vect.

compte tenu de ces informations, le taux d'interruption résultant serait: 16 MHz / (pré-échelle * (255 - TCNT2))

Vous pouvez faire fonctionner la minuterie à la fréquence complète de 16 MHz (62,5 ns), bien que ce soit beaucoup plus rapide que nécessaire; 2 MHz avec un décompte initial de (255-2) vous donnerait un taux d'interruption de 1 MHz. Divisez cela par 2 dans votre ISR:

extern uint32_t MicroSecClock = 0;

ISR(TIMER2_OVF_vect) {// this is a built in function that gets called when the timer gets to the overflow counter number
  static uint_8 count;            // interrupt counter

  if( (++count & 0x01) == 0 )     // bump the interrupt counter
    ++MicroSecClock;              // & count uSec every other time.

  digitalWrite(53,toggle);// pin 53 is arbitrary
  TCNT2 = 253;                    // this tells the timer when to trigger the interrupt. when the counter gets to 253 out of 255(because the timer is 8 bit) the timmer will trigger an interrupt
  TIFR2 = 0x00;                   // clear timer overflow flag
};

La fiche technique de votre MCU est la ressource de base; cet article vous donnera (et m'a donné!) une bonne longueur d'avance.

JRobert
la source
4

Mark, j'ai décidé d'écrire un nouvel ensemble de fonctions, basé sur l'Arduino Atmega328 Timer2, et en utilisant des interruptions de débordement, pour obtenir une précision à 0,5us. Mon code est disponible pour téléchargement et utilisation ici:

http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html

Voici une brève description: "J'ai écrit un" libary "pour obtenir une précision de 0,5us sur une fonction de remplacement" micros () ", afin que je puisse obtenir des résultats reproductibles en lisant un signal PWM ou PPM, à moins de 1us. J'ai cherché tout autour du Internet et ne pouvait pas trouver quelque chose de comparable (ou qui était facile à utiliser et qui maintenait la capacité de l'Arduino à écrire des signaux PWM via le Servo Libary), donc je pense que c'est ma première contribution majeure au monde de l'Arduino et du contrôle radio. "

Gabriel Staples
la source
3

Si le passage au niveau AVR est acceptable (comme vous l'avez mentionné dans votre question), vous pouvez configurer une minuterie et même obtenir les tics de l'horloge de l'AVR.

Vous devez vous référer à la fiche technique de votre AVR car elle diffère selon les différents ATMegas et ATTinys. Mais la procédure est toujours la même. Ce que vous devez faire, c'est:

  • Décidez de la mise à l'échelle à utiliser (par exemple, définissez-la sur 1, c'est-à-dire pas de mise à l'échelle si vous voulez la vitesse d'horloge réelle du processeur), reportez-vous à la documentation TCCR
  • configurer une interruption de dépassement de temporisation, un gestionnaire d'interruptions et activer les interruptions globalement
  • activer le temporisateur dans le registre de contrôle du temporisateur / compteur TCCR

De cette façon, vous pouvez obtenir les graduations exactes du registre de la minuterie, mais vous devez compter les débordements manuellement. C'est aussi précis que possible par la technologie.

Voici un exemple de code pour l'AT90S2313 obsolète, mais il vous donne de bons conseils sur la marche à suivre:

/* uC: AT90S2313 */
#include <avr/io.h>
#include <avr/interrupt.h>

int main(void)
{
  // set up timer 0
  TCCR0 = (1<<CS01); // Prescaler 8

  // enable overflow interrupt
  TIMSK |= (1<<TOIE0);

  // activate interrupts
  sei();

  while(1)
  {
     /* Do whatever you like */
  }
}


ISR (TIMER0_OVF_vect)
{    
   /*
      This gets called everytime there in an overflow in the timer register  
   */
}
FeeJai
la source
Merci FeeJai. Puis-je définir ces interruptions dans le contexte d'un croquis Arduino?
Mark Harrison
1
Je ne connais pas le framework Arduino. Mais s'il ne configure pas tous les temporisateurs en interne, vous le pouvez certainement. Sinon, vous pouvez toujours lire les registres de la minuterie et calculer vous-même.
FeeJai
Oui, vous programmez en C ++ complet dans Arduino et pouvez utiliser tous les registres et symboles tels que TIMSK, TOIE0etc. (copie de la configuration du minuteur à partir de la fiche technique atmega328p). Il semble que vous ne puissiez pas utiliser la macro ISR (), utilisez plutôt arduino.cc/en/Reference/AttachInterrupt .
joeforker