Comportamiento aleatorio e impredecible del comparador analógico

Estoy trabajando en un proyecto relativamente "simple" en el que necesito medir la frecuencia de una onda sinusoidal que varía en amplitud y frecuencia. Para simplificar las cosas, por ahora, solo tengo una entrada de onda sinusoidal de frecuencia fija (27 Hz) (entrada negativa del comparador) que solo se puede variar en amplitud (usando un potenciómetro). La entrada positiva del comparador se establece en Vcc/2. La salida del comparador luego se alimenta al registro de captura de entrada del microcontrolador atmega2560 para medir la frecuencia.

El problema es que en ciertas amplitudes de la señal de entrada obtengo un cambio bastante intenso (o, a veces, bandas muertas) en la salida que se ve así:

ingrese la descripción de la imagen aquí

Donde, como el resultado esperado, debería verse así:

ingrese la descripción de la imagen aquí

Cosas que he probado hasta ahora:

Usando el comparador interno de atmega2560 interno. Utilizando un comparador externo. Introducción de histéresis usando software y circuito de disparo Schmitt. Probé varias configuraciones de entrada, incluida la configuración de referencia fija y la configuración de segmentación de datos. Probando diferentes atmega2560. Probando diferentes velocidades de reloj.

Algunas soluciones eran más estables que otras, pero ninguna de ellas era ni cerca de ser aceptable. Me he conformado con la configuración más estable hasta ahora:

ingrese la descripción de la imagen aquí

Con esta configuración, ciertas cosas mejoran/cambian la estabilidad, sin embargo, aún no son perfectas:

Cambiando el valor de R5 para aumentar la histéresis. Eliminando C2 por completo (no tengo idea de por qué). Tocando los cables en la protoboard (algunos de ellos uno al lado del otro). Conmutación de fuentes de alimentación de externas a USB y viceversa.

En este punto, es ruido, mi DAC con el que estoy generando la onda sinusoidal o estoy haciendo algo muy fundamental incorrectamente. Este circuito ha funcionado para otras personas sin ningún problema, por lo que algo debe estar mal con mi configuración o entorno.

Si alguien tiene alguna sugerencia, agradecería mucho su tiempo.

Aquí está mi fuente mínima:

#include <avr/io.h>

void init(void);

void init(void) {
    /* Setup comparator */
    ACSR = (1 << ACIE) | (1 << ACIS1);
    /* Initialize PORTD for PIND5 */
    DDRD = 0x00;
    PORTD = 0x00;
    /* Enable global interrupts */
    sei();
}

int main(void) {

    init();

    while (1) {}
}

ISR(ANALOG_COMP_vect) {

     if (!(ACSR &  (1<<ACIS0))) { //comparator falling edge
         /* Set PIND5 to 0V */
         PORTD &= ~(1 << PIND5);

         ACSR |= (1<<ACIS0); //set next comparator detection on rising edge
    }
    else  {
       ACSR &= ~(1<<ACIS0); //set next comparator detection on falling edge
       /* Set PIND5 to 5V */
       PORTD |= (1 << PIND5);
    }
}

Además, aquí está el enlace al diagrama del circuito y la biblioteca en sí:

http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/

ACTUALIZAR:

Probé todas sus sugerencias, ninguna funcionó excepto una. Borrar las banderas de interrupción o deshabilitar las interrupciones dentro o fuera del ISR realmente no tuvo ningún efecto. Parece que no entiendo bien cómo funciona realmente el registro del comparador del chip.

Como mencioné inicialmente, iba a usar la captura de entrada para medir la frecuencia de una onda cuadrada derivada de una onda sinusoidal. La salida del comparador se alimenta al pin de captura de entrada, luego usa temporizadores para medir el período, simple.

Aquí está el diagrama del comparador analógico de atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , página 265:

ingrese la descripción de la imagen aquí

Como puedes ver, el comparador tiene dos salidas, ACO y ACIS0+ACIS1. ACO se establece cuando + entrada > - entrada, se borra cuando + entrada < - entrada. ACIS0+ACIS1 son bits de selección de borde.

