AVR Medir la frecuencia utilizando una interrupción externa: ¿a dónde van mis ciclos?

3

Estoy usando una interrupción externa para medir la frecuencia de una señal, el reloj AVR está en 8MHz. Básicamente, estoy contando los tics entre las alternaciones de pin con un temporizador de 16 bits (con algo de manejo para los desbordamientos de señales lentas).

El código de esqueleto es el siguiente:

int main(void){
clearbit(DDRD,PD2);
PCMSK3 = 0x00;
setbit(PCMSK3, PCINT26);
setbit(PCICR,PCIE3);
setbit(TIMSK1,TOIE1);
setbit(TCCR1B, CS10);

while(1){
}
return 0;
}

ISR(PCINT3_vect){
    togglebit(PORTD, PD1);  
    countl = TCNT1L;
    counth = TCNT1H;
    soverflow = overflow;
    overflow = 0;
    TCNT1 = 0x0000;
    return;
}

/* Overflow timer */
ISR(TIMER1_OVF_vect){
    overflow++;
    return;
}

Esto funciona prácticamente como se desea, sin embargo, no puedo medir nada más corto que alrededor de 49 ciclos. Las dos imágenes de alcance muestran el problema. La traza amarilla es del oscilador externo, la traza azul es la palanca de pin dentro del controlador de interrupción. No hay nada en el bucle principal, y tener cosas allí no cambia los resultados significativamente. Todas las variables son uint8_t . La lectura de la cuenta en bytes altos y bajos ganó un poco de velocidad (en comparación con una sola lectura / escritura de 16 bits).

Lo mejor que he podido hacer es ~ 95kHz, pero tengo curiosidad por saber hacia dónde van los ciclos perdidos. Las asignaciones no pueden durar más de unos pocos ciclos y, si entiendo bien, ISR básicamente llama a cli () en la entrada y sei () en la salida, por lo que lo único que sucede es que el temporizador se reinicia, la función regresa y nosotros espera el siguiente cambio.

EDIT

void get_count(void){
    uint8_t i=0, oldpin, newpin;
    setbit(TIMSK1,TOIE1);
    setbit(TCCR1B, CS10);
    sei();
    while(getbit(PIND, PD2) != 0){
    continue;
    }
    while(getbit(PIND, PD2) != 1){
    continue;
    }
    TCNT1 = 0;
    while(getbit(PIND, PD2) == 1){
        continue;
    }
    countl = TCNT1L;
counth = TCNT1H;
    TCNT1 = 0;
    soverflow = overflow;
    overflow = 0;
    clearbit(TIMSK1,TOIE1);
clearbit(TCCR1B, CS10);
    cli();
    return;
    }

Esto me lleva a alrededor de 400kHz antes de que las cosas empiecen a ponerse dudosas. No estoy seguro de que haya mucho más que pueda hacer para reducirlo. Espero a que el pin pase a 1, comienzo el temporizador y el tiempo que tarda en llegar a cero, es decir, en medio ciclo de reloj.

    
pregunta Josh

1 respuesta

3

Como @angelatlarge dijo que su primera tarea es ver el desmontaje de su vector de interrupción para determinar cuántos ciclos toma realmente su código. En segundo lugar, debe darse cuenta de que hay una sobrecarga adicional asociada con una llamada a un vector de interrupción. Aquí hay un extracto de la hoja de datos de ATMega328P, sección 7.7.1, que debe ser representativa de todos los AVR:

  

La respuesta de ejecución de interrupción para todas las interrupciones AVR habilitadas es de cuatro ciclos de reloj como mínimo. Después de cuatro horas   cicla la dirección del vector de programa para ejecutar la rutina de manejo de interrupciones. Durante este ciclo de cuatro relojes.   período, el contador de programa se empuja en la pila. El vector es normalmente un salto a la rutina de interrupción, y   Este salto lleva tres ciclos de reloj. Si se produce una interrupción durante la ejecución de una instrucción de varios ciclos, esta instrucción   se completa antes de que se sirva la interrupción. Si se produce una interrupción cuando la MCU está en modo de suspensión, el tiempo de respuesta de la interrupción de la ejecución aumenta en cuatro ciclos de reloj. Este incremento se suma al tiempo de puesta en marcha del   modo de espera seleccionado.   Un retorno de una rutina de manejo de interrupciones requiere cuatro ciclos de reloj. Durante estos cuatro ciclos de reloj, el Programa   El contador (dos bytes) se devuelve de la pila, el puntero de pila se incrementa en dos y el bit I en SREG   está establecido.

Entonces, para resumir:

  1. 4 ciclos de reloj para empujar la PC hacia la pila
  2. 3 ciclos de reloj para saltar para interrumpir el controlador
  3. Hasta 3 ciclos de reloj si se interrumpe una instrucción de varios ciclos (la tabla de conjunto de instrucciones AVR en el mismo documento indica que la instrucción más larga es 4 ciclos)
  4. 4 ciclos de reloj más al salir de la interrupción para restaurar la PC, incrementar SP, etc.

Esto agrega entre 11 y 14 ciclos adicionales de sobrecarga además de cuantos ciclos tome su código de interrupción.

    
respondido por el AngryEE

Lea otras preguntas en las etiquetas