Hallo,
ich versuche gerade mit meinem STK500 und dem Display DEM 16217 SYH eine
einstellbare Uhr zu bauen. Für das Timing nutze ich die util/delay.h und
den 8-Bit Timer 0, für das Ansprechen des Displays nutze ich die
lcd-routines von
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
.
Das Programm zählt die Sekunden hoch und nach den Regeln die unsere
Uhren nun mal haben auch die Minuten und Stunden.
Zusätzlich lassen sich auch die Minuten und Stunden einstellen. Die
Tasten wurden dafür entprellt.
Der Code stammt aus dem Wiki hier:
http://www.mikrocontroller.net/articles/Entprellung .
Hier erstmal mein Sourcecode:
Ich weiß, man muss nicht unbedingt den ganzen Sourcecode posten sondern
nur den relevanten Teil. Aber ich war mir nicht ganz sicher, auf was ich
hier verzichten sollte. Ich hoffe es ist im wesentlichen gut kommentiert
oder selbsterklärend.
Nun zu meinen drei Problemen:
Timing
Soweit funktioniert das Programm auch wie erwartet. Nur gibt es einen
extremen Tineshift.
Ich habe das Programm mal über Nacht laufen lassen um einen möglichst
(unter den Umständen) guten Messwert zu bekommen.
Nach 10 Stunden ging meine Uhr eine 3/4 Stunde nach.
Das heißt umgerechnet, aller 13 Sekunden geht mein Projekt 1 Sekunde
nach.
Das finde ich schon gehörig viel. Es wird sogar schwierig das mit dem
DCF77 Signal konstant zu halten, was ich später noch vorhabe.
Natürlich fordern der Code in der Main und der Aufruf der LCD Routinen
ihren Tribut. Da gehen bestimmt ein paar Overflows verloren.
Jedoch wenn ich das Programm stark abspecke und lediglich jede Sekunde
eine LED an und ausgehen lasse, fällt es auch schon stark auf.
Als vergleich dient meine Wanduhr, deren mechanischer Sekundenzeiger gut
zu hören ist.
Mir ist durchaus bewusst, dass, wenn auch ein Quarz in ihr schlägt, ich
hier nicht mit einer besonders hohen Genauigkeit zu tun habe.
Die Mechanik des Zeigers, bis ich es höre und der Umstand dass ich kein
Messinstrument bin verhindert das natürlich.
Jedoch, wenn beide einigermaßen synchron laufen, kann es nicht sein,
dass ich bereits nach wenigen Sekunden einen deutlichen Shift wahrnehme.
Bei einer Differenz von 1/13 aber auch kein Wunder. Bereits nach etwa 8
Sekunden sind sie aus dem Takt und nach weiteren 8 Sekunden wieder im
Takt.
Das ist wie ich finde gehörig viel.
Was kann die Ursache der Ungenauigkeit sein?
Der Jumper von OSCSEL ist auf Pin 1-2. Das ist die Standardeinstellung.
So wie ich es im Handbuch las, liegt dort ein Takt von 3.686.400Hz an.
Dieser Takt wird per Software generiert und ist die Standardeinstellung.
Diese habe ich natürlich auch nicht verändert.
Mir ist natürlich klar, dass ein solcher Takt nie so genau ist, wie der
eines Quarzes. Aber kann die Ungenauigkeit denn wirklich so erheblich
sein?
So nebenbei, ich habe nirgends gefunden welcher Takt beim OSCSEL an Pin
3 an liegt. Da liegt einer an, erheblich langsamer als die 3,7MHz. Aber
er ist da. Nur was für einer das ist, konnte ich bisher noch nicht raus
finden.
Vielleicht liegt auch das Problem bei der Verwendung der util/delay.h.
Ich habe mir die delay.h und die delay_basic.h genau angeschaut, aber
keinen Hinweis darauf gefunden, ob und welchen Timer sie verwenden.
Volatile
Was ist die Eigenschaft des Specifiers volatile?
Ein Link würde reichen.
Der Wikipedia-Artikel http://de.wikipedia.org/wiki/Volatile_(Informatik)
half mir nicht wirklich weiter.
Ich verstehe vor allem nicht den Unterschied zu static und warum ich
auf diese globalen Variablen von der Main aus nicht darauf zu greifen
kann.
Weshalb ich diese Variablen als volatile spezifizierte.
Ich habe mir sagen lassen, bei einem PC landen diese Variablen im Level1
Cache und sind somit schneller zu verarbeiten. Was einleuchtet. So lange
der Level1 Cache nicht voll ist. Aber ein Level1 Cache bei einem
Kontroller? (ATmega8515)
Wenn also jemand einen Link weiß, wo es für Idioten beschrieben ist,
wäre ich sehr dankbar.
Switch Register
Warum scheitert der Versuch den Inhalt einer Registers als
Switch-Bedingung zu nutzen?
1
switch(~PINA)
2
{
3
case1:Minutes++;break;
4
case2:Hours++;break;
5
default:/* Show content of PINA on Display */break;
6
}
oder auch:
1
switch(PINA)
2
{
3
case254:Minutes++;break;
4
case253:Hours++;break;
5
default:/* Show content of PINA on Display */break;
6
}
????
Es landet immer im Default. Obwohl der Inhalt des Registers den Case
Bedingungen entspricht.
Weshalb ich die Variable Buttons zusätzlich verwenden muss.
Vielen Dank erstmal schon fürs Durchlesen.
Das ist ja schon eine ganze Menge.
Gruß
BrEin
_____________________________________________________________________
P.S.
Ich lese des öfteren hier im Forum wie einige sich nicht an hiesige
Gepflogenheiten halten und deshalb gemaßregelt werden.
Ich habe versucht mich an diese zu halten.
Sollte ich mich dennoch vertan haben, so bitte ich um Rücksicht und
einen freundlichen Hinweis.
Sollte es durch diesen riesigen Artikel so wirken, als würde ich alle
meine Probleme auf euch Abladen, möchte ich mich dafür entschuldigen.
Erste Massnahme sollte sein, die LCD-Ausgabe aus der Interrupt-Routine
ins Hauotprogramm zu verlagern.
Zweite Massnahme: wenn man schon dabie ist, sollte man auch das
Berechnen der Uhrzeit ins Hauptprogramm verlagern, so dass in der
interruptroutine nur noch ein Merker gesetzt wird, dass eine Sekunde
vergangen ist.
clocker schrieb:> Erste Massnahme sollte sein, die LCD-Ausgabe aus der Interrupt-Routine> ins Hauotprogramm zu verlagern.>> Zweite Massnahme: wenn man schon dabie ist, sollte man auch das> Berechnen der Uhrzeit ins Hauptprogramm verlagern, so dass in der> interruptroutine nur noch ein Merker gesetzt wird, dass eine Sekunde> vergangen ist.
Vielen Dank für deine schnelle Antwort.
Aber ich glaube, ich habe ein generelles Problem.
Denn selbst bei einem einfachen/kurzen Programm entsteht der Shift.
Beispiel:
1
/*
2
* LightRunner_AVR.c
3
*
4
* Created on: Oct 13, 2010
5
* Author: fabian
6
*/
7
8
#include<avr/io.h>
9
#include<avr/interrupt.h>
10
11
volatilecharc=0;
12
13
SIGNAL(TIMER0_OVF_vect)
14
{
15
TCNT0=30;
16
if(c++==16)
17
{
18
c=0;
19
if(PORTB==0x7F){PORTB=0xFE;return;}
20
PORTB=~(~PORTB<<1)|0x01;
21
}
22
}
23
24
intmain(void)
25
{
26
DDRB=0xFF;
27
PORTB=0xFE;
28
TCCR0=0x05;// 0000|0101
29
TCNT0=30;
30
TIMSK|=0x02;// 0000|0010
31
sei();
32
while(1)
33
{
34
}
35
}
Oder ist auch hier die Berechnung in der SIGNAL das Problem? Soll auch
hier ich es auslagern?
Danke
Übertreib mal nicht.
Die LCD Aufrufe aus dem Interrup raus ist gut und richtig.
Den Rest kannst du (und sollst du sogar) drinnen lassen, damit die Uhr
für sich alleine vollständig tickt.
Auch wenn die Empfehlung lautet: ISR so kurz wie möglich, muss man auch
nicht übertreiben. In einer ISR soll nur das notwendigste passieren,
aber man muss auch nicht auf Biegen und Brechen die ISR auf reines Flag
setzen reduzieren.
> Jedoch, wenn beide einigermaßen synchron laufen, kann es nicht sein,> dass ich bereits nach wenigen Sekunden einen deutlichen Shift> wahrnehme. Bei einer Differenz von 1/13 aber auch kein Wunder.> Bereits nach etwa 8 Sekunden sind sie aus dem Takt und nach weiteren> 8 Sekunden wieder im Takt.>> Das ist wie ich finde gehörig viel.>> Was kann die Ursache der Ungenauigkeit sein?
Was erwartest du
3686400 / 1024 = 3600
3600 / 236 = 15.25zerquetschte
Interrupts pro Sekunde. Du hast keinen ganzzahligen Teiler, so dass du
deine Taktfrequenz auf eine ganzzahlige Anzahl Interrupt Aufrufe pro
Sekunde herunterteilen würdest.
Wenn du daher erst alle 16 Interrupts 1 Sekunde weiterschaltest, dann
geht deine Uhr zu langsam. Und zwar um 0.75/16 Sekunden pro Sekunde. Und
das ist schon ziemlich viel und summiert sich sehr schnell.
Fabian Hoemcke schrieb:> Ich habe versucht mich an diese zu halten.> Sollte ich mich dennoch vertan haben, so bitte ich um Rücksicht und> einen freundlichen Hinweis.
Dein Posting ist fein.
Es sollte mehr Postings in diesem Stil geben.
Einzig: Wenn du uns noch die Hardware dazu nennst, wäre alles
vollständig, denn das hier
> Der Jumper von OSCSEL ist auf Pin 1-2. Das ist die> Standardeinstellung. So wie ich es im Handbuch las, liegt dort ein> Takt von 3.686.400Hz an. Dieser Takt wird per Software generiert und> ist die Standardeinstellung.
lässt sich nur verstehen, wenn man die konkrete Schaltung kennt.
Zitat:
[...]
In C und C++ wird durch diesen Typqualifikator spezifiziert, dass sich
der Wert der Variablen außerhalb des aktuellen Programmkontextes ändern
kann, beispielsweise durch externe Hardware. Bei der Generierung des
Maschinen-Codes aus einem in C oder C++ geschriebenen Programm
verhindert die Kennzeichnung einer Variablen als volatile eine
übermäßige Optimierung, so dass das Programm immer auf den tatsächlich
in der Hardware vorhandenen Wert zugreift
[...]
Quelle: Der vom Threadsteller gelinkte Artikel wikipedia.
Ich verstehe noch nicht ganz was man an diesem Artikel missverstehen
kann.
Mal ganz salop gesagt: Nehmen wir du veränderst in der Software den Wert
dieser Variable nie dann könnte der Compiler "denken": 'Ha um
Speicherplatz zu sparen kann ich die ja umwandeln oder ihr nur weniger
Speicherplatz zugestehen. Die wird ja sowieso nicht verändert'
Sagst du den Compiler, dass sie volatile ist so weiß der Compiler - oha
auch externe Hardware (PORT, ADC, etc.) können die Variable "ohne mein
Wissen" verändern. Darum nicht optimieren.
Knut schrieb:> Nur leider verschenkst du eine Sekunde wenn du Sekunden und Minuten bei> 59 schon wieder auf 0 setzt ;-)
:-)
Seine Schreibweise ist nicht sehr glücklich. Tatsächlich stimmt die
Logik durch den Postincrement schon.
Karl heinz Buchegger schrieb:>> Jedoch, wenn beide einigermaßen synchron laufen, kann es nicht sein,>> dass ich bereits nach wenigen Sekunden einen deutlichen Shift>> wahrnehme. Bei einer Differenz von 1/13 aber auch kein Wunder.>> Bereits nach etwa 8 Sekunden sind sie aus dem Takt und nach weiteren>> 8 Sekunden wieder im Takt.>>>> Das ist wie ich finde gehörig viel.>>>> Was kann die Ursache der Ungenauigkeit sein?>>> Was erwartest du>> 3686400 / 1024 = 3600>> 3600 / 236 = 15.zerquetschte>> Interrupts pro Sekunde. Du hast keinen ganzzahligen Teiler, so dass du> deine Taktfrequenz auf eine ganzzahlige Anzahl Interrupt Aufrufe pro> Sekunde herunterteilen würdest.
Stimmt! Danke! TCNT0 muss den Wert 31 erhalten!
Ich probiere es gleich mal aus, ob das der Teufel im Detail war!
Karl heinz Buchegger schrieb:> Fabian Hoemcke schrieb:>>> Ich habe versucht mich an diese zu halten.>> Sollte ich mich dennoch vertan haben, so bitte ich um Rücksicht und>> einen freundlichen Hinweis.>> Dein Posting ist fein.> Es sollte mehr Postings in diesem Stil geben.> Einzig: Wenn du uns noch die Hardware dazu nennst, wäre alles> vollständig, denn das hier>>> Der Jumper von OSCSEL ist auf Pin 1-2. Das ist die>> Standardeinstellung. So wie ich es im Handbuch las, liegt dort ein>> Takt von 3.686.400Hz an. Dieser Takt wird per Software generiert und>> ist die Standardeinstellung.>> lässt sich nur verstehen, wenn man die konkrete Schaltung kennt.
STK500 ist das bekannte Evauationbord von Atmel. Bestückt mit einem
ATmega8515.
Handbuch: http://www.atmel.com/atmel/acrobat/doc1925.pdfLehrmann Michael schrieb:> Zitat:> [...]> In C und C++ wird durch diesen Typqualifikator spezifiziert, dass sich> der Wert der Variablen außerhalb des aktuellen Programmkontextes ändern> kann, beispielsweise durch externe Hardware. Bei der Generierung des> Maschinen-Codes aus einem in C oder C++ geschriebenen Programm> verhindert die Kennzeichnung einer Variablen als volatile eine> übermäßige Optimierung, so dass das Programm immer auf den tatsächlich> in der Hardware vorhandenen Wert zugreift> [...]>> Quelle: Der vom Threadsteller gelinkte Artikel wikipedia.>>> Ich verstehe noch nicht ganz was man an diesem Artikel missverstehen> kann.> Mal ganz salop gesagt: Nehmen wir du veränderst in der Software den Wert> dieser Variable nie dann könnte der Compiler "denken": 'Ha um> Speicherplatz zu sparen kann ich die ja umwandeln oder ihr nur weniger> Speicherplatz zugestehen. Die wird ja sowieso nicht verändert'> Sagst du den Compiler, dass sie volatile ist so weiß der Compiler - oha> auch externe Hardware (PORT, ADC, etc.) können die Variable "ohne mein> Wissen" verändern. Darum nicht optimieren.
Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten
Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.
Danke.
Bin jetzt wieder ein Stück weiter.
Karl heinz Buchegger schrieb:> lässt sich nur verstehen, wenn man die konkrete Schaltung kennt.
Und selbst das nehm ich zurück. Ich hab nicht gut genug gelesen.
Fabian Hoemcke schrieb:>> Was erwartest du>>>> 3686400 / 1024 = 3600>>>> 3600 / 236 = 15.zerquetschte>>>> Interrupts pro Sekunde. Du hast keinen ganzzahligen Teiler, so dass du>> deine Taktfrequenz auf eine ganzzahlige Anzahl Interrupt Aufrufe pro>> Sekunde herunterteilen würdest.>> Stimmt! Danke! TCNT0 muss den Wert 31 erhalten!> Ich probiere es gleich mal aus, ob das der Teufel im Detail war!
Nimm dir einen Taschenrechner und rechne es durch. Auch mit 235 geht es
sich nicht aus.
> Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten> Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.
Weil Wikipedia auf die speziellen Belange von µC (wie die meiste
C-Literatur) kaum Rücksicht nimmt.
In deiner Hauptschleife steht zb
hour = 0
while( hour == 0 )
mach irgendwas in dem hour nicht vorkommt
Für den Compiler, der sich nur diese Schleife ansiehst, sieht es so aus,
als ob hour keine Chance hätte, innerhalb dieser Schleife seinen Wert zu
verändern. Dass hour in der ISR verändert wird, erkennt der Compiler
nicht, da es keinen direkten Aufruf der Funktion gibt. ISR werden von
der Hardware aufgerufen, ohne dass es dazu im Programm einen expliziten
Aufruf gäbe.
Der Compiler geht daher von der Annahme aus, dass innerhalb dieser
Schleife hour immer den Wert 0 haben wird und optimiert in dem Fall zb
die Überprüfung ob hour gleich 0 ist einfach weg. Denn: Wenn hour
sowieso nur 0 sein kann, dann muss man das auch nicht ständig abtesten.
Durch das volatile sagst du dem Compiler:
Diese Variable kann sich auch auf Wegen ändern, die du nicht erkennen
kannst, wenn du Codeanalyse betreibst. Du musst jeden Zugriff auf diese
Variable, jede Überprüfung, jedes Auslesen auch tatsächlich so machen,
wie ich das hingeschrieben habe. Deine Datenflussanalyse wird nämlich
sonst zu falschen Ergebnissen kommen.
Hi
>ASo wie ich es im Handbuch las, liegt dort ein Takt von 3.686.400Hz an.
Darauf würde ich mich nicht unbedingt verlassen. Die Frequenz wird durch
den Controller auf dem STK500 aus einer Quarzfrequenz von 7,37MHz
mittels Timer erzeugt. Und da können nur 3,685MHz herauskommen.
MfG Spess
Fabian Hoemcke schrieb:> Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten> Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.
Das hat auch nichts damit zu tun.
Und die Sache mit dem Level 1 Cache ... vergiss sie am besten auch
gleich wieder.
Bei volatile geht es einzig und alleine darum, dem Compiler mitzuteilen:
Deine Datenflussanalyse wird bei dieser Variablen zu falschen Schlüssen
kommen, also spar dir jegliche Optimierung auf dieser Variablen.
Alles andere sind Folgeerscheinungen davon und bei manchen Compilern so,
bei anderen wieder nicht.
volatile hat damit zu tun, eine Variable von der Optimierung des
Compilers auszunehmen, da der Compiler die komplettenm Zusammenhänge
nicht übersehen kann.
Karl heinz Buchegger schrieb:> In deiner Hauptschleife steht zb>> hour = 0>> while( hour == 0 )> mach irgendwas in dem hour nicht vorkommt>> Für den Compiler, der sich nur diese Schleife ansiehst, sieht es so aus,> als ob hour keine Chance hätte, innerhalb dieser Schleife seinen Wert zu> verändern. Dass hour in der ISR verändert wird, erkennt der Compiler> nicht, da es keinen direkten Aufruf der Funktion gibt. ISR werden von> der Hardware aufgerufen, ohne dass es dazu im Programm einen expliziten> Aufruf gäbe.
In dieser while-Schleife wird aber Hour verändert.
Aber danke bisher. Jetzt erschließt sich mir so einiges.
spess53 schrieb:> Hi>>>ASo wie ich es im Handbuch las, liegt dort ein Takt von 3.686.400Hz an.>> Darauf würde ich mich nicht unbedingt verlassen. Die Frequenz wird durch> den Controller auf dem STK500 aus einer Quarzfrequenz von 7,37MHz> mittels Timer erzeugt. Und da können nur 3,685MHz herauskommen.>> MfG Spess
Entschuldige meine Haarspalterei,
aber liegen da nun 3,685MHz oder 3,6864MHZ an???
Gruß und Danke
Karl heinz Buchegger schrieb:>>>> Stimmt! Danke! TCNT0 muss den Wert 31 erhalten!>> Ich probiere es gleich mal aus, ob das der Teufel im Detail war!>> Nimm dir einen Taschenrechner und rechne es durch. Auch mit 235 geht es> sich nicht aus.
ähmmm
3.686.400 1024 (256 - 31 = 225) = 16
Wo hab ich einen Denkfehler?
Fabian Hoemcke schrieb:> Karl heinz Buchegger schrieb:>> In deiner Hauptschleife steht zb>>>> hour = 0>>>> while( hour == 0 )>> mach irgendwas in dem hour nicht vorkommt>>>> Für den Compiler, der sich nur diese Schleife ansiehst, sieht es so aus,>> als ob hour keine Chance hätte, innerhalb dieser Schleife seinen Wert zu>> verändern. Dass hour in der ISR verändert wird, erkennt der Compiler>> nicht, da es keinen direkten Aufruf der Funktion gibt. ISR werden von>> der Hardware aufgerufen, ohne dass es dazu im Programm einen expliziten>> Aufruf gäbe.>> In dieser while-Schleife wird aber Hour verändert.
Nicht in dem Beispiel das ich konstruiert habe :-)
> aber liegen da nun 3,685MHz oder 3,6864MHZ an???
Ist ziemlich egal.
Denn auch ein 16Mhz Quarz hat nicht exakt 16000000 Hz :-)
Du kannst es drehen wie du willst, du musst die Uhr sowieso manuell
abgleichen. Und daher ist auch der Ansatz mit dem Timer Overflow im
Grunde für eine Uhr nicht zu gebrauchen. Du hast zuwenig Möglichkeiten
aus der tatsächlich vorliegenden Frequenz auf 1 Hz genau deinen
Uhrentakt abzuleiten.
CTC-Modus ist das Stichwort
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Uhr
Karl heinz Buchegger schrieb:> Fabian Hoemcke schrieb:>>> Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten>> Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.>> Das hat auch nichts damit zu tun.> Und die Sache mit dem Level 1 Cache ... vergiss sie am besten auch> gleich wieder.>> Bei volatile geht es einzig und alleine darum, dem Compiler mitzuteilen:> Deine Datenflussanalyse wird bei dieser Variablen zu falschen Schlüssen> kommen, also spar dir jegliche Optimierung auf dieser Variablen.>> Alles andere sind Folgeerscheinungen davon und bei manchen Compilern so,> bei anderen wieder nicht.>> volatile hat damit zu tun, eine Variable von der Optimierung des> Compilers auszunehmen, da der Compiler die komplettenm Zusammenhänge> nicht übersehen kann.
Ahhh, danke!
Und bei mir macht der Compiler hat Optimierungen, denen ich zwar nicht
folgen kann, aber die mir die Suppe gehörig versalzen.
Alles klar!
Man sollte sich halt nicht auf den Buschfunk verlassen.
Fabian Hoemcke schrieb:> ähmmm> 3.686.400 1024 (256 - 31 = 225) = 16>> Wo hab ich einen Denkfehler?
:-)
Nicht du.
Ich.
Kopfrechnen. 256 - 31 ergibt 225. Zwanzig, nicht Dreissig. Ich hab mit
235 gerechnet :-)
Spielt aber nicht wirklich die grosse Rolle. Deine Uhr wird zwar genauer
gehen, aber nicht genau genug :-) Denn der Ausgangs-Takt ist nicht exakt
3686400 sondern ein paar Zig-Herz daneben :-)
Klaus schrieb:> Hallo,>> woher kommen eigentlich diese 236 bzw 235 her ?
Ich muss nochmal zurück in die Grundschule Kopfrechnen im Zahlenraum
über Hundert üben.
Vielen Dank für eure kompetenten, schnellen und sachlichen Antworten.
Leider blieben aber immer noch drei Fragen offen.
1.) Woher bekommt die util/delay library die Zeit?
2.) Wieso funktionieren Register als Switch-Bedingungen nicht?
3.) Mehr als abschließende Antwort.
Dieser enorme Shift ist also normal?
Unabhängig ob Quarz oder Software als Taktquelle?
Gruß und Danke nochmal
BrEin
Fabian Hoemcke schrieb:> 1.) Woher bekommt die util/delay library die Zeit?
util/delay braucht keine Zeit.
util/delay will nur wissen wie schnell der µC getaktet wird. Es rechnet
sich dann aus, wie oft der µC Däumchen drehen muss, damit das in Summe
eine gewisse Zeit dauert
> 2.) Wieso funktionieren Register als Switch-Bedingungen nicht?
Ich bin mir ziemlich sicher, dass sie funktionieren
> 3.) Mehr als abschließende Antwort.> Dieser enorme Shift ist also normal?> Unabhängig ob Quarz oder Software als Taktquelle?
Er ist genauso normal, wie eine Pendeluhr falsch geht, deren Pendel
nicht auf die richtige Länge gebracht wurde. Ist das erst einmal
erledigt, dann geht auch eine Pendeluhr genau.
Genauso bei dir: Sind alle Programmfehler draussen, dann bleibt noch die
Abweichung, die dadurch entsteht das ein Quarz mit XYZMhz
Nominalfrequenz eben nicht mit XYZMhz schwingt, sondern im Rahmen seiner
Genauigkeit ein wenig schneller oder langsamer. Wobei ein Quarz seine
Freuqenz dann auch noch mit der Temperatur ändert. Aber das tut das
Pendel einer Pendeluhr auch (seine Länge mit der Temperatur ändern),
nimmt man einen mittleren Wert dann gleicht sich das über den Tag
gesehen in etwa aus.
@ Karl heinz Buchegger
Danke für Frage eins, ich hatte deshalb gefragt, da ich nicht einen
Timer verwenden wollte, der schon implizit durch diese Library genutzt
wird.
Danke für Frage drei, ich werde dann man beobachten, wie groß die
Differenz morgen Früh ist.
Nun zur Frage zwei. Ich hatte ganz oben beschrieben dass das (zu mindest
so wie ich es nutzte) nicht funktioniert. Wenn du jetzt aber sagst, dass
es funktioniert, hättest du eine Idee wo mein Fehler sein könnte?
Hi
>Aber das tut das Pendel einer Pendeluhr auch (seine Länge mit der >Temperatur
ändern), nimmt man einen mittleren Wert dann gleicht sich das
>über den Tag gesehen in etwa aus.
Im dem Fall hinkt der Quarz der Pendeluhr hinterher. Mein Regulator aus
den 30-er Jahren hat ein temperaturkompensiertes Pendel.
MfG Spess
Fabian Hoemcke schrieb:> Nun zur Frage zwei. Ich hatte ganz oben beschrieben dass das (zu mindest> so wie ich es nutzte) nicht funktioniert. Wenn du jetzt aber sagst, dass> es funktioniert, hättest du eine Idee wo mein Fehler sein könnte?
Kaprizier dich nicht darauf.
Das was du als Fehler angesehen hast, kann auch ein Nebeneffekt von
einem ganz anderen Fehler gewesen sein.
Kaprizier...
Das habe ich ja noch nie gehört! :D
Musste ich erstmal nachschlagen.
Aber gut, ich werde mich nicht darauf versteifen.
Wenn es mit einer Variable funktioniert, auch gut!
So, ich habe die vorgeschlagenen Verbesserungen vorgenommen und die Uhr
wieder eine Nacht durchlaufen lassen.
Die Differenz diesmal: 40Min.
5 Minuten Verbesserung immerhin.
Aber mir ist das immer noch viel zu ungenau.
In dem genannten Tutorial
http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC
wurde die Ungenauigkeit durch die Fehlende Teilbarkeit der
Ursprungsfrequenz erklärt.
In meinem Fall aber, geht die Rechnung genau auf, wodurch ein Wechsel
vom Overflow-Interrupt zum CopareMatch-Interrupt mir in diesem Fall
keinen Vorteil bringt.
Die Ungenauigkeiten, durch die Taktquelle (Quarz oder Software) müssten
über diesem Zeitraum im Bereich weniger Sekunden liegen. 40 Minuten ist
einfach zu viel. Selbst wenn ich den Kontroller im Tiefkühlfach gehabt
hätte.
Also, entweder habe ich wirklich einen enormen Fehler gemacht oder die
Taktquelle ist nutzlos. Ich meine, 4 Minuten in einer Stunde, dass ist
nicht zu gebrauchen.
Hier nochmal der Code:
Eine Sekunde innerhalb von 2 Stunden wären mit zusammengekniffenen Augen
noch zu verkraften. Denn dann könnte man mit dem DCF77 Signal die Uhr
halbwegs genau halten. Aber bei 4 Minuten, geht die Uhr trotz DCF77
immer wieder erheblich nach. Also bis zu 4 Minuten, und auch nur, wenn
das Signal gut genug ist um es jede Stunde einmal exakt auszulesen.
Ich bin zwar noch weit weg davon, daran zu verzweifeln.
Aber es stört mich, wenn ich etwas nicht verstehe.
Und in diesem Fall verstehe ich nun wirklich nicht, woher dieser Fehler
kommt.
Gruß und Danke
BrEin
Hi
>Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen!
Ich habe heute mal an einem STK500 die Frequenz gemessen. Lag noch unter
dem errechnenten Wert. Der Zettel mit dem genauen Wert liegt allerdings
gerade 10km entfernt. Aber das was du machst ist Unsinn. Besorge dir
einfach einen passenden Quarz für das STK und höre auf deine Zeit zu
vergeuden.
MfG Spess
Fabian Hoemcke schrieb:> Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen!> :D
Du sollst endlich den Artikel ernst nehmen und zurückrechnen welche
Taktfrequnz du wirklich hast!
Geh dein Programm durch und wenn du keine Fehler mehr siehst, dann ist
es nur logisch, dass die Taktfrequenz mit der du zu rechnen angefangen
hast, nicht stimmt.
Und jetzt ist es dann plötzlich auch nicht mehr egal ob du Overflow oder
CTC Modus implementiert hast :-)
Karl heinz Buchegger schrieb:> Fabian Hoemcke schrieb:>> Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen!>> :D>> Du sollst endlich den Artikel ernst nehmen und zurückrechnen welche> Taktfrequnz du wirklich hast!>> Geh dein Programm durch und wenn du keine Fehler mehr siehst, dann ist> es nur logisch, dass die Taktfrequenz mit der du zu rechnen angefangen> hast, nicht stimmt.>> Und jetzt ist es dann plötzlich auch nicht mehr egal ob du Overflow oder> CTC Modus implementiert hast :-)
Hallo Karl,
ich nehme diesen Artikel durchaus ernst.
Denn erstens hätte ich mir sonst nicht so viel Mühe gegeben ihn zu
erstellen und zweitens wäre ich nicht so hartnäckig.
Aber dennoch habe ich den Quatsch mit den 55 Sekunden ausprobiert und
siehe da, über eine Stunde absolut in Ordnung.
(Länger habe ich es nicht getestet, da diese Lösung keine ernsthafte
Alternative darstellt.)
Ich hatte schon vor ein paar Tagen mal die eigentliche Taktfrequenz
errechnet, aber gedacht ohne eine Methode sie wirklich zu Messen/Prüfen
wäre es sinnlos. Denn der Fehler kann ja eigentlich überall sein.
(Und bei Messgeräten die das leisten können habe ich eine Hausnummer von
300€ gehört.)
1
3.686.400Hz | x
2
------------------
3
60s | 56s
4
x = 3.686.400Hz * 56s / 60s = 3.440.640Hz
Sollte das stimmen (also auch der Rechenansatz und die Lösung), so habe
ich ein gravierendes Problem.
Dafür werde ich mal einen eigenen Thread auf machen, damit hier nichts
durcheinander kommt. Denn mit einen Quarz (3,6864MHz) funktioniert es
auch nicht.
Karl heinz Buchegger schrieb:> Und jetzt ist es dann plötzlich auch nicht mehr egal ob du Overflow oder> CTC Modus implementiert hast :-)
Das verstehe ich nun wirklich nicht.
Ich meine, du hast absolut recht, aber der Fehler (falscher Takt) müsste
sich doch auf die gleiche Art zeigen.
Ich habe es mal umgeschrieben und nun habe ich 10 - 11 Sekunden Schwund
pro Minute:
Hi
[c]
SIGNAL(TIMER0_COMP_vect)
{
if(c++ == 16)
{
c = 0;
if(Secundes++ == 59)
{
Secundes = 0;
if(Minutes++ == 59)
{
Minutes = 0;
if(Hours++ == 23) Hours = 0;
}
}
}
return;
}
[/]
Mit der Rechnung verlierst du eine Sekunde pro Minute, eine Minute pro
Stunde und eine Stunde pro Tag.
MfG Spess
Hi
>Und bitte, wie kommst du dadrauf?
Wenn deine Sekunden 58 sind, und du die Sekunden incrementierst kommst
du auf 59. Und da stellst du die Sekunden auf Null und incrementierst
die Minuten. Damit fehlt dir die Sekunde 59. Das darf erst passieren
wenn
Secundes++ == 60
ist. Rest analog.
Ist dir nicht aufgefallen, das du von 58 auf 0 springst?
MfG Spess
Kennst du den Unterschied zwischen Prost- und Preincrement?
Pre
Bei
1
if( ++Secundes == 59 ) Secundes = 0;
hättest du recht. Da er erst inkrementiert und dann gleich auf Null
zurück setzt. Somit würde die 59 immer übersprungen werden.
Post
Aber bei
1
if( Secundes++ == 59 ) Secundes = 0;
vergleicht er erst und zählt dann hoch. In dem kritischen Fall sieht er
erst dass 58s nicht gleich 59s sind, zählt dann hoch und überspringt das
Zurücksetzten. Bei 59s zählt er dann zwar auch erst hoch auf 60s und
setzt es dann aber auch gleich auf Null.
Also kein Fehler!
Danke aber für deine Aufmerksamkeit.
Solche blöden Fehler sind es dann nämlich meist, die einem die größten
Probleme bereiten.
Gruß
Brein
Hi
>Kennst du den Unterschied zwischen Prost- und Preincrement?
Schon. Da ich aber eigentlich kein C kann, war mir das in dem Fall nicht
so gegenwärtig. Entschuldige die Aufregung.
MfG Spess
Dafür brauchst du dich nun wirklich nicht entschuldigen.
Aber zurück zum Thema.
Ich habe nun den
Beitrag "Taktproblem mit dem STK500" fertig.
Hat natürlich länger gedauert. Aber ich hoffe es führt zu Ziel.
Gruß
BrEin
PORTA=0xFF;// enable pull ups | buttons default high
26
TCCR0=0x05;// 0000|0101 | pre-scaler Clck/1024
27
OCR0=225;
du gehst zwar auf den Compare Interrupt, aber das bedeutet nichtr
automatisch, dass der Timer nach Erreichen des Compare Wertes von sich
aus auf 0 zurückgesetzt wird.
Dein Timer läuft nach wie vor von 0 bis 256. Nur dass du die
Sekundenweiterschaltung nicht bei einem Timerwert von 0 machst, sondern
bei einem Timerwert von 225. Das ändert aber nichts daran, dass der
Timer nach wie vor 256 Zählvorgänge machen muss, bis er das nächste mal
wieder den Compare Wert erreicht.
Du musst den CTC Modus auch aktivieren, wenn du ihn benutzen willst.
Fabian Hoemcke schrieb:> Fehler behoben! Sind wieder 4 Sekunden.
In welchem Kontrollzeitraum?
> Aber das mit dem Reset des TCNT0 wollte ich noch fragen.> Stimmt, hatte ich ganz vergessen.
Widersteh der Versuchung in der ISR den TCNT0 auf 0 zu setzen. Denn
genau dazu hast du den CTC Modus.
TCCR0 = ( 1 << WGM01 ) | ( 1 << CS02 ) | ( 1 << CS00 );
und schon setzt sich der Timer beim Compare Match selbst zurück auf 0.
Mit einem OCR Wert von 224 müsste es dann eigentlich ziemlich gut
stimmen
3686400/1024/16 = 225
d.h. 225 Zählzyklen. d.h. der OCR Wert muss 224 sein, weil ja 0 auch ein
Zählzyklus ist.
PS: Ich hätte das auch so geschrieben
1
c++
2
if(c==16)
3
{
4
c=0;
5
Secundes++;
6
7
if(Secundes==60)
8
{
9
Secundes=0;
10
Minutes++;
11
if(Minutes==60)
12
{
13
Minutes=0;
14
Hours++;
15
if(Hours==24)
16
Hours=0;
17
}
18
}
19
}
Über deine Version hab ich auch erst mal brüten müssen, bis ich wusste,
dass sie richtig ist. So finde ich das einfach weniger verwirrend.
Karl heinz Buchegger schrieb:> Fabian Hoemcke schrieb:>> Fehler behoben! Sind wieder 4 Sekunden.>> In welchem Kontrollzeitraum?
1 Minute! :D
War eher eine Stichprobe!
Kannst du mir hier erklären was gemacht wird?
Fabian Hoemcke schrieb:> Karl heinz Buchegger schrieb:>> Fabian Hoemcke schrieb:>>> Fehler behoben! Sind wieder 4 Sekunden.>>>> In welchem Kontrollzeitraum?>> 1 Minute! :D> War eher eine Stichprobe!
Das ist schon eine ganze Menge.
>> Kannst du mir hier erklären was gemacht wird?>
1
TCCR0=(1<<WGM01)|(1<<CS02)|(1<<CS00);
Es werden die Bits WGM01, CS02 und CS00 im Register TCCR0 gesetzt.
Wenn du wissen willst was die machen: Steht im Datenblatt - mit genau
den gleichen Bitnamen.
(Ach, ich vergass. Sag, welchen µC hast du nochmal am STK stecken. Ich
hatte noch einen Mega16 im Hinterkopf, kann aber auf die Schnelle im
Thread nicht mehr finden, welcher Prozessor es wirklich ist)
Sorry! Es ging um den TCCR0 und nicht um den TCNT0.
Das hatte mich schon gewundert.
CS02 und CS00 sind mir klar und habe ich auch gesetzt wie du siehst!
//Prescaler = 1/1024
Bit 6, 3 - WGM01:0: Waveform Generation Mode
______________________________________________
These bits control the counting sequence of the counter, the source for
the maximum (TOP) counter value, and what type of waveform generation to
be used. Modes of operation supported by the Timer/Counter unit are:
Normal mode, Clear Timer on Compare Match CTC mode, and two types of
Pulse Width Modulation (PWM) modes.
Wer lesen kann...
danke dir!
Wird ausprobiert!
Fabian Hoemcke schrieb:> Sorry! Es ging um den TCCR0 und nicht um den TCNT0.>> Das hatte mich schon gewundert.> CS02 und CS00 sind mir klar und habe ich auch gesetzt wie du siehst!
Ja.
Der springende Punkt ist:
Gewöhn dir deine Bit-Schreibweise mit den hardcecodeten Bits wieder ab.
Habe ich leider so gelernt bekommen.
Kann man natürlich auch wieder verlernen.
Aber eigentlich finde ich meine Schreibweise mit den Kommentaren besser.
Ist wohl aber Gewöhnungssache.
Ich habe den Autoreset eingestellt.
Aber selbes Probelem.
(Eine Minute getestet - 4 Sekunden verloren. Ich denke, bei einer
gravierenden Änderung hätte man das schon vestgestellt.)
Fabian Hoemcke schrieb:> Habe ich leider so gelernt bekommen.> Kann man natürlich auch wieder verlernen.>> Aber eigentlich finde ich meine Schreibweise mit den Kommentaren besser.> Ist wohl aber Gewöhnungssache.
:-)
Das machst du so lange, bis du das erste mal ein Programm von Prozessor
A nach Prozessor B portierst. Prozessor B hat dasselbe Steuerregister
und in diesem Register sind sogar dieselben Bits drinnen, aber leider
leider an einer anderen Stelle.
TCCR0 = ( 1 << WGM01 ) | ( 1 << CS02 ) | ( 1 << CS00 );
solange das TCCR0 Register über das Bit WGM01 verfügt, muss´ich mich
nicht darum kümmern, welches Bit im Register das genau ist. Der Compiler
kümmert sich darum.
D.h. ich muss beim portieren gar nichts tun.
Schreibst du aber
TCCR0 = 0x15;
dann bist du dafür verantwortlich, dass dieses 1 Bit an eine andere
Stelle kommt, weil es beim anderen Prozessor woanders sitzt
TCCR0 = 0x25;
Bist du einmal darüber gestolpert und hast 2 Stunden damit verbracht
dieses Problem zu suchen, dann wirst du nie wieder die Bitschreibweise
benutzen.
> (Eine Minute getestet - 4 Sekunden verloren. Ich denke, bei einer> gravierenden Änderung hätte man das schon vestgestellt.)
das heißt jetzt dein Proz macht in 1 Minute nur 56 Sekunden?
Das hatten wir doch oben schon mal. Noch ehe der Timerreset bei 225
gemacht wurde.
Irgendwas ist bei dir oberfaul.
Das hatte ich überhaupt noch nie. Egal mit elchem Quarz, mit der
richtigen Teilerkette im Timer erreicht man auf Anhieb und mit
Leichtigkeit eine Genauigkeit von ein paar Sekunden pro Tag. So weit
kann der Quarz gar nicht daneben sein.
Grr.
Bitte schreib das nicht so dicht sondern in der naheliegenden Form
1
SIGNAL(TIMER0_COMP_vect)
2
{
3
4
if(c++==16)
5
{
6
c=0;
Das zählt 17 ISR Aufrufe, bis dann endlich die Sekundenweiterschaltung
in Gang kommt. Du hast die Teilerkette aber auf 16 ISR Aufrufe
gerechnet. Pro Sekunde verlierst du also 1/16 Sekunde. In 1 Minute (=60
Sekunden) sind das 3.75 Sekunden. Da hast du deine 4 Sekunden.
Ich sagte doch schon. Schreibs so
1
c++
2
if(c==16)
3
{
4
c=0;
5
Secundes++;
6
7
if(Secundes==60)
8
{
9
Secundes=0;
10
Minutes++;
11
if(Minutes==60)
12
{
13
Minutes=0;
14
Hours++;
15
if(Hours==24)
16
Hours=0;
17
}
18
}
19
}
da kann ein Blinder greifen, wann genau jeweils 1 weitergezählt wird. Es
bringt nichts, wenn man in C zu clever coden will. Man legt sich
meistens nur selber hinein.
STIMMT!
Das ist es!
Das ist genau die Art Fehler von denen ich weiter oben Sprach!
Fabian Hoemcke schrieb:> Kennst du den Unterschied zwischen Prost- und Preincrement?
...
>> Also kein Fehler!>>> Danke aber für deine Aufmerksamkeit.> Solche blöden Fehler sind es dann nämlich meist, die einem die größten> Probleme bereiten.>> Gruß> Brein
Danke dir!
Immerhin rechnete ich ja auch bis 59s, 59min oder 23h.
Warum habe ich dann hier so einen Mist gemacht.
Naja.
Aber,
was du da oben geschrieben hast, war schlicht weg ein Preincrement.
Ich denke aber, auch das sollte ich langsam lassen.
Immerhin gab es ja auch andere die das nicht ohne Probleme lesen konnten
und mir ist der Fehler auch nicht sofort oder bei Zeiten aufgefallen.
Da muss ich halt der Lesbarkeit der Eleganz den Vorzug geben.
Danke dir und Asche auf mein Haupt.
Vielen Dank!
Die erste Minute hat es immerhin schon sauber geschafft.
Danke dir nochmal!
Fabian Hoemcke schrieb:> Immerhin rechnete ich ja auch bis 59s, 59min oder 23h.
Das ist der eigentliche Mist.
Du willst 60 Sekunden abzählen, also sollten auch die 60 im Vergleich
auftauchen.
Das ist genau derselbe Grund warum man
for( i = 0; i < N; i++ )
schreibt, wenn man eine Schleife haben will, die N mal wiederholt wird.
Das N taucht in der Abbruchbedingung direkt auf.
Und nicht
for( i = 0; i <= N-1; i++ )
und auch nicht
for( i = 1; i == N; i++ )
und was es sonst noch so an diversen vermeintlich cleveren Formen gibt.
Den Hinweis nehme ich dankend an!
(Das mit der Zählschleife, mache ich genauso.
Aber aus einem anderen Grund. Obwohl deiner deutlich besser ist.)
---
Ich habe einen Test über 15 Minuten laufen lassen.
Dabei gingen 12 Sekunden verloren.
Also mein Meisterwerk geht immer noch 9 Sekunden bei 10 Minuten nach.
Ist das bei einem SW-Takt schon normal?
Kann da ein Offset errechnet und eingestellt werden, wie bei den
internen Clocks der µCs?
Oder sollte ich lieber so lange auf den Code schauen, bis ich auch
diesen Fehler finde.
---
Ich möchte jetzt nicht päpstlicher sein als der Papst, ich möchte nur
wissen, ob ich mit dieser Taktquelle das Limit schon erreicht habe oder
ob es noch besser geht?
Gruß und tausend Dank
BrEin
Fabian Hoemcke schrieb:> Ich habe einen Test über 15 Minuten laufen lassen.> Dabei gingen 12 Sekunden verloren.> Also mein Meisterwerk geht immer noch 9 Sekunden bei 10 Minuten nach.
Das sind 1.5%
> Ist das bei einem SW-Takt schon normal?
Ich weiß jetzt nicht, wie das STK den Takt erzeugt.
Für einen Quarz ist das deutlich zu viel
Karl heinz Buchegger schrieb:> Das sind 1.5%
Oder auch nur noch 3.75% der ursprünglichen Differenz.
Darüber bin ich ja schon mal froh.
Dieser Takt wird vom Onbord-Kontroller in Software generiert.
Karl heinz Buchegger schrieb:> Mit einem OCR Wert von 224 müsste es dann eigentlich ziemlich gut> stimmen>> 3686400/1024/16 = 225>> d.h. 225 Zählzyklen. d.h. der OCR Wert muss 224 sein, weil ja 0 auch ein> Zählzyklus ist.
Das verstehe ich jetzt nicht.
Wenn TCNT0 die 225 erreicht hat, wird er doch schon zurückgesetzt und
die ISR ausgeführt oder etwa nicht.
Ich stelle die Frage etwas anderes.
1
Einen Zählzyklus lang ist der TCNT0 auf Null. Am ende wir er dann auf Eins Hochgezählt.
2
Einen Zählzyklus lang ist der TCNT0 auf Eins. Am ende wir er dann auf Zwei Hochgezählt.
3
Einen Zählzyklus lang ist der TCNT0 auf 223. Am ende wir er dann auf 224 Hochgezählt.
4
Einen Zählzyklus lang ist der TCNT0 auf 224. Am ende wir er dann auf 225 Hochgezählt.
5
Der TCNT0 resettet und die ISR ausgelöst.
D.h. wenn meine Vorstellung von der Arbeitsweise des Kontrollers nicht
falsch ist, müssten OCR0 = 225 richtig sein, oder ist sie falsch?
---
Danke, wir nähern uns an - haben es bald geschafft! :D
Gruß BrEin
Fabian Hoemcke schrieb:> Das verstehe ich jetzt nicht.> Wenn TCNT0 die 225 erreicht hat, wird er doch schon zurückgesetzt und> die ISR ausgeführt oder etwa nicht.
Nein. Erst im nächsten Schritt wird dann der Zähler zurückgesetzt. Der
Zähler nimmt den Wert des Vergleichswertes noch an.
mit einem Vergleichswert von 8 hättest du dann
der Timer zählt Anzahl Zählvorgänge
0 ---+
| 1
1 <--+ ---+
| 2
2 <--+
3
3
4
4
5
5
6
6
7
7
8
8
9
0
Wenn der Zähler von 0 über 8 wieder bis 0 zählt, dann braucht er dazu 9
Zählschritte.
Willst du also für einmal Rundum n Zählschritte haben, dann musst du den
Vergleichswert auf n-1 setzen.
Du willst alle 225 Schritte einen Interrupt haben, also muss der
Vergleichswert auf 224 eingestellt werden
Überzeugt!
Wird ausprobiert!
Entschuldigung, wenn ich dich immer dazu nötige ausführlich zu werden.
Erstens möchte ich verstehen, weshalb das, was andere sagen auch richtig
sein soll und zweitens, copy & paste ist wirklich nicht meine Art.
Auch wenn ich mir nun bei dir ziemlich sicher bin, dass ich mir das
sparen könnte.
Danke!