Forum: Mikrocontroller und Digitale Elektronik abweichung beim zeitmessungsprogramm


von Josua Sabo (Gast)


Angehängte Dateien:

Lesenswert?

Hallo.
Ich habe mehr schlecht alls recht in codevision mit dem atmel 162 ein 
zeitmessungsprogramm gebastelt. allerding ergab eine test messung über 2 
stunden eine abweichung von ca. 10 Minuten was einer abweichung von ca. 
7% entspricht.

im anhang ist der code. kann mir jemand sagen wo ich am ehesten die 
ursache für die abweichung finde?

danke im vorraus.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Schmeiss die riesige float-Berechnung und die langwierige Ausgabe aus 
der Interruptroutine raus. Die halten den Interrupt zu lange auf!

Merke dir nur die Anzahl der Overflows in einer volatile 
gekennzeichneten globalen Variable und mache die Berechnung und die 
Ausgabe im Nicht-Interruptteil.

von spess53 (Gast)


Lesenswert?

Hi

Was benutzt du als Oszillator?

MfG Spess

von Josua Sabo (Gast)


Lesenswert?

Einen 4Mhz Quarz. Wieso?

von Josua Sabo (Gast)


Lesenswert?

wieso muss die berechung+ausgabe aus der interrupt routine raus?
der interrupt wird doch bloss am ende der zeitmessung wenn quasi der 
auslöser betätigt wird gestoppt.

von oszi40 (Gast)


Lesenswert?

Kleinvieh macht manchmal auch Mist. Wenn z.B. ein Programm bei der 
Arbeit durch einen Interrupt gestört wird, fehlt ihn ein kleines Stück 
Rechenzeit.

von Karl H. (kbuchegg)


Lesenswert?

Lies dir mal diesen Tutorialabschnitt durch.
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Uhr

Die Assembler-Programme kannst du ignorieren, aber der Abschnitt über 
die Ganggenauigkeit könnte dich interessieren.

von Peter D. (peda)


Lesenswert?

Josua Sabo wrote:
> Ich habe mehr schlecht alls recht in codevision mit dem atmel 162 ein
> zeitmessungsprogramm gebastelt. allerding ergab eine test messung über 2
> stunden eine abweichung von ca. 10 Minuten was einer abweichung von ca.
> 7% entspricht.

Bei 4MHz kannst Du mit 32Bit nur bis 17,8min messen. Es können also 
garkeine 2h+-10min angezeigt worden sein.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Und werd um Himmels Willen die float Berechnungen los.
Wenn du Genauigkeit haben willst, dann ist float so ziemlich das letzte 
was du benutzen willst.

von Peter D. (peda)


Lesenswert?

Karl heinz Buchegger wrote:
> Wenn du Genauigkeit haben willst, dann ist float so ziemlich das letzte
> was du benutzen willst.

Immer diese Vorurteile.
Float ist auf 5..6 Digits genau, das reicht für viele Anwendungen völlig 
aus.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger wrote:
> Karl heinz Buchegger wrote:
>> Wenn du Genauigkeit haben willst, dann ist float so ziemlich das letzte
>> was du benutzen willst.
>
> Immer diese Vorurteile.
> Float ist auf 5..6 Digits genau, das reicht für viele Anwendungen völlig
> aus.


Hast du dir seine konstanten Zahlenwerte im Programm angesehen?
1
    const float time=0.000256F;          //Konstante entspricht der Zeit eines "Umschaltens" des Z�hlers
2
    const float over=65536.0F;           //Konstante die dem Wert eines �berlaufes entspricht
3
4
 ergebnis= (float) overfl*over*time+ (float) timer1*time);

mich wundert, dass da überhaupt noch irgendwas sinnvolles rauskommt.

von Peter D. (peda)


Lesenswert?

Karl heinz Buchegger wrote:
> Hast du dir seine konstanten Zahlenwerte im Programm angesehen?

Stimmt, der ist falsch (um 2,3%).

Für 1ms Schritte bei 4MHz Quarz nicht 0,000256 sondern:

1e3 / 4e6 = 0,00025


Die Berechnung der oberen 16 Bit sollte man auch besser als long machen, 
da ja noch eine Überlaufkorrektur notwendig ist:

Beitrag "AVR Timer mit 32 Bit"



Peter

von Josua Sabo (Gast)


Lesenswert?

danke für den link ins tutorial. habs beim ersten mal überlesen.
aber irgendwie kann ich die Berechungen nicht erkennen. an was kann das 
liegen?

mfg Josua
danke für die antworten

von Josua Sabo (Gast)


Lesenswert?

ich lies den timer auch mit einem vorteiler von 1024 arbeiten sprich er 
war mit 3,9 irgendetwas kHz getaktet.

von Josua Sabo (Gast)


Lesenswert?

weis jemand was mit einem subcounter im interrupt gemeint sein könnte?

von Karl H. (kbuchegg)


Lesenswert?

Der Timer ist der Hauptzähler.
In regelmässigen Abständen ruft er eine Interrupt Routine auf.

Innerhalb der Interrupt Routine installierst du einen zusätzlichen 
Software-Zähler
1
int Counter;
2
3
4
ISR( ... )
5
{
6
  Counter++;
7
8
  if( Counter == 10 ) {
9
    Counter = 0;
10
11
    // mach was
12
  }
13
}

Wenn deine ISR, sagen wir mal 1000 mal in der Sekunde aufgerufen wird, 
dann wird der Teil
   // mach was
tatsächlich nur 100 mal in der Sekunde ausgeführt.
Der Subcounter Counter hat den 'ISR-Takt' weiter heruntergeteilt.

von Josua Sabo (Gast)


Lesenswert?

danke jetzt kann ich mir was darunter vorstellen!

von Josua Sabo (Gast)


Lesenswert?

aber wie lange (wie viele takte) dauert dann die abarbeitung des 
Interrupts?

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:
> aber wie lange (wie viele takte) dauert dann die abarbeitung des
> Interrupts?

Kommt drauf an wieviel du reinschreibst :-)
Lanfgdauernde Operationen, wie LCD ausgabe oder Ausgabe über UART sollte 
man daher da drinnen nicht machen, sonst kann es sein, dass man in 
Zeitnot kommt und der nächste Interrupt schon signalisiert wird, noch 
ehe der vorhergehende fertig abgearbeitet wurde. Aber normalerweise hat 
man genügend Zeit um von irgendwo Daten zu holen, ein wenig Rechenarbeit 
zu machen und die Ergebnisse wieder in globalen Variablen abzulegen.

von josua sabo (Gast)


Lesenswert?

läuft während der abarbeitung des interrupts der timer nach dem lesen 
oder schreiben gleich weiter oder erst nach dem interrupt(Annahme es 
wird kein anderer interrupt während der abarbeitung aufgerufen) ?

und ich habe keine ahnung wie ich das in einem anderen programmteil 
ausrechnen und ausgeben soll? muss ich im interrupt einfach ein 
unterprogramm ausführen oder wie soll das funktionieren.

von Karl H. (kbuchegg)


Lesenswert?

Der Timer läuft weiter.
Ein Timer läuft immer, sobald er einen Vorteiler eingestellt bekommen 
hat.

In der ISr wertest du zb den Timerstand aus ... gibst ihn aber nicht auf 
einem LCD aus. DIe Ausgabe dauert verhältnismässig lange.
Stattdessen kannst du zb eine globale Variable auf 1 setzen.

Im Hauptprogramm, in der Hauptschleife überwachst du ständig diese 
globale Variable. Ist sie 1, dann signalisiert damit die ISR, dass es 
neue Werte gibt. Die Hauptschleife holt sich dann die Werte, bereitet 
sie unter Umständen für die Ausgabe vor und gibt sie am LCD aus. Die 
globale Variable wieder auf 0 setzen und das wars. Allerdings gibts da 
ein paar Fallen: Die Zugriffe auf die globalen Variablen müssen atomar 
sein (also mit cli und sei gekapselt werden) und die global Variable 
selbst muss volatile sein.

