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í:
Donde como la salida esperada debería verse más o menos así:
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:
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:
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.
fuente
Respuestas:
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.
fuente
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.
Cuando el valor de histéresis en el estado de umbral alto debería 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 elimine cualquier interrupción pendiente y comience a esperar una interrupción de borde positiva.
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.
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.
fuente
Este efecto es similar al rebote de contacto y puede mitigarse con las mismas técnicas de rebote que usaría para los botones.
Td
Td
, ignore la interrupción actualfuente