¿Codificador demasiado rápido para un pobre Atmega328?

He estado jugando con un par de servomotores que obtuve de un excedente de máquinas de recoger y colocar. Hasta ahora, he decodificado su pinout y estoy trabajando para leer el codificador, aquí viene el problema:

Por lo que observé, el motor viene equipado con una cuadratura de 4096 pulsos por revolución +1 pulso índice por revolución. Haciendo algunas pruebas en el IDE de Arduino mostró que girando el motor un poco más rápido y el codificador comienza a perder pasos...

Decidí migrar el código a AS7 y descargar toda la sobrecarga de Arduino, pero el chip parece incapaz de manejarlo. Corrígeme si me equivoco en lo siguiente:

Con 2048 cpr (solo usando el pulso ascendente de un canal de cuadratura) y una velocidad de rotación de 3000 rpm, se completa una revolución en 0,02 segundos.

Asumiendo los 20mS / 2048ppr anteriores, tenemos un flanco ascendente cada 0.097mS -> 97uS más o menos.

¿Es ese tiempo suficiente para ejecutar el siguiente ISR?:

#define F_CPU 16000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>

volatile int count;

int main(void)
{

    DDRD  = (0<<PORTD2) | (0<<PORTD3)| (0<<PORTD4); 
    PORTD = (0<<PORTD2) | (0<<PORTD3)| (0<<PORTD4);  
    EICRA = (1 << ISC11) | (1 << ISC01);  // Configure interrupt trigger on rising edge of INT0  
    EIMSK = (1 << INT0); //ebable INT0 
    sei();

    while (1) 
    {

    }
}

ISR (INT0_vect){

    uint8_t i = ((PIND & 0b00010000)>>4);


        if (i == 1) {

            count = count +1;

            }else{

            count = count -1;
        }       

    EIFR = (1<<INTF0);
}

Si no, ¿cómo debo hacerlo... Contador IC dedicado?

Gracias

**Editar:**captura del analizador lógico comparando una revolución (índice) con el codificador A

captura

No ha mencionado la frecuencia de MCU, pero incluso para algo como 1 MHz, todavía son ~ 100 ciclos, lo que debe ser suficiente para ISR anterior. ¿No tiene ningún otro ISR (más lento) en su código u otro bloque largo con IRQ deshabilitados? ¿Está bien la señal eléctrica en los pines (límite del codificador, traza de inductancia/capacitancia, pull-ups, ...)?
Pero, en realidad, tus matemáticas están mal. 20 ms / 2048 son solo 10 us, no 100 us. Entonces, ¿qué frecuencia está ejecutando su MCU?
Ignorando por ahora que el tiempo se mide en segundos, no en Siemens, con un compilador no optimizador, su ISR probablemente llegará a aproximadamente 4+12+10+12+4 = 42 ciclos. Dar o tomar un par como estoy adivinando. Con la corrección de @Martin, debe estar funcionando a 4,2 MHz para mantenerse al día, cuando no hace nada más. Así que eso es un mínimo de 12 MHz de manera realista cuando no se hace mucho ni nada más... así que...
Oups, olvidé mencionar que ahora mismo estoy usando un Arduino Uno estándar a 16 MHz. @Martin, tienes razón, ¡estoy publicando astutamente desde la oficina y olvidé un 0! El código no hace nada más que realizar el ISR avobe, déjame publicar el código completo.
@Martin, como se ve en el alcance, los pulsos se ven nítidos y agradables, no crea que es un problema de integridad de la señal.
Debe hacer que su incremento/decremento sea condicional al bit directamente, sin toda la asignación intermedia. También asegúrese de que su acceso al conteo fuera del ISR sea atómico: un Arduino intes de 16 bits, por lo que el acceso normalmente sería en dos pasos. Por último, descartar un pick & place para las piezas del experimentador parece una mala idea; probablemente valga más la pena como plataforma para alguien que quiere jugar con un pick & place, o como piezas para mantener un modelo comparable.
Hay algunos controladores en la familia PIC diseñados para manejar codificadores y hacerlo bien in silico. Podrías considerar cambiar.

Respuestas (3)

He ejecutado su código avr-gcccon -Osoptimización (YMMV si usa un compilador diferente, banderas, etc., pero podría ser un buen punto de partida) y desarmé el resultado, aquí está:

00000090 <__vector_1>:
 2  90: 1f 92           push    r1
 2  92: 0f 92           push    r0
 1  94: 0f b6           in  r0, 0x3f    ; 63
 2  96: 0f 92           push    r0
 1  98: 11 24           eor r1, r1
 2  9a: 8f 93           push    r24
 2  9c: 9f 93           push    r25
1 2 9e: 4c 9b           sbis    0x09, 4 ; 9
2 . a0: 06 c0           rjmp    .+12        ; 0xae <__vector_1+0x1e>
. 2 a2: 80 91 00 01     lds r24, 0x0100
. 2 a6: 90 91 01 01     lds r25, 0x0101
. 2 aa: 01 96           adiw    r24, 0x01   ; 1
. 2 ac: 05 c0           rjmp    .+10        ; 0xb8 <__vector_1+0x28>
2 . ae: 80 91 00 01     lds r24, 0x0100
2 . b2: 90 91 01 01     lds r25, 0x0101
2 . b6: 01 97           sbiw    r24, 0x01   ; 1
 2  b8: 90 93 01 01     sts 0x0101, r25
 2  bc: 80 93 00 01     sts 0x0100, r24
 1  c0: 81 e0           ldi r24, 0x01   ; 1
 1  c2: 8c bb           out 0x1c, r24   ; 28
 2  c4: 9f 91           pop r25
 2  c6: 8f 91           pop r24
 2  c8: 0f 90           pop r0
 1  ca: 0f be           out 0x3f, r0    ; 63
 2  cc: 0f 90           pop r0
 2  ce: 1f 90           pop r1
 4  d0: 18 95           reti

