Codificador de cuadratura: la implementación más eficiente

-1

En pocas palabras, estoy controlando un simple motor de CC con un codificador de doble canal con un microcontrolador para un proyecto personal, y estoy tratando de encontrar la "mejor" implementación de la siguiente máquina de estados que se presenta en este Manual de referencia de TI :

Antesderealizarunainvestigaciónrealsobreeltema,yo,siendounidiotadecabezacaliente,tratérápidamentedeusarun(implementacióndesoftwaredea)D-flip-flopparaobtenerladireccióndelmotor,yluegoincrementarodisminuiruncontador.Segúnladirección.MediaC,mitadpseudocódigoaquí:

uint16_tcounter=0;uint8_tdirection=0;voidinterruptEncoderA(void){if(getInput(encoder_a)==1){direction=getPinValue(encoder_b);}direction?counter++:counter--;}voidinterruptEncoderB(void){direction?counter++:counter--;}

Lasinterrupcionesocurrenenambosbordesdelcanalcorrespondiente.Estaingenuaimplementacióntienealgunosproblemasmuyclaros.Porejemplo,cuandoelmotoravanzayretrocedeentrelosbordesdelcodificadorB,seguirácontandoenlamismadirección.

Séqueestapreguntapuedeserenrealidadalgosubjetiva,oinclusodependerdelaarquitecturadelhardware.Loquerealmenteestoybuscandosonsusopinionessobresolucioneseficientes,elegantesysimples.Siesposible,proporcioneunabrevedescripcióndelosprosyloscontrasdesurespuesta,ocualquierotrainformaciónqueconsidereútil.

Además,siéntaselibredecambiarcómofuncionanlasinterrupciones.SiesmásadecuadotenersolounainterrupciónquesedisparaencadabordedelrelojdelcodificadorAoB,otalvezsiprefieres4interrupciones(unaporborde,porcanal),hazlo.Soloasegúratedequeseaobvio.

Además,soloparadejarloenclaro,estapreguntaessobreunaimplementacióndemicrocontrolador,noHDL.

Esta pregunta realmente tiene una muy buena solución a este problema Mi opinión sobre esta implementación estará en las respuestas.

    
pregunta Chi

1 respuesta

0

Aquí va mi propia implementación.

Usando una LUT, uno puede almacenar el incremento que debe ir en un contador, que determina la posición del rotor.

El índice de la LUT es una combinación del estado actual, el último estado conocido y la última dirección conocida:

Los bits 0 y 1 (los 2 LSB) son el estado actual (valores A y B, respectivamente), mientras que los bits 3 y 2 son el estado anterior.

El bit 4 es la dirección anterior. Si se pierde un estado, se supone que el motor va en la misma dirección que en el último estado, recuperándose así de un paso perdido.

Pros: No hay necesidad de desalojar la señal. Huella de código pequeño. Se recuperará de los pasos perdidos.

Contras: tiempo de CPU perdido cuando el motor se detiene o gira lentamente. La frecuencia de sondeo debe ser superior a 4 veces la frecuencia máxima del codificador (2x por borde, 2 canales).

Seudocódigo aquí:

int32_t counter_lut[32] = {
        //Direction = 1
        0, // 00 to 00
       -1, // 00 to 01
       +1, // 00 to 10
       +2, // 00 to 11

       +1, // 01 to 00
        0, // 01 to 01
       +2, // 01 to 10
       -1, // 01 to 11

       -1, // 10 to 00
       +2, // 10 to 01
        0, // 10 to 10
       +1, // 10 to 11

       +2, // 11 to 00
       +1, // 11 to 01
       -1, // 11 to 10
        0, // 11 to 11

        //Direction = 0
        0, // 00 to 00
       -1, // 00 to 01
       +1, // 00 to 10
       -2, // 00 to 11

       +1, // 01 to 00
        0, // 01 to 01
       -2, // 01 to 10
       -1, // 01 to 11

       -1, // 10 to 00
       -2, // 10 to 01
        0, // 10 to 10
       +1, // 10 to 11

       -2, // 11 to 00
       +1, // 11 to 01
       -1, // 11 to 10
        0, // 11 to 11
};

uint32_t counter = 0,  direction = 0, lut_index = 0;

void encoderInterrupt(void){

    lut_index |= getInput(encoder_A)<<1 | getInput(encoder_B);
    counter += counter_lut[lut_index];

    if (counter_lut[lut_index] != 0){
        direction = (counter_lut[lut_index] > 0) ? 1 : 0;
    }

    //Prepare for next iteration by shifting current state
    //bits to old state bits and also the direction bit
    lut_index = ((lut_index << 2) & 0b1100) | (direction<<4);
}

Probado para funcionar perfectamente en un codificador de 100 conteos por revolución, con una caja de engranajes de 12.5: 1 y un máximo de 260 RPM (después del engranaje), lo que lo convierte en 5000 transiciones de estado por revolución (100 conteos * 12.5 * 4 bordes de señal del codificador), para un máximo de ~ 22000 transiciones de estado por segundo.

La frecuencia de muestreo se establece en 100 kHz, y la prioridad de interrupción está por encima de todas las demás interrupciones, lo que no es un gran problema teniendo en cuenta el poco código que la CPU realmente tiene que ejecutar. Esto se probó en un microcontrolador Cortex-M4 a 144 MHz.

    
respondido por el Chi

Lea otras preguntas en las etiquetas