随机且不可预测的模拟比较器行为


10

我正在一个相对“简单”的项目中工作,我需要测量振幅和频率变化的正弦波的频率。为了简化起见,现在,我只有一个固定频率(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页,我已多次阅读,但看不到我做错了什么。

我想找出如何正确使用它,但是如果失败,我会有一个备份。如果您有其他意见,我们将不胜感激。


4
对于初学者...在输出和+ ve输入之间添加一个1M电阻。这是产生迟滞的原因,而不是您的R5 ...只是改变了基准
JonRB

1
如何产生芯片内部比较器无法访问的比较器输出的示波器图像?
安迪(aka Andy)'18

2
输入ISR时是否要禁用其他中断?您可能需要-可能是大多数ISR都受到了双重打击。
安迪(aka Andy)'18

1
您如何切换磁滞引脚,并通过当前值对其进行限定。中断和切换之间的延迟可能会困扰您。
Trevor_G

1
原理图中未显示的是引脚5和引脚6之间的内部电容,可以使用引脚7上的内部上拉电阻来代替吗?
Jasen

Answers:


13

我读到您正在使用DAC生成正弦波信号。DAC输出会在输出状态变化时出现毛刺,因此在将其馈入比较器电路之前,您一定要对DAC输出进行一些模拟滤波。这有助于防止可能发生的某些双中断触发。

我还要评论一下,您确实希望针对此类型的问题使用外部比较器,以便可以在不使用软件交互的情况下将带滞回的电阻应用于电阻器。由于您可以直接监视比较器的输出,因此也可以更好地隔离问题。

最后的评论与您使用的磁滞类型有关。很难确切地看到正在使用的方案,但是请注意,您想要的是执行此操作的行为:您希望磁滞在相反方向上将阈值电压拉向OPPOSITE方向,而不是信号在过渡。因此,对于上升沿,您希望阈值比零点高一点,然后在状态更改时将阈值拉到较低的水平。


1
+1可进一步说明磁滞方向的工作方式。第2款是很好的建议,但内部也可以,只要做得对,在此示例中,情况似乎并非如此。
Trevor_G

@Trevor_G-:^)
Michael Karas

1
@Hypomania-我知道您可以在ISR中读取单个计时器。但是除非定时器是双缓冲的,否则输出寄存器将保留来自触发器的计数,而定时器本身仍可继续计数,则有必要停止定时器,以便您可以读取它,然后在读取后重新启用它。许多MCU定时器不是那样的双缓冲,因此进入ISR直到重新启用定时器的处理时间在下一个半周期的周期时间测量上浪费了时间。这在某种程度上取决于计时器的计时速度(续)
Michael Karas

1
(续上),但是当时钟可能同时出现以更改计数时,您永远不会想要读取计数值。我尚未研究您使用的特定MCU,以查看您的计时器是否在触发捕获事件中被双重缓冲。
Michael Karas

1
@Hypomania-一时兴起,我看了看您链接的AVR MCU数据表,发现定时器输入捕捉功能是双缓冲的!!实际上,这些部分中的计时器看起来非常强大。自从我使用任何AVR部件以来,已经过去了15年。
Michael Karas

6

这种情况的问题是,在比较器切换和处理中断之间的时间延迟到切换“滞后”引脚的时间。

考虑到信号的用途,该信号电平的磁滞带也很小。尤其是当我看到示波器上的方波有多少噪声时。

考虑到这两个因素,很有可能在某些输入电平下,比较器会先获得多个边沿,然后才能处理第一个。由于在中断处理程序中比较器状态可能处于两种状态,因此对其进行检查不会有太大帮助。

不幸的是,您还没有在问题中详细介绍处理程序的工作方式。

但是,您的处理程序应该像这样工作。

  1. 当磁滞值处于高阈值状态时,您应等待下降沿中断。

  2. 当所述下降沿中断到达时,将迟滞切换到较低的值,等待几个周期,然后清除所有未决的中断并开始等待上升沿中断。

  3. 当所述上升沿中断到达时,将滞后引脚切换回高电平,等待几个周期,清除所有未决的中断,然后再次开始等待下降沿中断。

  4. 从步骤1开始重复。

顺便说一句,我不太热衷于使用比较器参考作为信号偏置的方式。这会导致从信号到参考以及从磁滞到信号的串扰,尤其是低频信号。有了这些值,影响应该很小,但对于纯净度而言,对信号单独偏置会更好。

编辑:重新您的代码。

在else语句中,您可以在设置滞后之前更改中断边沿。

无论哪种情况,您都不会暂停并清除所有未决的中断,然后再返回。(注意,更改中断控制寄存器可以自己创建中断。)

我不知道Atmega是否会重入中断,也就是说,后续边缘是否会中断前一个边缘中仍在运行的处理程序。如果是这样,则需要适当地处理并发。

不确定PORTC零件是做什么的,但可能需要移入合格零件。


非常感谢您,我明天将尝试您的建议并向您提供更新。至于我的ISR,我有if-else if语句,用于描述您所描述的确切场景(不包括等待)。
Shibalicious

1
@Hypomania,您应该编辑问题并发布中断处理程序代码,以便人们可以查看您是否在某个地方搞砸了。虽然这可能只是噪声,但您的示波器走线看起来好像那里的噪声超过50mV。仍然有噪声,我希望它能自行校正,全部都是在过渡时偶尔会有额外的脉冲。
Trevor_G

这也是我所期望的。会尽快做到的。
Shibalicious

1
@Hypomania见编辑
Trevor_G

1
@Hypomania因为您可能在这两个命令之间遇到另一个中断。我还编辑了编辑…
Trevor_G,

3

这种效果类似于触点弹跳,可以通过使用与按钮相同的弹跳技术来缓解。

  • 决定去抖时间 Td
  • 将最后一个边缘中断的时间戳保存在变量中
  • 如果当前中断到最后一个中断之间的时间小于Td,则忽略当前中断
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.