Estou trabalhando em um projeto relativamente "simples" em que preciso medir a frequência de uma onda senoidal que varia em amplitude e frequência. Para simplificar, por enquanto, só tenho uma entrada de onda senoidal de frequência fixa (27Hz) (entrada negativa do comparador) que só pode variar em amplitude (usando um potenciômetro). A entrada positiva do comparador é definida como Vcc / 2. A saída do comparador é então alimentada no registro de captura de entrada do microcontrolador atmega2560 para medir a frequência.
O problema é que, em certas amplitudes do sinal de entrada, eu alterno bastante intensamente (ou às vezes, faixas mortas) na saída, que se parece com isso:
Onde, como a saída esperada, deve ser algo como isto:
Coisas que tentei até agora:
Usando o comparador interno do atmega2560. Usando um comparador externo. Introdução à histerese usando software e circuito de disparo de Schmitt. Tentei várias configurações de entrada, incluindo configuração de referência fixa e configuração do slicer de dados. Tentando diferentes atmega2560's. Tentando diferentes velocidades de relógio.
Algumas soluções eram mais estáveis que outras, mas nenhuma delas era nem de longe aceitável. Eu resolvi com a configuração mais estável até agora:
Com essa configuração, certas coisas melhoram / alteram a estabilidade, mas ainda nem de longe são perfeitas:
Alterando o valor de R5 para aumentar a histerese. Removendo completamente o C2 (não faço ideia do porquê). Tocando fios na tábua de pão (alguns deles próximos um do outro). Alternando as fontes de alimentação de externas para USB e vice-versa.
Nesse ponto, é o ruído, meu DAC com o qual estou gerando a onda senoidal ou estou fazendo algo muito fundamental incorretamente. Este circuito funcionou para outras pessoas sem problemas, portanto, algo deve estar errado com minha configuração ou ambiente.
Se alguém tiver alguma sugestão, eu apreciaria muito o seu tempo.
Aqui está minha fonte 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);
}
}
Além disso, aqui está o link para o diagrama de circuitos e a própria biblioteca:
http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
ATUALIZAR:
Eu tentei todas as suas sugestões, nenhuma delas funcionou, exceto uma. Limpar os sinalizadores de interrupção ou desativar as interrupções dentro ou fora do ISR realmente não teve nenhum efeito. Pareço entender mal como o registro comparador do chip realmente funciona.
Como mencionei inicialmente, eu usaria a captura de entrada para medir a frequência de uma onda quadrada derivada de uma onda senoidal. A saída do comparador é alimentada no pino de captura de entrada e, em seguida, use temporizadores para medir o período, simples.
Aqui está o diagrama comparador analógico do 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 você pode ver, o comparador possui duas saídas, ACO e ACIS0 + ACIS1. ACO é definido quando + entrada> - entrada, limpo quando + entrada <- entrada. ACIS0 + ACIS1 são bits de seleção de borda.
O que eu estava fazendo inicialmente estava verificando o tipo de borda no meu ISR. Mudei o ISR para isso:
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACO))) { // + < -
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
}
else {
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
E a saída se comportou perfeitamente (exatamente como na segunda foto). Depois, medi a largura dos pulsos, mas os resultados não foram excelentes. Alternância intensa no visor LCD, números saltando para valores aleatórios ou permanecendo em 0, apesar de ter um sinal limpo. Eu reescrevi meu código várias vezes usando condições diferentes, a única solução semi-estável que eu tenho até agora é a seguinte:
#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-estável, quero dizer, obtenho o valor correto 1/3 das vezes. Nas outras vezes, 2/3 das vezes é metade do valor correto ou um valor aleatório. Tentei usar os bits de registro do temporizador para instruções condicionais, bem como os bits de registro do comparador no meu ISR, esta é a única configuração que funciona.
O que fiz no final do dia foi usar um comparador externo, com a mesma configuração e fonte (excluindo todas as linhas relacionadas ao comparador). Sua saída foi alimentada no pino de captura de entrada e funcionou como planejado (nem sequer precisava de histerese).
Neste ponto, posso dizer que resolvi usando um comparador externo, mas não tenho idéia do porquê o interno não se comporta. Eu li muitos posts e guias sobre isso, li diferentes bibliotecas, tentei imitá-las sem nenhum resultado aceitável. A folha de dados possui apenas 5 páginas em toda a unidade comparadora, reli-a várias vezes e não vejo o que estou fazendo de errado.
Gostaria de descobrir como usá-lo corretamente, mas se isso falhar, tenho um backup. Se você tiver mais alguma informação, é muito apreciada.