El contador del codificador FPGA se escapa aleatoriamente

Estoy programando un FPGA Altera usando Quartus II v9.0 para contar los pulsos del codificador y enviarlos a un programa externo de LabVIEW (vea el diagrama a continuación). Pude depurar un problema con mi código gracias a la comunidad de StackExchange , pero ahora tengo un descontrol intermitente en el recuento de mi codificador.

A medida que muevo mi codificador, mi código de LabVIEW muestra el conteo actual correctamente. Cuando dejo de mover el codificador, el conteo se detiene aproximadamente la mitad del tiempo y la otra mitad simplemente se desvanece. Sospecho que el codificador se está atascando en un estado intermedio y una de mis fases está aleteando. ¿Hay algún truco que pueda usar para filtrar esto, o un método mejor para contar los pulsos del codificador que pueda programar en mi Altera FPGA?

esquemático

simular este circuito : esquema creado con CircuitLab

El bloque de 4 entradas XORse ejecuta como:

(IN1 xor IN2) xor (IN3 xor IN4)

Las señales Count enabley Count directionvan a una función de contador integrada de 32 bits ( LPM_COUNTER) en mi Altera FPGA programada con Quartus II v9.0. La salida de este contador se almacena en un búfer ( LPM_BUSTRI) y se lee usando un programa de LabVIEW siempre que el código de LabVIEW lo necesite. Tengo un código de LabVIEW similar que lee otros búferes de la FPGA que funciona bien, por lo que estoy bastante seguro de que el problema radica en alguna parte de mi FPGA.

He intentado agregar un retardo de activación a las señales Ay Bque solo registra un cambio en la señal si esa señal se ha mantenido alta o baja durante una cierta cantidad de ciclos de reloj (probé 2 y 4 hasta ahora). Esto parecía empeorar el problema. También intenté agregar un cuarto juego de chanclas DQ, que no tuvo ningún efecto evidente.

¡Gracias por su ayuda!

¿Está utilizando disparadores Schmitt para limpiar las entradas del codificador antes del FPGA? De lo contrario, necesitará un poco de rebote digital en A y B después de los registros del sincronizador (antes del registro 2/5 anterior).
¿Has probado el antirrebote 'analógico'? Un paso bajo RC pequeño es suficiente en la mayoría de los casos. Además, ¿estás usando un codificador óptico? ¿Qué pasa con la luz ambiental que golpea los detectores? Esto podría causar una salida irregular del codificador.
El codificador está completamente cerrado y no estoy seguro de si es óptico. No creo que la interferencia en el codificador sea el problema, aunque podría estar equivocado. Veré los filtros Schmitt y la eliminación de rebotes. Gracias.
No necesariamente necesita el circuito antirrebote fuera de la FPGA. La última vez que trabajé en un proyecto que necesitaba un codificador, el antirrebote se construyó completamente en VHDL. recibiera un pico de entrada del sensor de pasillo, generaría una salida alta (una vez) y luego ignoraría por completo su entrada del sensor de pasillo hasta que hubiera pasado X tiempo, donde X era un valor que era lo suficientemente alto como para ignorar el rebote pero lo suficientemente bajo como para que no descartó el siguiente pulso; algo así como 1 ms.
@medivh gracias. Parece que tengo algunas ideas para probar de este hilo. Me pondré en contacto con todos ustedes con lo que descubra. ¡Gracias a todos!
@Engineero Por lo general, se considera una buena forma esperar uno o tres días antes de aceptar una respuesta. De esa forma da tiempo a que varias personas den respuestas y puedes aceptar la mejor. Pero si acepta una respuesta y luego aparece una respuesta mejor, puede y debe cambiar la respuesta que acepta. Dado que hay un ligero conflicto de intereses cuando digo eso, déjenme ser absolutamente claro en que la mejor respuesta siempre debe ganar (incluso si no es mi respuesta).
@DavidKessner Bueno, acepté la respuesta porque la implementé en mi sistema y funcionó a la perfección. De lo contrario, estaría de acuerdo con usted en que esperar algunas respuestas más sería lo mejor.

Respuestas (2)

Capture la entrada en lugar del contador para ver si el rebote es su problema. Si tiene alcance de almacenamiento, use el activador de borde para obtener la captura. De lo contrario, intente agregar rebote entre Reg1/Reg2 y entre Reg4/5.

Para implementar esto, use un registro de desplazamiento de 8 o 16 elementos alimentado desde A sincronizado. Tome el AND y el ~OR de entre todos los bits del registro de desplazamiento, estos le dan señales de "todo listo" y "todo despejado". Use esto como el ajuste y restablecimiento de un registro de salida, que alimentaría REG2. Lo mismo para B -> REG 5.

Si encuentra que esto todavía falla, puede probar con un reloj más bajo o extender el registro de desplazamiento. Por encima de las 16 etapas, probablemente sea mejor remodelarlo como un contador binario y un bit de último estado. En cada ciclo, si el estado es diferente al último, restablezca el contador y actualice el último estado. Si el contador se desborda sin borrarse, ese es su disparador para configurar/restablecer la salida.

