ฉันกำลังทำงานในโครงการ "เรียบง่าย" ที่ฉันต้องการวัดความถี่ของคลื่นไซน์ที่แตกต่างกันไปตามแอมพลิจูดและความถี่ เพื่อลดความซับซ้อนของสิ่งต่าง ๆ ตอนนี้ฉันมีเพียงอินพุตคลื่นไซน์ (27Hz) ความถี่คงที่ (อินพุตลบของตัวเปรียบเทียบ) ซึ่งสามารถเปลี่ยนแปลงได้ในแอมพลิจูดเท่านั้น (โดยใช้โพเทนชิออมิเตอร์) อินพุตเชิงบวกของตัวเปรียบเทียบถูกตั้งค่าเป็น Vcc / 2 เอาต์พุตของตัวเปรียบเทียบจะถูกป้อนเข้าสู่รีจิสเตอร์การจับการป้อนข้อมูลของไมโครคอนโทรลเลอร์ atmega2560 เพื่อวัดความถี่
ปัญหาคือที่แอมพลิจูดบางอย่างของสัญญาณอินพุตฉันได้รับการสลับค่อนข้างรุนแรง (หรือบางครั้งแถบแบนด์ตาย) บนเอาต์พุตซึ่งมีลักษณะดังนี้:
โดยที่ผลลัพธ์ที่คาดไว้ควรมีลักษณะดังนี้:
สิ่งที่ฉันได้ลองไปแล้ว:
ใช้ตัวเปรียบเทียบภายในของ atmega2560 ใช้ตัวเปรียบเทียบภายนอก แนะนำ hysteresis โดยใช้ซอฟต์แวร์และวงจรทริกเกอร์ Schmitt พยายามตั้งค่าอินพุตที่หลากหลายรวมถึงการตั้งค่าการอ้างอิงแบบคงที่และการตั้งค่าตัวแบ่งข้อมูล ลองใช้ 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/
UPDATE:
ฉันลองทำตามคำแนะนำทั้งหมดของคุณแล้วไม่มีข้อเสนอแนะใด ๆ เลย การล้างค่าสถานะอินเตอร์รัปต์หรือปิดใช้งานการอินเตอร์รัปต์ภายในหรือภายนอก ISR ไม่มีผลใด ๆ ฉันดูเหมือนจะเข้าใจผิดว่าการเปรียบเทียบของชิปทำงานอย่างไร
อย่างที่ฉันได้กล่าวไปในตอนแรกฉันจะใช้การจับสัญญาณเข้าเพื่อวัดความถี่ของคลื่นสี่เหลี่ยมที่มาจากคลื่นไซน์ เอาท์พุทของเครื่องเปรียบเทียบถูกป้อนเข้าสู่พินการจับภาพอินพุตจากนั้นใช้ตัวจับเวลาเพื่อวัดระยะเวลาง่าย ๆ
นี่คือแผนภาพเปรียบเทียบอะนาล็อกของ atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datsheet.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 ของค่าครึ่งหนึ่งของค่าที่ถูกต้องหรือค่าสุ่ม ฉันลองใช้ register bits ของตัวจับเวลาสำหรับคำสั่งแบบมีเงื่อนไขรวมถึง register bits ของ comparator ใน ISR ของฉันนี่คือการกำหนดค่าเดียวที่เรียงลำดับของงาน
สิ่งที่ฉันทำในภายหลังในวันนั้นคือการใช้ตัวเปรียบเทียบภายนอกแทนการตั้งค่าและแหล่งข้อมูลที่เหมือนกัน (ยกเว้นบรรทัดทั้งหมดที่เกี่ยวข้องกับตัวเปรียบเทียบ) เอาท์พุทของมันถูกป้อนเข้าสู่หมุดจับการป้อนข้อมูลและทำงานตามที่ตั้งใจไว้ (ไม่ต้องการแม้แต่ฮิสเทรีซิส)
ณ จุดนี้ฉันสามารถพูดได้ว่าฉันได้รับการแก้ไขโดยใช้ตัวเปรียบเทียบภายนอก แต่ฉันไม่รู้ว่าทำไมตัวภายในไม่ทำงาน ฉันได้อ่านบทความและคำแนะนำมากมายเกี่ยวกับเรื่องนี้อ่านห้องสมุดที่แตกต่างพยายามเลียนแบบพวกเขาโดยไม่มีผลลัพธ์ที่ยอมรับได้ แผ่นข้อมูลมีเพียง 5 หน้าในหน่วยเปรียบเทียบทั้งหมดฉันอ่านซ้ำหลายครั้งและฉันไม่เห็นสิ่งที่ฉันทำผิด
ฉันต้องการค้นหาวิธีการใช้อย่างถูกต้อง แต่หากล้มเหลวฉันได้รับการสำรองข้อมูล หากคุณมีข้อมูลเพิ่มเติมใด ๆ ก็ชื่นชมอย่างมาก