Lo que estaba haciendo inicialmente era verificar el tipo de borde en mi ISR. Cambié el ISR a esto en su lugar:

    ISR(ANALOG_COMP_vect) {

     if (!(ACSR &  (1<<ACO))) { // + < -
         /* Set PIND5 to 0V */
         PORTD &= ~(1 << PIND5);
    }
    else  {
       /* Set PIND5 to 5V */
       PORTD |= (1 << PIND5);
    }
}

Y la salida se comportó perfectamente (al igual que en la segunda imagen). Luego procedí a medir el ancho de los pulsos pero los resultados no fueron muy buenos. Cambios intensos en mi pantalla LCD, números saltando a valores aleatorios o permaneciendo en 0, a pesar de tener una señal limpia. Reescribí mi código muchas veces usando diferentes condiciones, la única solución semiestable que tengo hasta ahora es esta:

#include <avr/io.h>
#include <util/delay.h>
#include "UART.h"

void init(void);

volatile uint16_t y = 0;
volatile uint16_t x = 0;
volatile uint16_t current_value = 0;
volatile uint16_t previous_value = 0;
volatile uint16_t total = 0;

void init(void) {
    /* Normal mode, 64 prescaler, Rising Edge trigger, Input Capture */
    TCCR1A = 0;
    TCCR1B = (1 << CS10) | (1 << CS11) | (1 << ICES1);
    TIMSK1 = (1 << ICIE1);

    ACSR = (1 << ACIC);
    ADCSRB = 0x00;

    /* This port is used for simulating comparator's output */
    DDRC = 0xFF;
    PORTC = 0xFF;

    DDRD = 0x00;
    PORTD = 0x00;

    USART_Init(UBRR_VALUE);

    sei();
}

int main(void) {

init();

    while (1) {
        if (TCNT1 == 60000) {
            /* Display the values on the LCD */
            USART_Transmit(0xFE);
            USART_Transmit(0x01);

            USART_Transmit_Double(x+y);
        }
    }
}

ISR(TIMER1_CAPT_vect) {

    //ACSR &= ~(1<<ACIC);

    if (!(ACSR & (1 << ACO))) {
        if (!(TCCR1B & (1 << ICES1))) { // check for falling edge
            PORTD |= (1 << PIND5);

            PORTC &= ~(1 << PINC1);

            TCCR1B |= (1 << ICES1);

            current_value = ICR1;
            x = current_value - previous_value;
            previous_value = current_value;
        }
    }        
    else {
        if (TCCR1B & (1 << ICES1)) { // check for rising edge
            PORTD &= ~(1 << PIND5);

            PORTC |= (1 << PINC1);

            TCCR1B &= ~(1 << ICES1);

            current_value = ICR1;
            y = current_value - previous_value;
            previous_value = current_value;
        }
    }

    //ACSR |= (1<<ACIC);
}

Por semiestable quiero decir, obtengo el valor correcto 1/3 de las veces. Las otras veces, 2/3 de las veces es la mitad del valor correcto o un valor aleatorio. Intenté usar los bits de registro del temporizador para declaraciones condicionales, así como los bits de registro del comparador en mi ISR, esta es la única configuración que funciona.

Lo que hice más tarde ese día fue usar un comparador externo con la misma configuración y fuente (excluyendo todas las líneas relacionadas con el comparador). Su salida se introdujo en el pin de captura de entrada y funcionó según lo previsto (ni siquiera necesitaba histéresis).

En este punto, puedo decir que lo resolví usando un comparador externo, sin embargo, no tengo idea de por qué el interno no se comporta. He leído muchas publicaciones y guías sobre esto, leí diferentes bibliotecas, traté de imitarlas sin ningún resultado aceptable. La hoja de datos solo tiene 5 páginas en toda la unidad de comparación, la releí muchas veces y no veo qué estoy haciendo mal.

Me gustaría saber cómo usarlo correctamente, pero si eso falla, tengo una copia de seguridad. Si tiene alguna entrada adicional, es muy apreciada.

