Я работаю над относительно «простым» проектом, в котором мне нужно измерить частоту синусоидальной волны, которая варьируется по амплитуде и частоте. Для упрощения, на данный момент у меня есть только синусоидальный вход с фиксированной частотой (27 Гц) (отрицательный вход компаратора), который можно изменять только по амплитуде (используя потенциометр). Положительный вход компаратора установлен на Vcc / 2. Затем выход компаратора подается на входной регистр захвата микроконтроллера atmega2560 для измерения частоты.
Проблема в том, что при определенных амплитудах входного сигнала я получаю довольно интенсивное переключение (или иногда мертвые зоны) на выходе, который выглядит так:
Где ожидаемый результат должен выглядеть примерно так:
Вещи, которые я пробовал до сих пор:
Использование внутреннего компаратора atmega2560. Использование внешнего компаратора. Введение гистерезиса с использованием программного обеспечения и схемы триггера Шмитта. Пробовал различные входные настройки, включая фиксированную настройку эталона и настройку среза данных. Пробую разные atmega2560. Пробую разные тактовые частоты.
Некоторые решения были более стабильными, чем другие, но ни одно из них не было приемлемым. Я остановился на самой стабильной конфигурации:
При такой настройке некоторые вещи улучшают / изменяют стабильность, но все еще не достигли совершенства:
Изменение значения R5 для увеличения гистерезиса. Полное удаление C2 (не знаю почему). Касаясь проводов на макете (их довольно много рядом). Переключение источников питания с внешнего на USB и наоборот.
На данный момент это либо шум, мой ЦАП, с помощью которого я генерирую синусоидальную волну, либо я делаю что-то очень фундаментальное неправильно. Эта схема работала для других людей без каких-либо проблем, поэтому что-то должно быть не так с моей конфигурацией или средой.
Если у кого-то есть предложения, я буду очень признателен за ваше время.
Вот мой минимальный источник:
#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 устанавливается, когда + input> - input, очищается, когда + input <- input. 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);
}
}
И вывод себя вел себя без нареканий (как на второй картинке). Затем я продолжил измерять ширину импульсов, но результаты были невелики. Интенсивное переключение на моем ЖК-дисплее, числа переходят в случайные значения или остаются на 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 страниц на весь блок компаратора, я перечитываю его много раз и не вижу, что делаю неправильно.
Я хотел бы узнать, как правильно его использовать, но в случае неудачи у меня есть резервная копия. Если у вас есть какие-либо дополнительные материалы, это очень ценится.
источник
Ответы:
Я читал, что вы используете ЦАП для генерации синусоидального сигнала. Выходы ЦАП могут давать сбой при изменении состояния выхода, поэтому вам обязательно следует применить некоторую аналоговую фильтрацию к выходу ЦАП, прежде чем подавать его в схему компаратора. Это может помочь предотвратить некоторые из триггеров двойного прерывания, которые могут произойти.
Я также хотел бы отметить, что вы действительно хотите использовать внешний компаратор для решения этой проблемы, чтобы вы могли применять гистерезис с резисторами без использования программного взаимодействия. Это также позволит улучшить изоляцию проблемы, поскольку вы можете напрямую контролировать выход компаратора.
Последний комментарий относится к типу гистерезиса, который вы используете. Немного трудно понять, какую именно схему вы используете, но обратите внимание, что вам нужно поведение, которое делает это: вы хотите гистерезис, который притягивает пороговое напряжение в направлении OPPOSITE, чем сигнал, который передается. Таким образом, для нарастающего фронта вы хотите, чтобы порог был немного выше нулевой точки, а затем, когда состояние изменяется, порог переводится на более низкий уровень.
источник
Проблемы, связанные с этим сценарием, заключаются в том, что между переключением компаратора и обработкой прерывания существует временная задержка до точки, где вы переключаете вывод «гистерезиса».
Ваша полоса гистерезиса также довольно мала для этого уровня сигнала, учитывая, для чего вы его используете. Особенно, когда я вижу, сколько шума на этой прямоугольной волне на вашем прицеле.
Учитывая оба этих фактора, существует высокая вероятность того, что на определенных входных уровнях вы получите несколько ребер от компаратора, прежде чем сможете обработать первый. Проверка состояния компаратора во время этого обработчика прерываний мало поможет, так как он может находиться в любом состоянии.
К сожалению, вы не уточнили в вопросе, как работает обработчик.
Однако ваш обработчик должен работать примерно так.
Когда значение гистерезиса в верхнем пороговом состоянии, вы должны ждать отрицательного граничного прерывания.
Когда поступает упомянутое прерывание по отрицательному фронту, переключите гистерезис на низкое значение, подождите несколько циклов, затем удалите все ожидающие прерывания и начните ожидание прерывания по положительному фронту.
Когда поступает указанное прерывание положительного фронта, верните гистерезисный вывод обратно в высокое значение, подождите несколько циклов, очистите все ожидающие прерывания и снова начните ожидание прерывания отрицательного фронта.
Повторите с шага 1.
Кстати, я не слишком заинтересован в том, как вы используете эталон компаратора в качестве смещения для сигнала. Это приводит к небольшим перекрестным помехам как от сигнала к эталону, так и от гистерезиса к сигналу, особенно с низкочастотными сигналами. Учитывая те значения, эффект должен быть небольшим, но для чистоты лучше было бы использовать отдельное смещение сигнала.
РЕДАКТИРОВАТЬ: Re ваш код.
В операторе else вы изменяете край прерывания перед тем, как установить гистерезис.
Ни в том, ни в другом случае вы не приостанавливаете и не очищаете все ожидающие прерывания перед возвратом. (Обратите внимание, что изменение регистра управления прерываниями может создавать прерывания само по себе.)
Я не знаю, будет ли Atmega повторно вводить прерывания, то есть, если последующее ребро прервет все еще работающий обработчик из предыдущего ребра. Если это так, вам нужно соответствующим образом обрабатывать параллелизм.
Не уверен, для чего предназначена часть PORTC, но, вероятно, она должна перейти в квалифицированную часть.
источник
Этот эффект похож на отскок контакта и может быть смягчен с помощью тех же методов отказов, которые вы использовали бы для кнопок.
Td
Td
, игнорирует текущее прерываниеисточник