-         
Ergebnis 1 bis 7 von 7

Thema: ATMEGA 328P Frequenzzähler Jittert

  1. #1
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    2.147

    ATMEGA 328P Frequenzzähler Jittert

    Anzeige

    Ich bau zur Zeit einen Frequenzzähler mit einem ATMEGA 328P.
    Als Zähler wird der 16Bit Timer 1 Verwendet der vom Messsignal getaktet wird.
    Ca. alle 8ms wird der aktuelle Zählerstand ausgelesen und zu dem Ergebnis dazuaddiert.
    Nach 200ms wird dann das Ergebnis auf dem Display ausgegeben.
    Im Prinzip funktioniert das Ganze, allerdings schwanken die Messwerte ziemlich stark.
    Bei einer Eingangsfrequenz vom 1MHz werden Werte von 999810 bis über 1001300 angezeigt.
    Ich hätte einen Fehler von +- 1 Count * 5 also +-5Hz erwartet.
    Da der TCNT1 zu keiner Zeit manipuliert wird, sollten sich eventuelle Taktglitches ausgleichen.
    Es läuft auch keine 2te Interruptroutine die das Timing stören könnte.

    Zur Zeit betreibe Ich den Controller mit der internen 8MHz Quelle, aber das die so Jittert kann Ich mir nicht vorstellen.
    Messfehler durch die interne ungenaue Taktung sind natürlich klar, aber Jitter?!

    Mein parallel angeschlossener Frequenzzähler zeigt übrigens stabile 1000000Hz an, die nur an der letzten stelle +/- 1 sind.
    Also sollte mein DDS Signalgenerator i.O. sein.

    Anbei der zur Zeit benutzte Quelltext für Studio 7:
    Code:
    /*
     * RDS_Frequenzzaehler.c
     *
     * Created: 02.01.2021 13:55:13
     * Author : wKrug
     */ 
    
    #define F_CPU 8000000
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include "oled_ssd1306.c"
    
    volatile uint32_t measure , result ;
    volatile uint16_t oldcount = 0;
    //volatile uint8_t measpoint = 0;
    volatile uint8_t f_flag = 0;
    volatile uint8_t f_loopcount;
    
    // Timer2 output compare interrupt service routine
    // Runs any 8ms
    ISR (TIMER2_COMPA_vect)
    {
        uint16_t newcount;
        newcount = TCNT1;
        measure +=(newcount-oldcount);
        oldcount=newcount; 
        f_loopcount++;
        if(f_loopcount>24)    //was 24 New measure any 200ms at 16MHz
        {
            result=measure;
            measure = 0;
            f_loopcount=0;
            f_flag = 1;
        }
    }
    
    
    
    void show_display(void)
    {
        uint32_t temp32;
        char text [11];
        temp32 = result * 5;
        temp32 = (temp32 * 51)/100;                //******** Hier für 8MHz + Taktausgleich int Osz************
        ltoa (temp32,text,10);
        //strcat(text,"kHz");
        while (strlen (text) < 7)
        strcat (text," ");
        oled_font_size(3);
        oled_gotoxy(0,0);
        oled_write_str(text);
        //oled_write("%lu",(unsigned long) measure);
        /*oled_font_size(1);
        oled_gotoxy(0,1);
        oled_write("2 Z Text");
        oled_font_size(3);
        oled_gotoxy(0,3);
        oled_write("4Text");
        oled_font_size(0);
        _delay_ms(100);*/
        PORTB ^= (1<<PB5);//Port B5 toggelt
        
    }
    
    int main(void)
    {
        // Input/Output Ports initialization
        // Port B initialization
        // Function: Bit7=In Bit6=In Bit5=Out Bit4=In Bit3=In Bit2=In Bit1=Out Bit0=In
        DDRB=(0<<DDB7) | (0<<DDB6) | (5<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
        // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=0 Bit0=T
        PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);
    
        // Port C initialization
        // Function: Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
        DDRC=(0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
        // State: Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
        PORTC=(0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);
    
        // Port D initialization
        // Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
        DDRD=(0<<DDD7) | (1<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
        // State: Bit7=T Bit6=0 Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
        PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
    
        // Timer/Counter 0 initialization
        // Clock source: Disabled
        // Mode: Fast PWM top=0xFF
        // OC0A output: Non-Inverted PWM
        // OC0B output: Disconnected
        TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
        TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (0<<CS00);
        TCNT0=0x00;
        OCR0A=0x00;
        OCR0B=0x00;
    
    // Timer/Counter 1 initialization
    // Clock source: T1 pin Rising Edge
    // Mode: Normal top=0xFFFF
    // OC1A output: Disconnected
    // OC1B output: Disconnected
    // Noise Canceler: Off
    // Input Capture on Falling Edge
    // Timer1 Overflow Interrupt: Off
    // Input Capture Interrupt: Off
    // Compare A Match Interrupt: Off
    // Compare B Match Interrupt: Off
    TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
    TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (1<<CS12) | (1<<CS11) | (1<<CS10);
    TCNT1H=0x00;
    TCNT1L=0x00;
    ICR1H=0x00;
    ICR1L=0x00;
    OCR1AH=0x00;
    OCR1AL=0x00;
    OCR1BH=0x00;
    OCR1BL=0x00;
    
    // Timer/Counter 2 initialization
    // Clock source: System Clock
    // Clock value: 15,625 kHz
    // Mode: CTC top=OCR2A
    // OC2A output: Disconnected
    // OC2B output: Disconnected
    // Timer Period: 12,544 ms
    ASSR=(0<<EXCLK) | (0<<AS2);
    TCCR2A=(0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (0<<WGM20);
    TCCR2B=(0<<WGM22) | (1<<CS22) | (1<<CS21) | (1<<CS20);
    TCNT2=0x00;
    OCR2A=0x7C;
    OCR2B=0x00;
    
        // Timer/Counter 0 Interrupt(s) initialization
        TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);
    
        // Timer/Counter 1 Interrupt(s) initialization
        TIMSK1=(0<<ICIE1) | (0<<OCIE1B) | (0<<OCIE1A) | (0<<TOIE1);
    
        // Timer/Counter 2 Interrupt(s) initialization
        TIMSK2=(0<<OCIE2B) | (1<<OCIE2A) | (0<<TOIE2);
    
        // External Interrupt(s) initialization
        // INT0: Off
        // INT1: Off
        // Interrupt on any change on pins PCINT0-7: Off
        // Interrupt on any change on pins PCINT8-14: Off
        // Interrupt on any change on pins PCINT16-23: Off
        EICRA=(0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
        EIMSK=(0<<INT1) | (0<<INT0);
        PCICR=(0<<PCIE2) | (0<<PCIE1) | (0<<PCIE0);
    
        // USART initialization
        // USART disabled
        UCSR0B=(0<<RXCIE0) | (0<<TXCIE0) | (0<<UDRIE0) | (0<<RXEN0) | (0<<TXEN0) | (0<<UCSZ02) | (0<<RXB80) | (0<<TXB80);
    
        // Analog Comparator initialization
        // Analog Comparator: Off
        // The Analog Comparator's positive input is
        // connected to the AIN0 pin
        // The Analog Comparator's negative input is
        // connected to the AIN1 pin
        ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
        ADCSRB=(0<<ACME);
        // Digital input buffer on AIN0: On
        // Digital input buffer on AIN1: On
        DIDR1=(0<<AIN0D) | (0<<AIN1D);
    
        // ADC initialization
        // ADC disabled
        ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);
    
        // SPI initialization
        // SPI disabled
        SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);
    
        // TWI initialization
        // TWI disabled
        TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);
    
        // Globally enable interrupts
        
        /* Replace with your application code */
        DDRB |= (1<<PB5);
        oled_init();
        oled_gotoxy(0,0);
        sei();
        while (1) 
            {
                if(f_flag)
                {
                    f_flag=0;
                    show_display();
                }
            }
        
    }
    Wenn Ihr gute Ideen hab, woran es liegen könnte - Immer her damit!

  2. #2
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    25.12.2018
    Beiträge
    362
    Die Schwankung liegt um die 400 Prozessortakte - das erscheint mir auch nicht akzeptabel.
    Ich würde mal die gesamte Verarbeitung der Daten aus der ISR rausnehmen - die ISR sollte so kurz wie möglich sein. Dort gehört eigentlich nur die Abfrage des Timers rein. Newcount würde ich ebenfalls als globale Variable definieren. Evtl. ein Flag setzen, damit du weißt, dass ein neuer Wert gelesen wurde. Die Verarbeitung kommt dann in den Hauptteil. Allerdings müsste man mal genauer schauen, welche Variante ggf. schneller ist. Direkter Zugriff auf eine globale Variable oder erst irgendeine Zwischenspeicherung in einem Register zwecks Übergabe an eine lokale Variable... das geht dann doch sehr ins Detail.
    Du rechnest newcount-oldcount. Was passiert, wenn der Timer übergelaufen ist und newcount kleiner ist als oldcount? Du hast measure als uint32 und oldcount und newcount als uint16 definiert - kann das probleme geben? Ich denke eigentlich nicht.
    Die Lösung mit Loopcount finde ich auch nicht besonders elegant. Ich würde im Hauptteil eine Zeitgesteuerte Ausgabe basierend auf millis machen.
    Trotzdem, abgesehen davon, das der größte Teil deiner Programmlogik in der ISR steht, wo er nicht hin gehört, finde ich keinen Grund, warum das nicht halbwegs ordentlich laufen soll.
    Probier mal die 8 ms zu erhöhen. 10 x 20ms oder 4 * 50 ms - ob sich dann was ändert.
    Übringens - was soll das eigentlich bringen, wenn du 25 Messungen in 8 ms Abstand machst? Dadurch wird es doch nicht genauer... (im Gegenteil) stell den Timer auf 200 ms, messe den Wert und gib ihn aus...

  3. #3
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    2.147
    Ich würde mal die gesamte Verarbeitung der Daten aus der ISR rausnehmen - die ISR sollte so kurz wie möglich sein. Dort gehört eigentlich nur die Abfrage des Timers rein.
    Im Prinzip hast Du recht - Ich seh da aber keinen Weg hin.
    Im Prinzip frag ich eigentlich nur den Timer ab und zieh den letzten gemessenen Wert ab und summiere dann die Werte 25 mal.
    Erst bei 25 Durchlauf wird das Ergebnis übertragen.
    Die komplette Interupt Routine ohne Registerrettung dauert übrigens 44 Prozessortakte = 2,75µs ohne Aufruf und Registerrettung ( Die ohnehin immer stattfindet ).
    Die Berechnung in der Hauptroutine zu machen funktioniert leider nicht, weil allein die Displayausgabe 11ms dauert und somit mindestens eine Messung verloren ginge!
    Du rechnest newcount-oldcount. Was passiert, wenn der Timer übergelaufen ist und newcount kleiner ist als oldcount? Du hast measure als uint32 und oldcount und newcount als uint16 definiert - kann das probleme geben? Ich denke eigentlich nicht.
    Da alle Variablen als Unsigned definiert sind, sollte das kein Problem geben.
    Hab Ich auch des Öfteren schon so gemacht.

    Die Lösung mit Loopcount finde ich auch nicht besonders elegant. Ich würde im Hauptteil eine Zeitgesteuerte Ausgabe basierend auf millis machen.
    Trotzdem, abgesehen davon, das der größte Teil deiner Programmlogik in der ISR steht, wo er nicht hin gehört, finde ich keinen Grund, warum das nicht halbwegs ordentlich laufen soll.
    Die Lösung mit den kurzen 8ms hab ich so gewählt, weil nach 25 Durchläufen 200ms erreicht sind und somit das Ergebnis für die Ausgabe nur noch mit 5 multipliziert werden muß um die reale Frequenz zu erhalten.
    Zudem kann TCNT1 bis maximal 65635 Zählen. Damit sind theoretisch Frequenzen bis 8MHz messbar, was auch gewollt ist ( Ich brauch mindesten 1,2MHz ).

    Ich hab auch schon versucht 2 Timer hintereinander zu schalten und somit den kompletten Zählvorgang in Hardware zu erledigen.
    Da die Timer aber dann wieder auf 0 gesetzt werden müssen, was ja nicht absolut gleichzeitig geschehen kann gab hier noch mehr Probleme.

    Übringens - was soll das eigentlich bringen, wenn du 25 Messungen in 8 ms Abstand machst? Dadurch wird es doch nicht genauer... (im Gegenteil) stell den Timer auf 200 ms, messe den Wert und gib ihn aus...
    Genau 200ms zu generieren ist mit den gegebenen 16Mhz Quarz und einem 8 Bit Timer nicht möglich.
    Ausserdem würde man so die max mögliche messbare Frequenz auf 327KHz beschränken.

  4. #4
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    05.11.2007
    Beiträge
    1.076
    sorry, Problem missverstanden.
    Geändert von Siro (08.01.2021 um 06:43 Uhr)

  5. #5
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    2.147
    Das Rätsel scheint gelöst!
    Ich hab nun anstatt der internen 8MHz Quelle einen externen 16MHz Oszillator angeschlossen.
    Nun liegen die gemessenen Werte im erwarteten Bereich von +/- 5Hz ( = 1 Count bei 200ms Torzeit ).
    Die Messung läuft jetzt bis 7MHz stabil!

    Das hat meine alte Meinung bestätigt einen AVR Controller nur mit genauer Taktquelle zu verwenden, sonst gibts nur Ärger.

    Trotzdem Danke für die Tipps - Ich werde die soweit als möglich anwenden.

    Das Ganze soll übrigens ein Frequenzzähler für UKW und MW werden ( Mit Vorteiler natürlich ).
    Als zusätzliche Aufgabe soll auch noch RDS decodiert werden.
    Ich möchte damit meinen alten Radiotuner aufmotzen! - Mal gucken, was daraus wird...
    Geändert von wkrug (07.01.2021 um 14:51 Uhr)

  6. #6
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    07.04.2015
    Beiträge
    752
    Mal interessehalber (auch wenn der Jitter "nur" im Promillebereich liegt): Die Fehler des RC-Oszillators im DB beziehen sich ja nicht nur auf die Temperatur, sondern insbesondere auch auf die Betriebsspannung. Entsprechend die Frage: Hast Du mal mit dem Oszi gemessen, ob die Versorgung am Controller sauber ist?

  7. #7
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    2.147
    Es ist ne kleine Störspannung mit 60mVpp drauf.
    Das Ganze ist noch auf einem Steckboard mit nem Labornetzteil.

    Ich hab jetzt noch einen zusätzlichen Elko in die Versorgungsspannung gelegt.
    Die Störspannung ist jetzt auf 5mVpp runter.
    Trotzdem Jittert die Schaltung mit dem internen Oszillator immer noch ( ohne Änderung am Hauptprogramm );

Ähnliche Themen

  1. 128x32 SSD1306 O-LED Display am ATMEGA 328P - nun läuft es!
    Von wkrug im Forum C - Programmierung (GCC u.a.)
    Antworten: 0
    Letzter Beitrag: 03.01.2021, 09:20
  2. atmega 328P - Servomotor Steuerung
    Von ArduUser im Forum C - Programmierung (GCC u.a.)
    Antworten: 4
    Letzter Beitrag: 11.04.2016, 07:11
  3. Antworten: 0
    Letzter Beitrag: 15.01.2016, 17:16
  4. Immer diese Fuses ... Atmega 328p
    Von Sebas im Forum AVR Hardwarethemen
    Antworten: 6
    Letzter Beitrag: 16.05.2012, 18:18
  5. Frequenzzähler ohne Timer Interrupts mit Atmega
    Von Fabi1234 im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 13
    Letzter Beitrag: 31.12.2007, 08:31

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •