Comportamiento de comparador analógico aleatorio e impredecible

10

Estoy trabajando en un proyecto relativamente "simple" donde 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 (27Hz) (entrada negativa del comparador) que solo puede variar en amplitud (usando un potenciómetro). La entrada positiva del comparador se establece en Vcc / 2. La salida del comparador se alimenta al registro de captura de entrada del microcontrolador atmega2560 para medir la frecuencia.

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

ingrese la descripción de la imagen aquí

Donde como la salida esperada debería verse más o menos así:

ingrese la descripción de la imagen aquí

Cosas que he probado hasta ahora:

Usando el comparador interno de atmega2560 interno. Usando un comparador externo. Introducción de histéresis mediante 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. Probar diferentes atmega2560's. Probar diferentes velocidades de reloj.

Algunas soluciones eran más estables que otras, pero ninguna de ellas era aceptable. Me he decidido por 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:

Cambio de valor de R5 para aumentar la histéresis. Eliminar C2 completamente (no tengo idea de por qué). Tocando cables en el tablero (bastantes de ellos uno al lado del otro). Cambio de fuentes de alimentación de externo a USB y viceversa.

En este punto, es ruido, mi DAC con el que estoy generando la onda sinusoidal o estoy haciendo algo fundamentalmente incorrecto. 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:

He probado todas sus sugerencias, ninguna de ellas 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 cómo funciona realmente el registro comparador del chip.

Como había mencionado 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 comparativo 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 puede ver, el comparador tiene dos salidas, ACO y ACIS0 + ACIS1. ACO se establece cuando + input> - input, se borra cuando + input <- input. 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 excelentes. Alternar intensamente en mi pantalla LCD, los números saltan a valores aleatorios o permanecen en 0, a pesar de tener una señal limpia. Reescribí mi código muchas veces usando diferentes condiciones, la única solución semi-estable 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 semi-estable 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 las 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 en el día fue usar un comparador externo en su lugar 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 necesitó 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 solo. He leído muchas publicaciones y guías sobre esto, leí diferentes bibliotecas, intenté 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 lo que estoy haciendo mal.

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

Shibalicious
fuente
44
para empezar ... agregue una resistencia de 1M entre la salida y la entrada + ve. ESTO es lo que crea histéresis, no su R5 ... eso solo cambia la referencia
JonRB
1
¿Cómo puede producir imágenes de alcance de la salida desde un comparador que está dentro del chip y no es accesible?
Andy alias
2
¿Estás inhabilitando más interrupciones cuando ingresas a un ISR? Es posible que deba hacerlo, podría ser que la mayoría de los ISR están recibiendo doble golpe.
Andy alias
1
¿Cómo está alternando el pin de histéresis y si lo califica por el valor actual? La demora entre la interrupción y la palanca puede estar jugando contigo.
Trevor_G
1
no se muestra en su esquema la capacitancia interna entre el pin5 y el pin6, ¿puede usar el pull-up interno en el pin7 para hacer su histéresis?
Jasen

Respuestas:

13

Leí que está utilizando 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 algo de filtro analógico a la salida DAC antes de alimentarlo en su circuito comparador. Esto puede ayudar a prevenir algunos de los disparadores de doble interrupción que es probable que ocurran.

También comentaría que realmente desea utilizar un comparador externo para este tipo de problema, de modo 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 quiere es un comportamiento que haga esto: desea la histéresis que tira del voltaje de umbral en la dirección OPUESTA de lo que la señal está en transición. Entonces, para un borde ascendente, desea que el umbral sea un poco más alto que el punto cero y luego, cuando el estado cambia, el umbral se lleva a un nivel más bajo.

Michael Karas
fuente
1
+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 -: ^)
Michael Karas
1
@Hypomania: sé que puedes leer el temporizador único en el ISR. Pero a menos que el temporizador tenga doble búfer para que un registro de salida contenga el recuento de un disparador mientras que el 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 MCU no tienen doble búfer como ese y, por lo tanto, el tiempo de procesamiento para ingresar al ISR cuando se vuelve a habilitar el temporizador se pierde tiempo en la medición del tiempo del período para el próximo medio ciclo. Depende en cierta medida de la velocidad del reloj (continuación)
Michael Karas
1
(continúa desde arriba) pero nunca desea estar en la situación de que está leyendo un valor de conteo cuando podría estar llegando un reloj al mismo tiempo para cambiar el conteo. No he investigado el MCU específico que está utilizando para ver si su temporizador tiene doble buffer en un evento de captura de disparador o no.
Michael Karas
1
@Hypomania: ¡por capricho, miré su hoja de datos de AVR MCU 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é cualquier pieza AVR.
Michael Karas
6

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 donde se 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 tu telescopio.

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 el primero. Comprobar para ver cuál es el estado del comparador durante ese controlador de interrupciones no ayudará mucho, ya que puede estar en cualquier estado.

Lamentablemente, 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 en el estado de umbral alto debería 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 elimine cualquier interrupción pendiente y comience a esperar una interrupción de borde positiva.

  3. Cuando llegue dicha interrupción de borde positivo, vuelva a alternar el pin de histéresis al 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 demasiado 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 conversación cruzada tanto de la señal a la referencia como de la histéresis a la señal, especialmente con señales de baja frecuencia. Concedido con esos valores, ese efecto debería ser pequeño, pero para la pureza, un sesgo separado en la señal sería mejor.

EDITAR: Re su código.

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

En ninguno de los casos, hace una pausa y borra las interrupciones pendientes 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 aún en ejecución 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.

Trevor_G
fuente
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.
Shibalicious
1
@Hypomania, debe editar su pregunta y publicar su código de controlador de interrupciones para que la gente pueda ver si está cometiendo algún error. Sin embargo, puede ser solo ruido, sus trazas de alcance parecen tener más de 50mV de ruido allí. Todavía con ruido, esperaría que se corrigiera solo, todo con pulsos adicionales ocasionales en las transiciones.
Trevor_G
Eso es lo que yo también esperaba. Lo haremos lo antes posible.
Shibalicious
1
@Hypomania ver edición
Trevor_G
1
@Hypomania Porque podrías obtener otra interrupción entre esos dos comandos. También
edité
3

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

  • Decidir sobre el tiempo de rebote Td
  • 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
Dmitry Grigoryev
fuente