Mit dem Suchbegriff "Jobflag" müsstest du eigentlich hier im Forum 
fündig werden.

von Josua S. (teagn)


Lesenswert?

Also Codevision hat keine Interrupt bibliothek.
Deshalb kennt es die Befehle CLI(); und SEI(); nicht.
Was mache ich jetzt?

Wenn ich die Zeit nicht mit float berechne muss ich ein "künstliches" 
Komma einfügen oder?
Ich habe aber keinen Plan wie das funktionieren soll.
Kann ich es nicht doch mit float berechnen in der hauptschleife.

und mit globale variable auf eins setzten meinst du ein int oder den 
bool gibts ja nicht.

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:
> Also Codevision hat keine Interrupt bibliothek.
> Deshalb kennt es die Befehle CLI(); und SEI(); nicht.
> Was mache ich jetzt?

In deiner Doku nachsehen, wies bei Codevision heist.

>
> Wenn ich die Zeit nicht mit float berechne muss ich ein "künstliches"
> Komma einfügen oder?
> Ich habe aber keinen Plan wie das funktionieren soll.

Ah geh. Das hast du schon millionenfach gemacht.

Wenn du zu faul bist  €2.45 + @3.68 zu rechnen, weil du nicht mit 
Kommazahlen rumschmeissen willst, dann rechnest du eben in Cent:
245 + 368 = 613 Cent oder €6.13

(Hier hast du ein künstliches Komma nach der 2 Stelle eingeführt)

> Kann ich es nicht doch mit float berechnen in der hauptschleife.

Sicher kannst du.

>
> und mit globale variable auf eins setzten meinst du ein int oder den
> bool gibts ja nicht.

unsigned char
Vereinfacht den Zugriff.

von Josua S. (teagn)


Lesenswert?

Hallo.
Ich habe das Programm soweit abgeändert bzw. neu geschrieben wie 
besprochen. Jetzt  funktioniert garnichts mehr. Das if sowie das else 
werden nie abgearbeitet andernfalls müsste es doch eine Ausgabe auf dem 
LCD-Display geben(if ja bzw. if nein). Komisch ist auch, dass das 
Programm bis zu einfügen der "Komma Routine" wenigstens beim Interrupt 
etwas auf dem Display ausgab. Diese Ausgabe ist bei diesem Code 
allerdings nicht mehr vorhanden, ich habe die Ausgabe gelöscht, da sie 
ja keinen Sinn macht wenn sie nicht mehr funktioniert. Jetzt habe ich 
nur noch zwei stehende Balken auf dem 4 zeiligen Display an was liegt 
das?
Kann mir jemand sagen ob es grundlegende Fehler sind, die ich gemacht 
habe?
Unterprogramme habe ich zum ersten Mal verwendet.
Danke an alle Antwortenden.

von Josua S. (teagn)


Angehängte Dateien:

Lesenswert?

Ups Anhang vergessen!

von Karl H. (kbuchegg)


Lesenswert?

Du musst t als volatile Variable definieren.

t kann sich auf Wegen verändern, die der Compiler nicht einsehen kann. 
Daher muss man ihm mit 'volatile' helfen und ihn zwingen, bei einem 
Zugriff auf t nicht auf irgendwelche gecachten Werte zurückzugreifen, 
sondern die Variable tatsächlich aus dem Speicher zu holen.

Weiter hab ich nicht geschaut.

von Josua S. (teagn)


Lesenswert?

Jetzt springt das Programm wenigstens in die If Funktion.
Allerdings zeigt das Display garnichts an. Es flackert nur kurz wenn der 
Interrupt(INT1) ausgelöst wird.
Kann es sein, dass der µC zu schnell ist für das Display? Aber dann 
müsste ich doch irgendwo ein lcd_clear(); geschrieben haben damit es den 
Bildschirm löscht.

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:

