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í:
Donde, como el resultado esperado, debería verse así:
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:
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:
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.
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.
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.
Cuando el valor de histéresis está en el estado de umbral alto, debe estar esperando una interrupción de borde negativo.
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.
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.
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.
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.
Td
Td
, ignore la interrupción actualEsta 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
usuario16222
Andy alias
Shibalicioso
Shibalicioso
Andy alias
Shibalicioso
Andy alias
Shibalicioso
Andy alias
Shibalicioso
Trevor_G
Shibalicioso
Trevor_G
Jasén
Shibalicioso
Shibalicioso
Trevor_G