我正在做一个相对“简单”的项目,我需要测量幅度和频率变化的正弦波的频率。为简化起见,目前,我只有一个固定频率 (27Hz) 正弦波输入(比较器的负输入),其幅度只能改变(使用电位计)。比较器的正输入设置为 Vcc/2。然后将比较器的输出馈入 atmega2560 微控制器的输入捕捉寄存器以测量频率。
问题是,在输入信号的某些幅度下,我在输出上得到非常强烈的切换(或有时是死区),如下所示:
预期输出应如下所示:
到目前为止我尝试过的事情:
使用内部 atmega2560 的内部比较器。使用外部比较器。使用软件和施密特触发器电路引入迟滞。尝试了各种输入设置,包括固定参考设置和数据切片器设置。尝试不同的atmega2560。尝试不同的时钟速度。
有些解决方案比其他解决方案更稳定,但没有一个是可以接受的。到目前为止,我已经解决了最稳定的配置:
使用此设置,某些事情会改善/改变稳定性,但仍远非完美:
改变 R5 的值以增加滞后。完全移除 C2(不知道为什么)。触摸面包板上的电线(其中相当多的电线彼此相邻)。将电源从外部切换到 USB,反之亦然。
在这一点上,要么是噪音,要么是我用来生成正弦波的 DAC,要么是我在做一些非常基本的错误。该电路对其他人有效,没有任何问题,因此我的配置或环境一定有问题。
如果有人有任何建议,我将不胜感激您的时间。
这是我的最小来源:
#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);
}
}
此外,这是电路图和库本身的链接:
http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
更新:
我已经尝试了您的所有建议,但没有一个有效,只有一个。清除中断标志或禁用 ISR 内部或外部的中断实际上并没有任何效果。我似乎误解了芯片的比较器寄存器实际上是如何工作的。
正如我最初提到的,我打算使用输入捕获来测量从正弦波派生的方波的频率。比较器的输出馈入输入捕捉引脚,然后使用定时器测量周期,简单。
这是 atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf的模拟比较器图,第 265 页:
如您所见,比较器有两个输出,ACO 和 ACIS0+ACIS1。ACO 在 + 输入 > - 输入时置位,在 + 输入 < - 输入时清零。ACIS0+ACIS1 是边沿选择位。
我最初做的是检查 ISR 中的边缘类型。我改为将 ISR 更改为:
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACO))) { // + < -
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
}
else {
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
并且输出表现得完美无缺(就像第二张图片一样)。然后我开始测量脉冲的宽度,但结果不是很好。尽管有清晰的信号,我的 LCD 显示屏上的强烈切换,数字跳到随机值或保持在 0。我使用不同的条件多次重写了我的代码,到目前为止我得到的唯一半稳定的解决方案是:
#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);
}
我的意思是半稳定,我得到正确值的 1/3 次。其他时间的 2/3 是正确值的一半或随机值。我尝试在 ISR 中使用定时器的寄存器位作为条件语句以及比较器的寄存器位,这是唯一可行的配置。
我当天晚些时候做的是使用外部比较器,而不是使用相同的设置和源(不包括与比较器相关的所有行)。它的输出被送入输入捕捉引脚,它按预期工作(甚至不需要任何滞后)。
在这一点上,我可以说我通过使用外部比较器解决了这个问题,但是我不知道为什么内部比较器不表现自己。我已经阅读了很多关于这方面的帖子和指南,阅读了不同的库,试图模仿它们而没有任何可接受的结果。整个比较器单元的数据表只有 5 页,我重读了很多次,我看不出我做错了什么。
我想知道如何正确使用它,但如果失败,我有一个备份。如果您有任何进一步的意见,我们将不胜感激。