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.