Eso tiene sentido, y ya lo intenté, pero solo con un conteo de 2 y 4 pulsos de reloj. Lo implementé de manera diferente (usando un contador, no un registro de desplazamiento), pero puedo intentarlo a tu manera. Por curiosidad, ¿por qué lo pongo después de Reg1 y Reg4 en lugar de antes de ellos (en las entradas sin formato)?
Reg1, Reg4se utilizan como sincronizadores de cruce de reloj, ya que las entradas Ay Bson asíncronas con respecto a CLK1. Se nota porque no hacen nada más que retrasar las señales en un ciclo. Dado que desea que el tiempo de configuración máximo disponible para la metaestabilidad se asiente entre las etapas del sincronizador, no desea ninguna lógica intermedia que agregue demora de enrutamiento. Además, si el sincronizador está logrando algo, desea que esa lógica use la señal limpia después del sincronizador.
¿Qué tipo de registro usaría para el registro de salida (segundo párrafo de su respuesta anterior). ¿Podría hacer esto con un flip-flop DQ y simplemente no conectar los terminales de datos o de reloj?
¡Funcionó! Mis conteos ya no se escapan, y en realidad parece funcionar bien con un registro de desplazamiento de 4 bits. ¡Muchas gracias!
¡Impresionante! La etapa de salida sería un registro estándar, la entrada D sería (Q & ~reset) | setdonde set = & shift_reg_bits;y reset = ~ |shift_reg_bits;. No conozco LabVIEW, eso es Verilog.
Ah, esta parte se está haciendo en una placa Altera, por lo que se está programando en Quartus II. Podría hacer Verilog también si me sintiera ambicioso, pero parece estar funcionando ahora y no quiero molestarlo. ¡Gracias de nuevo!

Lo siento, habría respondido esto antes, pero estaba fuera de la ciudad con acceso limitado a Internet. Veo que Shuckc ya respondió esto, pero siento que tengo una solución que sería superior.

El problema con eliminar rebotes y/o filtrar A y B es que puede evitar que su decodificador funcione a la velocidad máxima (y, por lo tanto, perder cuentas), agrega complicaciones, aumenta el tamaño de la lógica y, lo más importante, no es necesario.

Comience manteniendo los seis registros que ya tiene en su esquema. Llamemos a la salida de Reg2 y Reg5 como A y B. La salida de Reg3 y Reg6 se llama A_prev y B_prev, que es básicamente el valor de A y B para el reloj anterior.

A continuación, crea una tabla de verdad más o menos así:

A_prev, B_prev, A, B, Count_En, Count_Dir
  0       0     0  0    0         0
  0       0     0  1    1         0
...etc...

Tendrás que llenar toda la tabla de verdad tú mismo, pero entiendes la idea. Básicamente, observa el estado actual de A y B, junto con los valores anteriores de A y B, y decide si desea aumentar o disminuir su contador. Ahora crea algo de lógica para implementar esta tabla de verdad. Te recomiendo que también registres la salida de esta tabla de verdad, antes de enviar las señales al contador.

Esta tabla de verdad encaja en un par de LUT de 4 entradas (el componente básico de la mayoría de los FPGA). Esencialmente, esto es súper pequeño. Registrar la salida de esta tabla de verdad también requiere esencialmente una lógica cero, ya que cada LUT en el FPGA tiene un Flip-flop en las salidas de los LUTS. Compare esto con hacer un poco de filtrado/eliminación de rebotes, lo que podría requerir 16 o 32 flip-flops y limita la utilidad de las LUT no utilizadas. Entonces, esta lógica es 1/16 o 1/32 del tamaño de la versión filtrada/rebotada.

Si está utilizando un FPGA más nuevo que tiene una LUT de 6 entradas que se puede dividir en 2 LUT más pequeñas, este diseño utiliza incluso menos recursos lógicos.

El registro de la salida permite que su lógica se ejecute a más de 100 MHz en la mayoría de los FPGA modernos, o hace que cumplir con sus limitaciones de tiempo sea mucho más fácil cuando se encuentra a velocidades de reloj más lentas. Si bien esto no parece importante, no cuesta nada y mejora en gran medida la solidez de este diseño y permite reutilizarlo más fácilmente en proyectos futuros donde su reloj principal es más rápido.

Lo bueno de este diseño es que A_in o B_in pueden cambiar literalmente en cada borde de su reloj de 25 MHz. Si tiene un poco de ruido o rebote en sus entradas, puede hacer que su contador "vibre" de un lado a otro entre dos señales adyacentes, pero esto también podría suceder con una versión filtrada/sin rebote.

La razón por la que este diseño no necesita filtrar/eliminar rebotes A y B es porque puede operar tan rápido como lo permita su reloj de 25 MHz. No puede confundirse con una entrada ruidosa o que cambia rápidamente, como podía hacerlo su diseño anterior.

Una de mis molestias favoritas es la decodificación en cuadratura mal implementada. Por lo general, es una mala implementación de software lo que hace que el decodificador pierda o salte pulsos, pero ocasionalmente una implementación de hardware también lo hará. No estoy diciendo que filtrar/eliminar el rebote de las entradas en cuadratura omitirá los pulsos, pero digo que este enfoque es mejor en todos los sentidos, incluida la velocidad máxima a la que puede aceptar pulsos.

¿Entiendo correctamente que básicamente está codificando los estados que puede tomar su codificador de cuadratura y luego comparando el estado actual del codificador con esta tabla de verdad para determinar si debe incrementar el conteo, disminuir el conteo o mantenerlo? ¿estable? Eso tiene sentido para mí también. Si tengo tiempo, intentaré implementar esto y veré si funciona aún mejor, pero la respuesta aceptada se ejecuta sin problemas y tengo un poco de tiempo. ¡Gracias!
@Ingeniero Entiendes bien.
Con un codificador mecánico, dudo que las frecuencias capturadas caigan fuera de 0-50 Hz, quizás con un rebote de EE. UU. en cada borde. Al pasar por el rebote, mueve el problema río abajo. Por ejemplo, si está posicionando una herramienta de corte o configurando un temporizador de autodestrucción con un jog dial, probablemente no toleraría un exceso en el punto de ajuste debido al rebote. Primero limpiaría las señales de entrada, preferiblemente fuera del FPGA hasta que supiera que tenía recursos lógicos libres para recortar la lista de materiales.