- Akku Tests und Balkonkraftwerk Speicher         
Ergebnis 1 bis 10 von 19

Thema: Timer1 ISRs Genauigkeit

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    Erfahrener Benutzer Begeisterter Techniker Avatar von PCMan
    Registriert seit
    05.08.2006
    Ort
    Munich
    Beiträge
    311
    Hallo nochmal,
    also falls es jemanden interessiert, ich habe das ganze für Meine Zwecke (Ausgabefrequenz habe ich mal auf zwischen 35 bis 2100 Hz festgelegt) folgendermaßen realisiert:

    Code:
    //toggled zwei Ausgänge
    ISR(TIMER2_COMP_vect) 
    {
       MOT_A_PORT ^= (1<<MOT_A_C_PIN);
       MOT_B_PORT ^= (1<<MOT_B_C_PIN);
    } 
    
    ...
    
    uint8_t autodetect(uint16_t *ps, uint8_t *ocr, uint16_t freq)
    {
        uint32_t i, ocra;
        uint32_t nenner;
        uint32_t prescale[] = {1,8,32,64,128,256,1024}; //mögliche Values für Timer2 lt Datenblatt...
    
        for (i = 0; i < 7; i++)
        {
            nenner = 2*prescale[i]*freq;
            ocra = round((16000000/nenner));
    
            //wenn Wert gefunden wurde, der in OCRA passt, schreiben und Funktion beenden
            //nach Tests war immer der erste ermittelte Wert der genaueste für die Ausgabefrequenz. Alle folgenden waren ungenauer...
            if (ocra < 255) 
            {
                *ps = prescale[i];
                *ocr = ocra;
                return 0;
            }
      }
    
     return 1;
    }
    
    ...
    // irgendwo im Programm
    
      uint8_t ocr_value;
      uint16_t prescaler;
      if (autodetect(&prescaler, &ocr_value,isr_frequency) == 1) return 1;
      else
      {
        if (prescaler == 1) TCCR2 |= (1<<CS20);
        else
        if (prescaler == 8) TCCR2 |= (1<<CS21);
        else
        if (prescaler == 32) TCCR2 |= (1<<CS21) | (1<<CS20);
        else
        if (prescaler == 64) TCCR2 |= (1<<CS22);
        else
        if (prescaler == 128) TCCR2 |= (1<<CS22) | (1<<CS20);
        else
        if (prescaler == 256) TCCR2 |= (1<<CS22) | (1<<CS21);
        else
        if (prescaler == 1024) TCCR2 |= (1<<CS22) | (1<<CS21) | (1<<CS20);
      }
      
      TCCR2 |= (1<<WGM21); //setze CTC Modus
      OCR2 = ocr_value;
      TIMSK |= (1<<OCIE2); //starte Timer
    Bin für Kritik natürlich offen.
    Viele Grüße,
    Simon

  2. #2
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Bin für Kritik natürlich offen.
    Ok, dann lege ich mal los.

    * Warum ist i ein uint32_t? uint8_t würde reichen.

    * Warum das uint32_t bei prescale[]? uint16_t würde reichen.
    (wenn du das änderst, muss aber auch die Zeile der nenner-Berechnung geändert werden)

    * Warum ist prescale[] eine automatic- und keine static-Variable?

    * Wozu soll die Funktion round() da gut sein? "16000000/nenner" ist eine Integer-Division und hat immer ein ganzzahliges Ergebnis. Da gibt es für round() rein gar nichts zu runden.

    All diese Punkte produzieren keinen Fehler, drücken aber auf die Performance. Was allerdings ein Fehler ist, ist dass du bei der OCR-Berechnung für den CTC-Modus ein "-1" vergisst.

    Noch was: Du scheinst 0 für "Erfolg" und 1 für "Fehler" zu verwenden. Das finde ich etwas ungeschickt, weil es genau entgegen der boolschen Logik ist (0 = False, 1 = True). Ich würde auch nicht den Prescaler-Wert speichern, sondern den Index, denn den kann man im restlichen Code dann direkt verwenden.

    So in etwa würde die Funktion bei mir aussehen:
    Code:
    uint8_t autodetect (uint8_t *ocr, uint8_t *pre, uint16_t freq) {
     
        static uint16_t prescale[] = {1,8,32,64,128,256,1024}; //mögliche  Values für Timer2 lt Datenblatt...
    
        for (uint8_t i = 0; i < 7; i++) {
            uint32_t nenner = 2UL * prescale[i] * freq;
            if (nenner >= (F_CPU/256)) {
                *ocr = ((F_CPU+(nenner/2)) / nenner) - 1;
                *pre = i + 1;
                return 1;
            }
        }
    
        return 0;
    }
    Und im restlichen Code dann:
    Code:
        uint8_t prescaler_index;
        uint8_t ocr_value;
        if (!autodetect(&ocr_value,&prescaler_index,isr_frequency))
            return 0;
        TCCR2 |= (prescaler_index<<CS20);
        OCR2 = ocr_value;
    Geändert von sternst (08.04.2011 um 01:04 Uhr)
    MfG
    Stefan

  3. #3
    Erfahrener Benutzer Begeisterter Techniker Avatar von PCMan
    Registriert seit
    05.08.2006
    Ort
    Munich
    Beiträge
    311
    Hi Sternst,
    danke für den Input. Dass alle Variable uint32_t sind war reine Faulheit. Ich habe vorher, wie du geschrieben hast, alle Variablen auf die nötigen Größen reduziert gehabt. Allerdings hat die Rechnung dann Müll produziert und der Fehler schien durch die stupide Erweiterung aller Variablen auf 32 gelöst. Ich weiß, wahrscheinlich hat das das Problem nicht gelöst, sondern nur verschoben.

    Gegenfragen:
    Wenn ich die Integerdivision durchführe bekomme ich durchaus keine ganzzahligen Ergebnisse. Das ganze wird dann durch die Zuweisung an einen int ganzzahling gemacht. Da aber immer die Nachkommas dabei abgeschnitten werden wollte ich das mit round genauer machen. Wieso sollte das nix bringen?

    in dem if-statement, welchen Vorzug bringt:
    if (nenner >= (F_CPU/256))
    gegenüber
    if (F_CPU/nenner) <= 256
    ?

    Kannst du mir bitte erklären, wie du auf diese Zeile kommst:
    Code:
    *ocr = ((F_CPU+(nenner/2)) / nenner) - 1;
    Ich dachte lt. Datenblatt ist OCR = (F_CPU / (2 * Prescaler * Frequenz)) - 1

    Wozu brauche ich bei dem prescaler index noch +1 und wozu das static?

    Danke,
    Simon

  4. #4
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Ich habe vorher, wie du geschrieben hast, alle Variablen auf die nötigen Größen reduziert gehabt. Allerdings hat die Rechnung dann Müll produziert
    Weil (wie ich schon schrieb) in dem Fall die Berechnungszeile etwas angepasst werden muss. Ohne Anpassung wird die Berechnung dann in uint16_t durchgeführt und läuft über. Man muss sie in uint32_t erzwingen, was in meinem Code das "UL" hinter der "2" erledigt.


    und der Fehler schien durch die stupide Erweiterung aller Variablen auf 32 gelöst. Ich weiß, wahrscheinlich hat das das Problem nicht gelöst, sondern nur verschoben.
    Ne, der Fehler ist damit tatsächlich behoben, nur halt unnötig aufwändig.


    Wenn ich die Integerdivision durchführe bekomme ich durchaus keine ganzzahligen Ergebnisse.
    Das ist schlicht falsch. Das Ergebnis einer Integer-Division ist wieder ein Integer (IMMER!), also ganzzahlig.


    Da aber immer die Nachkommas dabei abgeschnitten werden wollte ich das mit round genauer machen. Wieso sollte das nix bringen?
    Weil das Abschneiden nicht erst bei der Zuweisung passiert, sondern bereits bei der Division selber (siehe vorigen Quote). Das round() bekommt schon keine Nachkommastellen mehr zu sehen.


    in dem if-statement, welchen Vorzug bringt:
    if (nenner >= (F_CPU/256))
    gegenüber
    if (F_CPU/nenner) <= 256
    Bei "F_CPU/256" sind beide Operanden Konstanten, daher wird die Division bereits beim Compilieren durchgeführt und durch die resultierende Konstante ersetzt. Bei "F_CPU/nenner" muss die Division zur Laufzeit durchgeführt werden.


    Kannst du mir bitte erklären, wie du auf diese Zeile kommst:
    Code:
    *ocr  = ((F_CPU+(nenner/2)) / nenner) - 1;
    Ich dachte lt. Datenblatt ist OCR = (F_CPU / (2 * Prescaler * Frequenz)) - 1
    Richtig. Meine Zeile folgt genau aus der Formel, nur dass mit dem zusätzlichen Teil "+(nenner/2)" das Integer-Ergebnis gerundet wird (das was du mit round() versucht hast).


    Wozu brauche ich bei dem prescaler index noch +1
    Weil das Array mit "Index 0 = Prescaler 1" beginnt, während die Prescaler-Einstellung im Register mit "Index 0 = Timer gestoppt" beginnt.


    wozu das static
    Ohne das "static" wird das Array bei jedem Aufruf der Funktion neu angelegt und neu mit Werten befüllt. Da aber der Inhalt des Arrays in der Funktion ja gar nicht verändert wird, ist das unnötig. Mit dem "static" passiert das nur einmal am Anfang des Programms.
    Geändert von sternst (08.04.2011 um 11:09 Uhr)
    MfG
    Stefan

  5. #5
    Erfahrener Benutzer Begeisterter Techniker Avatar von PCMan
    Registriert seit
    05.08.2006
    Ort
    Munich
    Beiträge
    311
    Hi,

    besten Dank, sehr eingebend.
    Nur diese eine Zeile
    *ocr = ((F_CPU+(nenner/2)) / nenner) - 1;
    wurmt mich noch. In wie fern rundet man dort?

    Mit nenner = 2UL*Prescaler*Frequenz ergäbe sich in der Zeile:
    *ocr
    = ((F_CPU + (2*Prescaler*Frequenz/2)) / 2*Prescaler*Frequenz) -1
    = (F_CPU + Prescaler*Frequenz) / (2*Prescaler*Frequenz) -1
    = (F_CPU/2*Prescaler*Frequenz + 1/2) -1;

    oder hauen mich meine Mathe"kenntnisse" über's Ohr?

    Ciao,
    Simon

  6. #6
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Zitat Zitat von PCMan Beitrag anzeigen
    Nur diese eine Zeile
    *ocr = ((F_CPU+(nenner/2)) / nenner) - 1;
    wurmt mich noch. In wie fern rundet man dort?
    "Eigentlich" müsste die Zeile so aussehen:
    Code:
    *ocr  = (F_CPU / nenner) - 1;
    Um das Zwischenergebnis der Division zu runden, habe ich das "+(nenner/2)" hinzugefügt. Wie das funktioniert, kannst du leicht selber herausfinden. "A/B" im Vergleich zu "(A+(B/2))/B". Probiere es einfach mit ein paar Werten, dann siehst du sofort das Prinzip (wenn nicht, frag nochmal).

    PS: Deine mathematische Umformung mag richtig oder falsch sein (habe ich mir nicht näher angeschaut), ist aber irrelevant. Denn dort wird nicht berücksichtigt, dass beim "/" der Nachkommaanteil "weggeschmissen" wird. Du kannst eine Integer-Rechnung mit "/" darin nicht einfach nach mathematischen Regeln umformen.
    Einfaches Beispiel: "1/2" hat als Ergebnis 0. Du formst es mathematisch um in "1 - 1/2", und schon ist das Ergebnis 1.
    MfG
    Stefan

  7. #7
    Erfahrener Benutzer Begeisterter Techniker Avatar von PCMan
    Registriert seit
    05.08.2006
    Ort
    Munich
    Beiträge
    311
    Besten Dank, sternst.
    Wieder was gelernt.
    Ciao,
    Simon

Ähnliche Themen

  1. Positionierung Genauigkeit <1mm
    Von thomas0906 im Forum Sensoren / Sensorik
    Antworten: 17
    Letzter Beitrag: 08.06.2009, 20:39
  2. Timer 1 Genauigkeit
    Von asunn im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 1
    Letzter Beitrag: 27.06.2008, 22:51
  3. GPS-Kompass - Genauigkeit?
    Von Jon im Forum Elektronik
    Antworten: 13
    Letzter Beitrag: 10.04.2008, 23:13
  4. Genauigkeit der Int RC Oszialtoren
    Von The Man im Forum Assembler-Programmierung
    Antworten: 7
    Letzter Beitrag: 10.04.2008, 21:22
  5. Genauigkeit von Profilen...
    Von sonic im Forum Konstruktion/CAD/3D-Druck/Sketchup und Platinenlayout Eagle & Fritzing u.a.
    Antworten: 13
    Letzter Beitrag: 25.06.2004, 12:15

Stichworte

Berechtigungen

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

Solar Speicher und Akkus Tests