Los números antes de las direcciones de instrucción son mi adición a la salida del desensamblador, el número de ciclos para la ejecución basado en el Manual del conjunto de instrucciones AVR . Si lo estoy contando correctamente, son 43 ciclos en total + 5 ciclos para la respuesta de interrupción (+ aproximadamente 3 ciclos para que se propague el cambio de pin). El código ISR se puede optimizar a mano en mucho más corto si es necesario. Pero aun así son unos 50 ciclos, 3 us @ 16 MHz.

PIND se lee 12 ciclos después del inicio de ISR, aproximadamente 20 ciclos (1,25 us) después del flanco INT0. Debería estar bien.

No tienes mucho marginal, pero debería funcionar. OTOH, cualquier otro ISR probablemente lo "matará", ya que ATmega no tiene un controlador de interrupción con manejo de prioridad. Por cierto. en el código que pone aquí, no hay procesamiento de countvariables, por lo que incluso para la prueba debe ser más complicado. ¿Está seguro de que no está haciendo nada que afecte la latencia de ISR allí?

Como una solución alternativa, si puede considerar XMega para su proyecto (asumiendo que desea usar AVR), estos tienen soporte de hardware para el codificador y puede manejarlo sin necesidad de interacción FW.

No voy a pretender que comprendo completamente el alcance de su explicación, me llevará algo de tiempo leer y buscar en Google. No hay otros ISR, para la depuración, el contador de valores se almacenó e imprimió en el bucle principal cada 2 revoluciones (1 revolución para muestra (sin interferencia de la impresión uart) y otra para imprimir (con sus posibles cuentas faltantes debido a la impresión ). Definitivamente echaré un vistazo a xmega, soy ingeniero industrial, jugué un poco con Arduinos y quería aprovechar este proyecto como una oportunidad para aprender un poco más.
@Aleix, la nota sobre otro ISR se dirigió más bien hacia el futuro, si desea desarrollarlo, es probable que deba agregar algo para la funcionalidad, a la que se dirige. Acerca del contador de lectura: o lo está haciendo con ISR deshabilitado (por lo tanto, es posible que haya un problema de tiempo si no tiene cuidado), o tiene una variable multibyte de lectura de condición de carrera countmientras que ISR puede actualizarla en el medio. Si ve un error de apagado por uno, entonces, tal vez, puede ser causado por la latencia de la detección de revolución completa en el código.

Habrá menos de 5 usec entre flancos ascendentes porque hay dos sensores. Eso dejará cero tiempo de CPU para usar la información para cualquier cosa. Además, la mayoría de los esquemas de decodificadores de cuadratura limpios muestrean entre los 4 bordes, por lo que dejaría solo 2,5 uS entre bordes/muestras.

Probablemente tendrá muy poco tiempo para hacer el 100% en el firmware.

Es lo que me temo, sin embargo, girar alrededor de 1 revolución por segundo parece afectar la salida de la misma manera...
Si no funciona a 1 rps, algo más está mal.
De hecho, con solo observar el borde ascendente de uno de los canales y sin requerir que intervenga una transición del otro, la implementación se vuelve susceptible de multiplicar la vibración de conteo sobre ese borde . En realidad, es más probable que esto se vea a velocidades lentas o con rotación manual que cuando se ejecuta normalmente.
Eche un vistazo a la captura, tengo el alcance en el trabajo, desafortunadamente aquí solo tengo este pequeño analizador ... @ChrisStratton, entiendo que está lejos de ser ideal hacerlo de esta manera, solo estaba probando el hardware. Sin embargo, en lugar de perder pasos, ¿debería estar "ganando" pasos?
Podrías estar contando múltiples tirones hacia atrás en la rotación. Hasta cierto punto, no vale la pena intentar depurar los detalles de implementación de un algoritmo defectuoso hasta que haya arreglado el algoritmo en sí.

10us es bastante rápido incluso para velocidades de reloj más rápidas.

Es posible que desee considerar alimentar el pulso del reloj en un temporizador/contador y usarlo como un preescalar cuando se conduce el motor a velocidades. Por supuesto, la transición de un modo a otro es complicada y debe realizarse a velocidades más lentas donde pueda garantizar una ventana de tiempo lo suficientemente amplia entre pulsos para realizar el cambio.

Eso es lo que estaba viendo cuando buscaba en Google, eso me molestará mucho, tengo muy pocas habilidades de codificación (casi inexistentes) y tener que implementar una función de cambio de ese tipo huele a frustración.
Hasta donde yo sé, lo que traté de hacer allí es: -Obtener la totalidad de PIND -Enmascararlo y cambiar el bit interesante para obtener uno o 0 ¿Podría explicar un poco más esto: - Lo tengo.
Estoy usando PD4, ¿me equivoco al cambiar el 00010000 resultante cuatro posiciones a la derecha?
@Aleix nvm... mi error. Aparentemente leí mal >>