> Kann es sein, dass der µC zu schnell ist für das Display? Aber dann
> müsste ich doch irgendwo ein lcd_clear(); geschrieben haben damit es den
> Bildschirm löscht.

Ähm. Ich zitiere mal aus deinem Programm
1
      if (t==1)                         //sollte sobald t=1 ist abgearbeitet werden
2
      {                                   
3
       
4
       berechung(endergeb);
5
       uitoa(endergeb,string); 
6
       ausgabe(string); 
7
       
8
       lcd_clear();                     //test
9
       lcd_putsf("if ja");
10
      }
11
      else 
12
      {
13
        lcd_clear();                    //test
14
        lcd_putsf("if nein");
15
      }

also ich sehe da 2 wunderbare lcd_clear

von Karl H. (kbuchegg)


Lesenswert?

* Spar dir die cli / sei in den Interrupt Funktionen. Die sind 
kontraproduktiv!

*
1
void uitoa(long endergeb, char* string)             //addaptiertes unterprogramm zum speichern der einzelnen zeichen in einen string
2
        {
3
        unsigned int i;                             // schleifenz�hler
4
        string[10]='\0';                       // String Terminator
5
            for(i=9; i>=0; i--) 
6
            {
7
            string[i]=(endergeb % 10) +'0';         // Modulo rechnen, dann den ASCII-Code von '0' addieren
8
            endergeb /= 10;
9
            }
10
        }

Die Abbruchbedingung in der for-Schleife: Da i ein unsigned int ist, ist 
i per Definition immer größer oder gleich 0.
Dir ist hoffentlich klar, dass du deinem Prozessor hier eine 
Fleissaufgabe aufbürdest. Du lässt ihn bei 16-Bit Arithmetik schwitzen, 
nur um von 9 auf 0 zu zählen (zumindest war das die Absicht. Tatsächlich 
ist das ja eine Endlosschleife, die quer durch den ganzen Speicher 
marschiert und überall eine 0 reinschreibt. Solange bis es 
lebenswichtige Teile berührt und der Prozessor abschmiert.)


* Wie gross ist denn in
1
char string[];

das char Array tatsächlich?
Du musst schon eine Größe vereinbaren, der Compiler kann sich die nicht 
aus den Fingern saugen. Ein Wunder das das überhaupt compiliert/linkt.

* Wo du allerdings mittels cli/sei den Zugriff auf die Variablen 
absichern solltest, ist in der Berechnung! Was denkst du wohl, was 
passiert, wenn dein Code gerade mitten in der Berechnung steckt und ihm 
ein Interrupt unter dem Hintern die Werte verändert.

von Josua S. (teagn)


Angehängte Dateien:

Lesenswert?

Habe es geändert. Trotzdem tut sich nichts.

von Karl H. (kbuchegg)


Lesenswert?

Ich würde mal in Etappen vorgehen anstatt alles in einem zu testen und 
nicht zu wissen wo ich mit der Fehlersuche anfangen soll

* erster Schritt ist die Sicherstellung, dass die Ausgabefunktionen 
richtig funktionieren

Dazu wird nur die Funktion ausgabe benötigt

1
int main()
2
{
3
  char test[11] = "000000000";
4
5
  ....
6
7
  while( 1 )
8
  {
9
     ausgabe( test, 0, 5, 2 );
10
  }
11
}

liefert das eine Ausgabe und wenn ja, ist sie korrekt?
Den String in test mal etwas variieren. Mit welcher Variation erwartest 
du welche Anzeige und kommt die auch?

Ist dieser Test erfolgreich, wird er erweitert. Die unsigned long zu 
String Wandlung kommt ins Spiel
1
int main()
2
{
3
  char test[11];
4
  unsigned long endergeb = 1000;
5
6
  ...
7
8
 while( 1 )
9
 {
10
    uitoa( endergeb, test); 
11
    ausgabe( test, 0, 5, 2 ); 
12
 }
13
}

Wird die Zahl korrekt angezeigt?
Da die Funktion ausgabe bereits getestet ist und funktioniert kann bei 
einer fehlerhaften Anzeige daher nur die Funktion uitoa Schuld sein. 
Also dort nach Fehlern Ausschau halten.

