Arduino: mejor resolución de microsegundos que micros ()?

11

La documentación de micros () señala que el valor de retorno siempre será un múltiplo de 4.

¿Hay alguna forma de obtener un clic de microsegundos de mayor resolución, preferiblemente hasta el nivel de 1 microsegundo?

Bajar al nivel AVR es aceptable.

Mark Harrison
fuente
1
¿Por qué insiste en comenzar su título con una etiqueta cuando hice una pregunta perfectamente en inglés?
stevenvh
44
Disculpas Steven, sinceramente pensé (y pienso) que es una buena manera de expresar la pregunta de manera sucinta y clara. Hubo mucha discusión sobre esto, y Jeff estuvo de acuerdo en algunos casos en que era una buena forma de preguntar. La discusión está aquí; Los mapas de calor son lo que me convenció de que para este estilo de preguntas vale la pena enfatizar el entorno operativo. meta.stackexchange.com/questions/10647/writing-good-titles/… Pero, por respeto a sus excelentes respuestas, me encantaría volver a cambiarlo si cree que es mejor.
Mark Harrison el
¿Es posible usarlo para la función pulsein ()? Necesito medir la longitud de los pulsos en resolución de menos de 1 uso.
Este espacio está reservado para las respuestas a la pregunta en la parte superior. Si tiene algo que preguntar, comience su propia pregunta.
Olin Lathrop

Respuestas:

9

Sí, dependiendo de la frecuencia de reloj básica de su Arduino. Por ejemplo, aquí están las frecuencias de entrada del contador de tiempo y los períodos después del preescalado, para un contador de tiempo ATMega2560 2, y una frecuencia de reloj básica de 16MHz. El temporizador ha incorporado opciones de valor "preescaler" que determinan la frecuencia / período, que se muestran en esta tabla:

    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

Para una mejor resolución de tiempo, utiliza un valor llamado TCNT2. Hay un contador de compilación que va de 0 a 255 porque el temporizador es de 8 bits. Cuando el contador alcanza el valor asignado por TCNT2, desencadena una interrupción. Esta interrupción se llama TIMER2_OVF_vect.

dada esta información, la tasa de interrupción resultante sería: 16MHz / (preescaler * (255 - TCNT2))

Podría hacer que el temporizador funcione a 16MHz (62.5nSec), aunque eso es mucho más rápido de lo que necesita; 2MHz con un conteo inicial de (255-2) le daría una tasa de interrupción de 1MHz. Divide eso entre 2 en tu 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 hoja de datos para su MCU es el recurso básico; Este artículo le dará (¡y me dio a mí!) una buena ventaja.

JRobert
fuente
4

Mark, decidí escribir un nuevo conjunto de funciones, basado en el Arduino Atmega328 Timer2, y usando interrupciones de desbordamiento, para obtener precisión de 0.5us. Mi código está disponible para descargar y usar aquí:

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

Aquí hay una breve descripción: "Escribí una" biblioteca "para obtener una precisión de 0.5us en una función de reemplazo" micros () ", para que pueda obtener resultados repetibles leyendo una señal PWM o PPM, dentro de 1us. Busqué por todos lados Internet y no podía encontrar algo comparable (o que era fácil de usar, y mantenía la capacidad del Arduino de escribir señales PWM a través de la Servo Libary), por lo que creo que esta es mi primera gran contribución al mundo de Arduino y Radio Control ".

Gabriel Staples
fuente
3

Si es aceptable bajar al nivel AVR (como mencionó en su pregunta), puede configurar un temporizador e incluso obtener los tics del reloj del AVR.

Debe consultar la hoja de datos de su AVR porque difiere entre los diferentes ATMegas y ATTinys. Pero el procedimiento es siempre el mismo. Lo que debes hacer es:

  • decida qué preescalador usar (por ejemplo, configúrelo en 1, es decir, sin preescalado si desea la velocidad real del reloj de la CPU), consulte la documentación de TCCR
  • configurar una interrupción de desbordamiento del temporizador, un controlador de interrupciones y activar interrupciones globalmente
  • activar el temporizador en el registro de control de temporizador / contador TCCR

De esa manera puede obtener los ticks exactos del registro del temporizador, sin embargo, debe contar los desbordamientos manualmente. Esto es lo más preciso posible por la tecnología.

Aquí hay un código de ejemplo para el AT90S2313 obsoleto, pero le da buenos consejos sobre qué hacer básicamente:

/* 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
fuente
Gracias FeeJai. ¿Puedo configurar estas interrupciones dentro del contexto de un boceto de Arduino?
Mark Harrison el
1
No estoy familiarizado con el marco arduino. Pero si no configura todos los temporizadores internamente, ciertamente puede hacerlo. De lo contrario, aún puede leer los registros del temporizador y calcular usted mismo.
FeeJai
Sí, está programando en C ++ completo en Arduino y puede usar todos los registros y símbolos como TIMSK, TOIE0etc. (copie la configuración del temporizador de la hoja de datos atmega328p). Parece que no puede usar la macro ISR (), en su lugar use arduino.cc/en/Reference/AttachInterrupt .
joeforker