¿Codificador demasiado rápido para un Atmega328 pobre?

1

He estado jugando con un par de servomotores que obtuve del excedente de máquinas pick and place. 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 4096 pulsos de cuadratura por revolución +1 pulso de índice en cada revolución.  Hacer algunas pruebas en el IDE de Arduino mostró que girar el motor un poco más rápido y el codificador comienza a perder pasos ...

Decidí migrar el código a AS7 y limpiar toda la sobrecarga de Arduino, pero el chip parece incapaz de lidiar con eso. Corríjame si me equivoco con 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.

Suponiendo que los 20mS / 2048ppr anteriores tenemos un flanco ascendente cada 0.097mS - > 97uS dar o tomar.

¿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 ... IC de contador dedicado?

Gracias

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

    
pregunta Aleix

3 respuestas

4

He ejecutado su código a través de avr-gcc con -Os de optimización (YMMV si usa un compilador diferente, indicadores, etc., pero podría ser un buen punto de partida) y un resultado desensamblado, 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 instrucciones son mi adición a la salida del desensamblador, el número de ciclos para ejecución basados en Manual de instrucciones AVR . Si lo estoy contando correctamente, son 43 ciclos en total + 5 ciclos para respuesta de interrupción (+ aproximadamente 3 ciclos para que el cambio de pin se propague). El código ISR se puede optimizar a mano para que sea mucho más corto si es necesario. Pero aun así es de unos 50 ciclos, 3 us @ 16 MHz.

PIND se lee 12 ciclos después del inicio de ISR, unos 20 ciclos (1.25 us) después del borde INT0. Aún 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 interrupciones con manejo de prioridad. Por cierto en el código que coloca aquí, no hay procesamiento de la variable count , 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?

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

    
respondido por el Martin
1

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

Es probable que tenga muy poco tiempo para hacer el 100% en firmware.

    
respondido por el Entrepreneur
0

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 utilizarlo como un pre-escalar cuando se acciona el motor a velocidades. Por supuesto, la transición de un modo a otro es complicada y se debe realizar a velocidades más lentas, donde se puede garantizar una ventana de tiempo suficientemente amplia entre los pulsos para realizar el cambio.

    
respondido por el Trevor_G

Lea otras preguntas en las etiquetas