Jetzt kann man auch mal einen Massentest machen (auch zur eigenen 
Belustigung)
1
int main()
2
{
3
  char test[11];
4
  unsigned long endergeb = 0;
5
6
  ...
7
8
 while( 1 )
9
 {
10
    uitoa( endergeb, test); 
11
    ausgabe( test, 0, 5, 2 );
12
13
    endergeb++;
14
 }
15
}

damit kriegt man auch erst mal einen Eindruck von der Leistungsfähigkeit 
so eines µC. Wie schnell die Anzeige hochzählt (und das wird sehr 
schnell sein), hängt im wesentlichen von den LCD Funktionen ab. 
Wahrscheinlich werden ein paar der letzten Anzeigestellen nicht zu lesen 
sein und wie wild flackern. Aber ein paar Stellen davor sollte 
eigentlich vernünftig und ruhig hochgezählt werden.

Ist dieser Test erfolgreich, wird er erweitert.
Die Variablen tim und overfl kommen mit ins Spiel und damit auch 
gleichzeitig die Umrechnung dieser Werte ins Endergebnis

...

usw. usw.

Du darfst nicht den Fehler machen, zuviel auf einmal zu schreiben. Sonst 
stehst du mit einer Menge Code da, der fehlerhaft ist und du keine 
Ahnung hast, wo du mit der Fehlersuche anfangen sollst.
Du musst dir eine Entwicklungsstrategie überlegen, die dich von kleinen 
testbaren Einheiten zum Gesamtsystem führt, bei dem du jeden Schritt 
nach der Entwicklung sofort testen kannst und wo jeder Schritt auf dem 
Vorhergehenden aufbaut. Meistens bedeutet das, dass man mit den 
grundlegenden Ausgabefunktionen anfängt, denn die benötigt man um mal 
gesichert Ausgaben machen zu können, die auf jeden Fall funktionieren.
Dann wird Schritt für Schritt erweitert. Und da der jeweils 
vorhergehende Schritt zumindest soweit getestet wurde, dass er 
funktioniert, ist es sehr wahrscheinlich, dass ein Fehler der sich zeigt 
im zuletzt hinzugefügten Schritt steckt.


Und ja: Auch Profis gehen nach so einem Schema vor.


PS: Ich würd auch mal den ganzen Quatsch den dir der Code-Wizard 
generiert hat rausschmeissen. 60 bis 70% deines Codes haben nichts mit 
deiner Aufgabenstellung zu tun, sondern initialisieren nur 
µC-Bestandteile die du nicht benutzt. Allerdings blähen sie dein 
Programm soweit auf, dass man ständig rumscrollen muss.

von josua sabo (Gast)


Lesenswert?

Ich glaube der Fehler liegt bei der uitoa(), da ausgabe() einwandfrei 
funktioniert.
Ich verstehe nicht warum ich einen Bit-Terminator benötige.  Wenn ich 
schreibe string[10] wieviele "Ziffern" kann ich dann in diesem string 
speichern? (du schreibst string[11]="000000000" also nur 9 ziffern)

von josua sabo (Gast)


Lesenswert?

Für was brauche ich den String Terminator der setzt mir erste Zahl auf 0 
oder?

von josua sabo (Gast)


Lesenswert?

Bei der Frage zum String Terminator meinte ich natürlich anstatt Zahl 
Ziffer

von Karl H. (kbuchegg)


Lesenswert?

josua sabo wrote:
> Ich glaube der Fehler liegt bei der uitoa(), da ausgabe() einwandfrei
> funktioniert.
> Ich verstehe nicht warum ich einen Bit-Terminator benötige.

Was ist ein Bit-Terminator?

>  Wenn ich
> schreibe string[10] wieviele "Ziffern" kann ich dann in diesem string
> speichern? (du schreibst string[11]="000000000" also nur 9 ziffern)

klassische C-String Verarbeitung.