para empezar... agregue una resistencia de 1M entre la salida y la entrada +ve. ESTO es lo que crea la histéresis, no tu R5... eso solo cambia la referencia
¿Cómo puede producir imágenes de alcance de la salida de un comparador que está dentro del chip y no accesible?
@JonRB, lo siento, olvidé mencionarlo, pin5 alterna entre Vcc y 0, lo que cambia la referencia +/- 25 mV, que es esencialmente lo que hace el disparador Schmitt
@Andy alias, estoy alternando un pin de los comparadores analógicos interrumpiendo la rutina/sondeo del servicio
¿Está alternando dos veces durante 1 ISR? Me parece que la imagen 1 podría indicar un problema de código a pesar de que el comparador funciona correctamente.
@Andy alias, no, solo una vez por ISR, ya sea en el borde ascendente o descendente.
¿Está deshabilitando más interrupciones cuando ingresa a un ISR? Es posible que deba hacerlo; es posible que la mayoría de los ISR obtengan resultados dobles.
@Andy alias, no lo he probado, no. Eso tiene sentido, sin embargo, la biblioteca I que se hizo para este circuito sí lo tiene. Aunque lo intentaré mañana. Lo que no entiendo es por qué cada ~ 0.3VI tiene alternancia o bandas muertas donde otras amplitudes de entrada están bien.
La última parte de su comentario no tiene sentido: ¿de dónde provienen los 0,3 voltios? Prueba lo de la interrupción a ver si mejora.
@Andy alias, como en, amplitud de onda sinusoidal variable. Por ejemplo, pasar de 4v pk-pk a 3.7v pk-pk. 0.3v es una aproximación muy cruda, pero lo que intento decir es que el comportamiento aleatorio no es continuo en la amplitud de las entradas. Y sin duda probaré el ISR, gracias.
¿Cómo está alternando el pin de histéresis y lo está calificando por el valor actual? La demora entre la interrupción y la alternancia puede estar fastidiándote.
@Trevor_G, el comparador dispara la interrupción, configuro el pin 5 en alto o bajo dependiendo del borde. Eso es todo. Puede mirar "Freqmeasure.h" en github, tengo exactamente el mismo ISR excluyendo todo el código de los temporizadores.
@Hypomania desafortunadamente, no estoy lo suficientemente familiarizado con el micro para saber cómo sabes qué borde es.
no se muestra en su esquema es la capacitancia interna entre pin5 y pin6, ¿puede usar el pull-up interno en pin7 para hacer su histéresis en su lugar?
@Jasen, ciertamente puedo, sí, ¿qué diferencia habrá?
@Andyaka actualizado
Usar ACO directamente hará que la onda cuadrada se vea mejor ya que presumiblemente se corregirá mejor. Sin embargo, no hay garantía de que no rebote mientras lo mira en el código. Sería interesante ver la nueva traza del alcance realmente ampliada en la transición.

Respuestas (4)

Leí que está usando un DAC para generar la señal de onda sinusoidal. Las salidas DAC pueden fallar en los cambios de estado de salida, por lo que definitivamente debe aplicar un filtrado analógico a la salida DAC antes de alimentarlo a su circuito comparador. Esto puede ayudar a prevenir algunos de los desencadenantes de doble interrupción que probablemente ocurran.

También comentaría que realmente desea usar un comparador externo para este tipo de problema para que pueda aplicar la histéresis con resistencias sin el uso de una interacción de software. Esto también permitirá un mejor aislamiento del problema, ya que puede monitorear directamente la salida del comparador.

El último comentario se relaciona con el tipo de histéresis que está utilizando. Es un poco difícil ver exactamente qué esquema está utilizando, pero tenga en cuenta que lo que desea es un comportamiento que haga esto: desea la histéresis que tira del voltaje de umbral en la dirección OPUESTA a la transición de la señal. Entonces, para un flanco ascendente, desea que el umbral sea un poco más alto que el punto cero y luego, cuando el estado cambia, el umbral se reduce a un nivel más bajo.

