พฤติกรรมเปรียบเทียบแบบอะนาล็อกสุ่มและไม่แน่นอน


10

ฉันกำลังทำงานในโครงการ "เรียบง่าย" ที่ฉันต้องการวัดความถี่ของคลื่นไซน์ที่แตกต่างกันไปตามแอมพลิจูดและความถี่ เพื่อลดความซับซ้อนของสิ่งต่าง ๆ ตอนนี้ฉันมีเพียงอินพุตคลื่นไซน์ (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 หน้าในหน่วยเปรียบเทียบทั้งหมดฉันอ่านซ้ำหลายครั้งและฉันไม่เห็นสิ่งที่ฉันทำผิด

ฉันต้องการค้นหาวิธีการใช้อย่างถูกต้อง แต่หากล้มเหลวฉันได้รับการสำรองข้อมูล หากคุณมีข้อมูลเพิ่มเติมใด ๆ ก็ชื่นชมอย่างมาก


4
สำหรับ starters ... เพิ่มตัวต้านทาน 1M ระหว่างเอาต์พุตและอินพุต + ve นี่คือสิ่งที่สร้าง hysteresis ไม่ใช่ R5 ของคุณ ... แค่เปลี่ยนการอ้างอิง
JonRB

1
คุณจะสร้างภาพขอบเขตของเอาต์พุตจากตัวเปรียบเทียบที่อยู่ภายในชิปและไม่สามารถเข้าถึงได้อย่างไร
แอนดี้อาคา

2
คุณปิดใช้งานการขัดจังหวะเพิ่มเติมเมื่อคุณป้อน ISR หรือไม่ คุณอาจจำเป็นต้องใช้ - อาจเป็นไปได้ว่า ISR ส่วนใหญ่ได้รับความนิยมเป็นสองเท่า
แอนดี้อาคา

1
คุณเป็นอย่างไรบ้างในการสลับพินฮิสเทรีซิสและคุณมีคุณสมบัติตามมูลค่าปัจจุบัน ความล่าช้าระหว่างการขัดจังหวะและการสลับอาจทำให้คุณสับสน
Trevor_G

1
ไม่ได้แสดงในแผนผังของคุณคือความจุภายในระหว่าง pin5 และ pin6 คุณสามารถใช้ pull-up ภายในบน pin7 เพื่อสร้าง hysterisis แทนได้หรือไม่?
Jasen

คำตอบ:


13

ฉันอ่านว่าคุณกำลังใช้ DAC เพื่อสร้างสัญญาณคลื่นไซน์ เอาท์พุท DAC สามารถผิดพลาดที่การเปลี่ยนแปลงสถานะเอาท์พุทดังนั้นคุณควรใช้การกรองแบบอะนาล็อกบางอย่างกับเอาท์พุท DAC ก่อนที่จะป้อนลงในวงจรเปรียบเทียบของคุณ สิ่งนี้สามารถช่วยป้องกันการทริกเกอร์การขัดจังหวะสองครั้งที่น่าจะเกิดขึ้น

ฉันจะแสดงความคิดเห็นด้วยว่าคุณต้องการใช้ตัวเปรียบเทียบภายนอกสำหรับปัญหาประเภทนี้เพื่อให้คุณสามารถใช้ฮิสเทรีซิสกับตัวต้านทานโดยไม่ต้องใช้การโต้ตอบกับซอฟต์แวร์ สิ่งนี้จะช่วยให้แยกปัญหาได้ดีขึ้นเนื่องจากคุณสามารถตรวจสอบผลลัพธ์ของตัวเปรียบเทียบได้โดยตรง

ความคิดเห็นล่าสุดเกี่ยวข้องกับประเภทของ hysteresis ที่คุณใช้ เป็นการยากที่จะดูว่าคุณใช้รูปแบบใด แต่โปรดทราบว่าสิ่งที่คุณต้องการคือพฤติกรรมที่ทำสิ่งนี้: คุณต้องการฮิสเทรีซิสที่ดึงแรงดันธรณีประตูในทิศทาง 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 ที่เชื่อมโยงของคุณและดูว่าฟังก์ชั่นการจับการป้อนข้อมูลตัวจับเวลานั้นได้รับการบัฟเฟอร์สองเท่า !! ตามความเป็นจริงตัวจับเวลาในชิ้นส่วนเหล่านี้ดูแข็งแกร่งมาก เป็นเวลาเกือบ 15 ปีแล้วที่ฉันใช้ชิ้นส่วน AVR
Michael Karas

6

ปัญหาที่เกิดขึ้นกับสถานการณ์นี้คือมีการหน่วงเวลาระหว่างการเปรียบเทียบ comparator และการขัดจังหวะที่ถูกจัดการไปยังจุดที่คุณสลับพิน "hysteresis"

แถบฮิสเทรีซิสของคุณก็ค่อนข้างเล็กสำหรับระดับสัญญาณนั้นเมื่อพิจารณาว่าคุณใช้งานอะไร โดยเฉพาะอย่างยิ่งเมื่อฉันเห็นว่าเสียงของคลื่นสี่เหลี่ยมที่อยู่ในขอบเขตของคุณ

เมื่อคำนึงถึงปัจจัยทั้งสองนี้มีความน่าจะเป็นสูงที่ในระดับอินพุตบางระดับคุณจะได้รับขอบจำนวนมากจากตัวเปรียบเทียบก่อนที่คุณจะสามารถจัดการระดับแรกได้ การตรวจสอบเพื่อดูว่าสถานะตัวเปรียบเทียบคืออะไรระหว่างตัวจัดการขัดจังหวะนั้นจะไม่ช่วยอะไรมากนักเนื่องจากมันสามารถอยู่ในสถานะใดสถานะหนึ่ง

น่าเสียดายที่คุณยังไม่มีรายละเอียดในคำถามเกี่ยวกับวิธีการทำงานของตัวจัดการ

อย่างไรก็ตามผู้จัดการของคุณควรทำงานแบบนี้

  1. เมื่อค่า hysteresis ในสถานะ threshold สูงคุณควรรอการขัดจังหวะการลบขอบ

  2. เมื่อการขัดจังหวะเชิงลบดังกล่าวมาถึงให้สลับฮิสเทอรีซิสเป็นค่าต่ำรอสักครู่รอบแล้วล้างการขัดจังหวะที่ค้างอยู่ใด ๆ และเริ่มรอการขัดจังหวะเชิงบวก

  3. เมื่อมีการขัดจังหวะที่เป็นบวกเกิดขึ้นให้สลับฮิสเทอรีซิสกลับไปที่ค่าสูงรอสักสองสามรอบรอการขัดจังหวะที่ค้างอยู่และเริ่มรอการขัดจังหวะการลบขอบอีกครั้ง

  4. ทำซ้ำจากขั้นตอนที่ 1

BTW ฉันไม่กระตือรือร้นในวิธีที่คุณใช้การอ้างอิงเปรียบเทียบเป็นอคติสำหรับสัญญาณ ส่งผลให้พูดคุยข้ามเล็ก ๆ น้อย ๆ ทั้งจากสัญญาณไปยังการอ้างอิงและจาก hysteresis ไปยังสัญญาณโดยเฉพาะอย่างยิ่งกับสัญญาณความถี่ต่ำ ได้รับกับค่าเหล่านั้นผลกระทบที่ควรมีขนาดเล็ก แต่สำหรับความบริสุทธิ์อคติที่แยกต่างหากกับสัญญาณจะดีกว่า

แก้ไข: รหัสของคุณอีกครั้ง

ในคำสั่ง else คุณเปลี่ยน interrupt edge ก่อนที่คุณจะตั้งค่า hysteresis

ไม่ว่าในกรณีใดคุณจะหยุดชั่วคราวและล้างการขัดจังหวะใด ๆ ที่ค้างอยู่ก่อนกลับมา (หมายเหตุการเปลี่ยนการลงทะเบียนการควบคุมการขัดจังหวะสามารถสร้างการขัดจังหวะด้วยตัวเอง)

ฉันไม่ทราบว่า Atmega จะขัดจังหวะการเข้ามาอีกครั้งหรือไม่นั่นคือถ้าหากขอบที่ตามมาจะขัดจังหวะตัวจัดการการทำงานที่ยังคงทำงานอยู่จากขอบก่อนหน้า ถ้าเป็นเช่นนั้นคุณต้องจัดการภาวะพร้อมกันอย่างเหมาะสม

ไม่แน่ใจว่าชิ้นส่วน PORTC นั้นมีไว้เพื่ออะไร แต่อาจต้องย้ายเข้าไปในส่วนที่ผ่านการรับรอง


ขอบคุณมากฉันจะลองข้อเสนอแนะของคุณในวันพรุ่งนี้และให้การปรับปรุง สำหรับ ISR ของฉันฉันมี if-else ถ้าคำสั่งสำหรับสถานการณ์ที่แน่นอนที่คุณอธิบายไว้ไม่รวมการรอ
Shibalicious

1
@Hypomania คุณควรแก้ไขคำถามของคุณและโพสต์รหัสตัวจัดการขัดจังหวะของคุณเพื่อให้ผู้คนสามารถดูว่าคุณกำลังยุ่งอยู่ที่ไหนสักแห่ง อาจเป็นเพียงเสียงรบกวนขอบเขตของคุณดูเหมือนจะมีสัญญาณรบกวนมากกว่า 50mV ยังคงมีสัญญาณรบกวนฉันคาดว่าจะแก้ไขตัวเองทั้งหมดไม่ว่าจะเป็นจังหวะพิเศษที่ช่วงการเปลี่ยนภาพ
Trevor_G

นั่นคือสิ่งที่ฉันคาดหวังเช่นกัน จะทำมันให้เร็วที่สุด
Shibalicious

1
@Hypomania ดูการแก้ไข
Trevor_G

1
@Hypomania เพราะคุณจะได้รับการขัดจังหวะระหว่างคำสั่งทั้งสอง ฉันยังแก้ไขการแก้ไข ...
Trevor_G

3

เอฟเฟกต์นี้คล้ายกับการตีกลับของผู้ติดต่อและสามารถลดลงได้ด้วยเทคนิคการเด้งกลับแบบเดียวกับที่คุณใช้สำหรับปุ่มกด

  • ตัดสินใจเกี่ยวกับเวลา debounce Td
  • เก็บการประทับเวลาของการขัดจังหวะขอบสุดท้ายในตัวแปร
  • ถ้าเวลาระหว่างการขัดจังหวะปัจจุบันและช่วงสุดท้ายน้อยกว่าTdให้ข้ามการขัดจังหวะปัจจุบัน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.