In einem Array der Länge 10 gibt es grundsätzlich 10 Arrayelemente. 
Durchnummeriert von 0 bis 9 (sind genau 10 Stück).
Jeder C-String hat als letztes Zeichen, welches das tatsächliche 
Stringende kennzeichnet, immer ein 0-Byte.
Das Array kann also viel größer sein, als der String der darin 
gespeichert ist. Durch das 0-Byte ist immer eindeutig erkennbar wo der 
String aufhört.

Ich definiere string als ein Array der Größe 11, weil in uitoa eine
Zuweisung vorkommt

     string[10] = '\0';

Damit ein Element mit dem Index 10 überhaupt existiert, muss das Array 
aber mindestens eine Länge von 11 haben.

http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F

von Karl H. (kbuchegg)


Lesenswert?

> Ich glaube der Fehler liegt bei der uitoa(), da ausgabe() einwandfrei
> funktioniert.

Nicht glauben. Sicher gehen.
Wie sieht dein Testprogramm aus?

Dieser Analyse kann ich so nicht folgen. Dein uitoa sieht eigentlich 
ganz gut aus. Solange das char Array mindestens eine Länge von 11 hat, 
kann da eigentlich nichts schief gehen.

von Josua S. (teagn)


Angehängte Dateien:

Lesenswert?

Hab den Code nochmal hochgeladen. Es sieht so aus, dass alles Super 
funktioniert wenn ich z.B endergeb=0xFFFFFFFF; setzte dann gibt das LCD
49,24 aus. -->uitoa und ausgabe funktionieren der Fehler liegt bei der 
berechung();. Wenn ich nämlich mein Unterprogramm berechung(); einfüge 
dann gibt mir das LCD   ,00000 aus. Komisch kann mir keinen Reim darauf 
machen.

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:
> Fehler liegt bei der
> berechung();. Wenn ich nämlich mein Unterprogramm berechung(); einfüge
> dann gibt mir das LCD   ,00000 aus. Komisch kann mir keinen Reim darauf
> machen.

OK. Dann musst du die mal die Funktion berechnung() mal genauer ansehen.

Hier ist sie
1
long berechung(long endergeb)                     //unterprogramm zur berechung des endergebnisses des z�hlers
2
{   
3
  
4
    #asm("cli")
5
    endergeb = ((overfl*0xFFFF)+time)*2;
6
    #asm("sei")
7
    
8
    return endergeb;
9
    
10
}

Was macht sie?
Sie übernimmt einen Zahlenwert in der lokalen Variablen endergeb. Dieser 
Zahlenwert ist aber völlig uninteressant, weil er sofort mit einer 
Berechnung überschrieben wird und das Ergebnis dieser Berechnung ist der 
Returnwert der Funktion.
Wichtig ist auch: Diese lokale Variable endergeb hat nichts mit der 
globalen Variablen gleichen Namens zu tun! Das sind 2 verschiedene 
Variablen!

Die Funktion liefert also ihr Ergebnis als Returnwert, so wie zb eine 
Funktion sin() ihr Ergebnis auch als Ergebnis zurück liefert. Daher kann 
man beim Sinus dann auch schreiben   x = sin(y); und das Rechenergebnis 
vom SInus wird beim Aufrufer in der Variablen x gespeichert.

Wie ist das bei dir? Wo ist der Aufruf von berechnung()? Hier:
1
      if (t==1)                         //sollte sobald t=1 ist abgearbeitet werden
2
      {     
3
      
4
       berechung(endergeb);

Du benutzt den Returnwert gar nicht! Die Funktion berechnung() rechnet 
was aus, was aber anscheinend gar keinen interessiert :-)
1
      if (t==1)                         //sollte sobald t=1 ist abgearbeitet werden