+1 para la descripción adicional de cómo debería funcionar la dirección de histéresis. El párrafo 2 es un buen consejo, pero hacerlo internamente también está bien, siempre que se haga correctamente, lo que en este ejemplo no parece ser el caso.
@Trevor_G - :^)
@Trevor_G, ¿podría explicar por qué no se hace bien? Además, fuente proporcionada.
@MichaelKaras, gracias por tus sugerencias, probé un comparador externo con resistencias con el mismo resultado. Me hace preguntarme si el DAC está siendo ruidoso. Fuente proporcionada en la edición. Gracias por tu aporte por cierto.
@Hypomania, los rastros de su alcance son toda la explicación que necesita. No está funcionando como esperabas... así que algo no está bien ;D
@Trevor_G, ah, ya veo, no te entendí bien :) Desmontaré todo mañana y volveré a intentarlo, realmente aprecio tus aportes.
@MichaelKaras, actualizado
@Trevor_G, actualizado
Siempre que he medido los tiempos alto y bajo de la señal, he usado dos temporizadores, uno disparado en el borde positivo y el otro en el borde negativo. Esto da medio ciclo completo para procesar el conteo de la interrupción actual mientras que el otro temporizador se inicia y acumula el siguiente conteo. Con solo un temporizador, pierde la cuenta del tiempo de procesamiento de la interrupción o tiene conflictos entre la acumulación del temporizador y la lectura al mismo tiempo.
@MichaelKaras, pero el temporizador se lee dentro del ISR, que no se puede interrumpir. Además, funciona sin problemas si no uso el comparador, pero + para la idea, el problema es que solo me queda un temporizador de 16 bits :)
@Hypomania: sé que puedes leer el temporizador único en el ISR. Pero a menos que el temporizador tenga un búfer doble para que un registro de salida mantenga el conteo de un disparador mientras que el propio temporizador puede continuar contando, entonces es necesario detener el temporizador para que pueda leerlo y luego volver a habilitarlo después de haberlo leído. . Muchos temporizadores de MCU no tienen doble búfer de esa manera y, por lo tanto, el tiempo de procesamiento para ingresar al ISR cuando se vuelve a habilitar el temporizador se pierde en la medición de tiempo del período para el siguiente medio ciclo. Depende hasta cierto punto de qué tan rápido se está cronometrando el temporizador (continuación)
(continuación de arriba) pero nunca querrá estar en la situación de que está leyendo un valor de conteo cuando un reloj podría estar llegando al mismo tiempo para cambiar el conteo. No he investigado la MCU específica que está utilizando para ver si su temporizador tiene doble búfer en un evento de captura de activación o no.
@Hypomania: ¡por capricho, miré su hoja de datos de MCU AVR vinculada y vi que la función de captura de entrada del temporizador tiene doble búfer! De hecho, el temporizador en estas partes parece bastante robusto. Han pasado casi 15 años desde que usé piezas de AVR.

El problema con este escenario es que hay un retraso de tiempo entre el cambio del comparador y la interrupción que se maneja hasta el punto en que cambia el pin de "histéresis".

Su banda de histéresis también es bastante pequeña para ese nivel de señal considerando para qué la está usando. Especialmente cuando veo cuánto ruido hay en esa onda cuadrada en su alcance.

Con ambos factores en mente, existe una alta probabilidad de que en ciertos niveles de entrada obtenga múltiples aristas del comparador antes de que pueda manejar la primera. Verificar cuál es el estado del comparador durante ese controlador de interrupción no ayudará mucho ya que puede estar en cualquier estado.

Desafortunadamente, no ha detallado en la pregunta cómo funciona el controlador.

Sin embargo, su controlador debería funcionar de esta manera.

  1. Cuando el valor de histéresis está en el estado de umbral alto, debe estar esperando una interrupción de borde negativo.

  2. Cuando llegue dicha interrupción de borde negativo, cambie la histéresis al valor bajo, espere algunos ciclos, luego borre cualquier interrupción pendiente y comience a esperar una interrupción de borde positivo.

  3. Cuando llegue dicha interrupción de borde positivo, vuelva a colocar el pin de histéresis en el valor alto, espere algunos ciclos, borre cualquier interrupción pendiente y comience a esperar una interrupción de borde negativo nuevamente.

  4. Repita desde el paso 1.

Por cierto, no estoy muy interesado en la forma en que está utilizando la referencia del comparador como el sesgo de la señal. Eso da como resultado una pequeña diafonía tanto de la señal a la referencia como de la histéresis a la señal, especialmente con señales de baja frecuencia. De acuerdo con esos valores, ese efecto debería ser pequeño, pero por pureza, sería mejor un sesgo separado en la señal.

