我正在一个相对“简单”的项目中工作,我需要测量振幅和频率变化的正弦波的频率。为了简化起见,现在,我只有一个固定频率(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页,我已多次阅读,但看不到我做错了什么。
我想找出如何正确使用它,但是如果失败,我会有一个备份。如果您有其他意见,我们将不胜感激。