2
      {     
3
      
4
       endergeb = berechung(endergeb);

(Jetzt kannst du dich mal fragen, wozu du eigentlich die Variable 
endergeb in die Funktion hinein übergibst. Die Funktion braucht den Wert 
sowieso nicht. Das solltest du sowieso bereinigen, denn es ist immer 
unklug, wenn du mehrere Variablen, die nichts miteinander zu tun haben, 
mit dem gleichen Namen im Programm hast. Man kommt dann sehr leicht 
durcheinander. Und genau das ist dir passiert: Du hast eine lokale 
Variable endergeb in der Funktion und gleichzeitig eine globale Variable 
mitdemselben Namen. Du hast gedacht, wenn du an endergeb etwas zuweist, 
dann machst du das an die globale Variable, während du das 
Rechenergebnis in Wirklichkeit nur an die lokale Variable zugewiesen 
hast.
1
long berechung()                     //unterprogramm zur berechung des endergebnisses des z�hlers
2
{   
3
    long result;
4
5
    #asm("cli")
6
    result = ((overfl*0xFFFF)+time)*2;
7
    #asm("sei")
8
    
9
    return result;
10
}  
11
12
....
13
14
      if (t==1)                         //sollte sobald t=1 ist abgearbeitet werden
15
      {     
16
      
17
       endergeb = berechung();

von Josua S. (teagn)


Lesenswert?

Danke. Jetzt weiß ich wie das mit dem Returnwert funktioniert.

von Josua S. (teagn)


Lesenswert?

Da das Programm jetzt vom Anzeigen her macht was ich will dachte ich, 
dass ich fertig bin. Dem ist aber nicht so.
Der 16-Bit-Zähler läuft mit einer Frequenz von 500kHz das bedeutet alle 
2µSekunden wird der Zählerstand um eins erhöht.
Also rechne ich den Gesamtenzählerstand aus und Multipliziere ihn mit 2 
und füge ein Komma nach sechst Stellen von rechts ein.
Das Problem: Ich habe eine Abweichung von ca. 2 Sekunden.
Woran kann das liegen?

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:

> Das Problem: Ich habe eine Abweichung von ca. 2 Sekunden.

2 Sekunden kann viel sein ( 2 Sekunden in 10 Minuten ist viel), kann 
aber auch wenig sein ( 2 Sekunden in einem Jahr ist ein guter Wert)

Welcher Fall liegt bei dir vor?

von Josua S. (teagn)


Lesenswert?

Der Fehler beträgt ca. 7,9 bis 8,2%. D.h bei großen Messwerten 
entsprechend mehr bei kleinen Messwerten weniger Abweichung. Aber das 
zeigt, dass das Programm am Anfang garnicht so fehlerhaft war.

von Josua S. (teagn)


Lesenswert?

An wie läst sich diese ständige Abweichung erklären bzw. wo muss ich 
Anfangen zu suchen?

von Josua Sabo (Gast)


Angehängte Dateien:

Lesenswert?

Hallo.
Ich habe das Programm für 2 Läufer umgeschrieben.
Laut meiner Angabe rennt der 2 Läufer jedoch immer 10ms langsamer, als 
der erste Läufer egal welcher Interrupt zuerst betätigt wird.
Gibt es dafür eine logische Erklärung?

von Karl H. (kbuchegg)


Lesenswert?

Hab das jetzt nicht im Detail analysiert.
Aber: Du solltest die Timer im gestoppten Zustand halten.
Erst dann wenn das Startsignal tatsächlich kommt, werden beide Timer 
gestartet. Und sobald ein Läufer über die Ziellinie geht, wird sein 
zugehöriger Timer gestoppt! Und zwar so schnell wie möglich!

Dein Overflow Interrupt für Läufer 1 läuft nämlich sonst weiter, während 
Läufer 2 noch auf dem Weg ist und zählt munter weiter Overflows, die für 
Läufer 1 angerechnet werden.

von Josua Sabo (Gast)


Lesenswert?

Ok. Ein Timer wird mittels Vorteiler( Vorteiler auf 0) gestoppt.
Leider verwenden alle Timer den selben Vorteiler!

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:
> Ok. Ein Timer wird mittels Vorteiler( Vorteiler auf 0) gestoppt.
> Leider verwenden alle Timer den selben Vorteiler!

Möglich, dass das in deinem jetzigen Program so ist.
In deinem zuletzt geposteten Program ist es nicht so.
Timer1 ist für Läufer1 zuständig, Timer3 für Läufer3

von Josua Sabo (Gast)


Lesenswert?

Das hat auch nichts mit dem Programm zu tun, sondern mit dem 
Datenblatt(ATMEGA162). Im Datenblatt steht, dass alle Timer den selben 
Vorteiler haben.

von Karl H. (kbuchegg)


Lesenswert?

Josua Sabo wrote:
> Das hat auch nichts mit dem Programm zu tun, sondern mit dem
> Datenblatt(ATMEGA162). Im Datenblatt steht, dass alle Timer den selben
> Vorteiler haben.


Wo steht das?

Auszug aus der ersten Seite des Datenblatts

Peripheral Features
– Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes
– Two 16-bit Timer/Counters with Separate Prescalers, Compare Modes, and
Capture Modes


'Seperate Prescalers' klingt für mich nicht danach, dass sich alle Timer 
einen einzigen Vorteiler teilen müssen. Wär auch ein kleines bischen 
sinnlos, sowas zu machen.


> Timer/Counter3, Timer/Counter1, and Timer/Counter0 share the same
> prescaler module, but the Timer/Counters can have different prescaler
> settings.

Wenn du die Vorteiler selbst eingestellt hättest und dich nicht auf den 
Generierwizzard von IAR verlassen hättest, hättest du auch gemerkt, dass 
die Konfigurationsregister jedes Timers jeweils einen eigenen Satz von 
Vorteilereinstellbits haben. Das ist halt die Krux bei all diesen 
Wizzards: man weiß nicht mehr was man eigentlich tut.

von Josua Sabo (Gast)


Lesenswert?

Ok. Mit dem Starten und Stoppen der Timer funktioniert das Programm wie 
gewünscht. Jetzt würde ich aber gerne die Zeit während der Messung 
anzeigen. Wie mache ich das am besten?

von Karl H. (kbuchegg)


Lesenswert?

Wenn dein Programm noch einigermassen so aussieht wie eine Version von 
da weiter oben, dann wird die Zeit nur dann angezeigt, wenn t gleich 1 
geworden ist (weil ein Läufer ins Ziel gekommen ist). Mach die Abfrage 
weg und die Zeit wird bei jedem Schleifendurchlauf in main angezeigt.

von was-willst-du (Gast)


Lesenswert?

- Darstellung der Daten überlegen
- Anzeige anschließen
- Daten aufbereiten
- Daten auf die Anzeige schreiben

von Karl H. (kbuchegg)


Lesenswert?

Und hoffentlich hast du dir in der Zwischenzeit ein C-Buch organisiert. 
Das was du da mit Funktionen aufführst, das tut schon richtig weh.

von Josua Sabo (Gast)


Lesenswert?

Hallo.
Das Programm funktioniert jetzt so wie ich es wollte. Allerdings sollen 
die Interrupts des Endprogramms per Funk also mittels UART-Schnittstelle 
eintreffen.
Ich hätte mir das so vorgestellt:

ATMEGA162(bei den Lichtschranken):
-->bekommt einen Interrupt an dem Pin der Lichtschranke des 1 Läufers 
-->schickt eine Variable laeufer1 mit dem Wert 1 per UART an den 
ATMEGA162 beim Start.

ATMEGA162(am Start):
-die ankommende Variable löst den UART-Interrupt aus und stoppt 
jenachdem welche Variable auf 1 gesetzt wurde den entsprechenden Timer.

Wenn der andere Läufer seine Lichtschranke unterbricht soll natürlich 
der selbe Ablauf stattfinden.

Ist das so machbar?
Und müssten diese Variablen im µC beim Start global sein?
Mit welchen Zeitverzögerungen müsste man rechnen?

von josua sabo (Gast)


Lesenswert?

Ist das jetzt möglich oder nicht?
Gibt es vielleicht eine bessere Methode?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.