EDITAR: Re su código.

En la declaración else, cambia el borde de interrupción antes de establecer la histéresis.

En ninguno de los dos casos, haga una pausa y borre cualquier interrupción pendiente antes de regresar. (Tenga en cuenta que cambiar el registro de control de interrupciones puede crear interrupciones por sí mismo).

No sé si el Atmega hace interrupciones reentrantes, es decir, si un borde posterior interrumpirá el controlador que aún se está ejecutando desde el borde anterior. Si es así, debe manejar la concurrencia de manera adecuada.

No estoy seguro de para qué sirve la parte PORTC, pero probablemente deba pasar a la parte calificada.

Muchas gracias, probaré tus sugerencias mañana y te daré una actualización. En cuanto a mi ISR, tengo una declaración if-else if para el escenario exacto que ha descrito, excluyendo la espera.
@Hypomania, debe editar su pregunta y publicar su código de controlador de interrupción para que la gente pueda ver si se está equivocando en alguna parte. Sin embargo, puede ser solo ruido, parece que los rastros de su alcance tienen más de 50 mV de ruido allí. Aún con el ruido, espero que se corrija solo, ya sea con pulsos adicionales ocasionales en las transiciones.
Eso es lo que esperaba yo también. Lo hare lo antes posible.
Estoy totalmente de acuerdo con el punto que @Trevor_G está haciendo con respecto al uso de la referencia de umbral también como referencia para el lado a favor del viento del capacitor de acoplamiento de entrada.
@hipomanía ver editar
@Trevor_G, mi mal, PORTC era parte de un comentario, lo eliminé. En cuanto a la primera línea de su edición, ¿por qué importa? Sólo se aplica a la siguiente interrupción. Su segunda línea, nunca pensé que podría ser un problema, sin embargo, sin duda lo intentaré, ¡gracias!
@Hypomania Porque podría obtener otra interrupción entre esos dos comandos. También edité la edición...
@Trevor_G, AFAIK todas las interrupciones en los AVR están bloqueando (no interrumpibles). Además, de la hoja de datos: "El hardware borra ACI cuando se ejecuta el vector de manejo de interrupción correspondiente. Alternativamente, ACI se borra escribiendo un uno lógico en el indicador", siendo ACI el indicador de interrupción del comparador analógico. Sin embargo, las interrupciones se posponen, lo que podría causar todo el comportamiento extraño que he estado viendo. Intentaré deshabilitar las interrupciones globales al ingresar a la ISR mañana, gracias :)
@ Hipomanía eso es bueno. Luego, debe borrar cualquier indicador pendiente al final de la rutina, ya que para entonces puede estar esperando uno nuevo. No debería ser necesario deshabilitar global.

Este efecto es similar al rebote de contacto y se puede mitigar con las mismas técnicas de rebote que usaría para los botones pulsadores.

  • Decidir sobre el tiempo de reboteTd
  • mantener la marca de tiempo de la última interrupción de borde en una variable
  • si el tiempo entre la interrupción actual y la última es menor que Td, ignore la interrupción actual

ingrese la descripción de la imagen aquíEsta nota de diseño de TI explica exactamente el problema de una señal ruidosa y la necesidad de agregar una histéresis adecuada. Por supuesto, se hace con un comparador externo como recomienda @Michael Karas y también resuelve los disparos múltiples en transiciones de señal de entrada negativa, es decir, el problema de "rebote de contacto" como lo describen @Trevor_G y @Dmitry Grigoryev en sus respuestas https:// www.ti.com/lit/ug/tidu020a/tidu020a.pdf?ts=1621988309888&ref_url=https%253A%252F%252Fwww.google.com%252F

Usar "Respuesta #" no es confiable. El orden puede cambiar en función de varios factores, como la popularidad, los votos y varios otros factores. Haría bien en referirse a los nombres de los carteles o poner enlaces a las respuestas específicas a las que desea referirse.
@MichaelKaras, gracias, puntos válidos. Editado.