Hallo,
ich bin neu auf dem Gebiet und habe mir daher die Aufgabe gestellt eine
Uhr zu bauen (mittel 7-Segmentanzeigen). Das ganze funktioniert als
Brettaufbau an sich auch gut, nur dass die Uhr zu langsam läuft.
Daten:
- 8Mhz interner Osc. (laut Datenblatt und AVR Studio)
- Prescaler auf 1024
- 8Bit-Counter
Das macht also rund 30 Überläufe je Sekunde (256). Eine entsprechende
Zählvariable sorgt bei erreichten 30 für die Inkrementierung der
Zeitvariable (sek).
Nun ändert sich die Sekundenanzeige aber nur etwa alle 3-4 sek.
Hat jemand einen Vorschlag?
Das ganze riecht nach geringerem Takt als 8Mhz, ich habe aber nie etwas
am Takt geändert.
Anbei noch eine Frage: Zählt der Timer auch dann hoch, wenn gerade eine
ISR (Timer Overflow) abläuft, oder bleibt er währenddessen konst.?
Vielen Dank schonmal...
Henry schrieb:
> Brettaufbau an sich auch gut, nur dass die Uhr zu langsam läuft.>> Daten:> - 8Mhz interner Osc. (laut Datenblatt und AVR Studio)
Aus diesem Grund wird deine Uhr auch nie einigermassen genau gehen
> Das macht also rund 30 Überläufe je Sekunde (256). Eine entsprechende
Da ist die nächste Fehlerquelle
> Nun ändert sich die Sekundenanzeige aber nur etwa alle 3-4 sek.
Das ist allerdings zu heftig um es mit den beiden Fehlerquellen zu
erklären.
> Das ganze riecht nach geringerem Takt als 8Mhz,
allerdings
> ich habe aber nie etwas am Takt geändert.
macht nichts. Trotzdem kontrollieren.
(Endlosschleife und mit einem _delay_ms dazwischen eine LED blinken
lassen. Optisch kontrollieren ob die delay Zeiten stimmen können. 500ms
kann man auch optisch kontrollieren. Ob eine Led pro Sekunde 1 mal oder
3 blinkt, sieht auch ein Blinder)
Du darfst natürlich nur alle 60s die Minute inkrementieren.
Du machst es aber alle 30s.
Nimmt man an, das Du die CKDIV8 Fuse nicht zurückgesetzt hat, läuft der
uC mit 1MHz. Das würde etwa den Faktor 3-4 (im Zusammenhang mit dem
nicht korrekten 30s wg. 8MHz / 1024) erklären.
Oops. Da habe ich mich verguckt. Du inkrementierst die Minute jedenfalls
denke ich alle Sekunde da Du 30 Überläufe pro Sekunde hast.
CKDIV8 würde ich auf jeden Fall angucken.
Die Funktionalität in integer_split ist schon reichlich viel Tobak für
eine ISR. Divisionen sind so ziemlich das schlimmste, was du einem AVR
bei den Grundrechnungsarten antun kannst. Da muss er richtig arbeiten.
Die würde ich in die main Schleife rausziehen. Die ISR setzt nur ein
Flag, dass die min (welche eigentlich Sekunden sind) nicht mehr stimmen
und die Hauptschleife zerlegt sich dann die Zahlen so wie sie das
braucht.
Hi!
Danke zunächst für die Antworten. Werde das Bit und den Takt nochmal
überprüfen (mittels LED etc.).
Bezüglich der ISR habe ich noch eine Frage:
Ursprünglich wurde die Funktion "integer_split" in der main-schleife
ausgeführt. Da hatte ich allerdings das Problem, dass die
Segmentanzeigen nicht richtig funktionierten (Multiplex). Heißt, während
der Ausführung der integer_split blieb die letzte Anzeige an. Die letzte
Anzeige ist länger an als die anderen, so dass es fürs Auge aussieht als
ginge nur die letzte.
Vielleicht sollte ich während der Abarbeitung des integer_split kein
Signal auf die Anzeigen schicken....
Die überfüllte ISR braucht also u.U. derart lange, dass ich damit den
eigentlich folgenden Overflow Interrupt verpasse?
Henry schrieb:
> ausgeführt. Da hatte ich allerdings das Problem, dass die> Segmentanzeigen nicht richtig funktionierten (Multiplex).
Na ja.
Der ganze Multiplex ist .... unkonventionell aufgebaut :-)
Normalerweise verlegt man diesen Multiplex in eine Timer-ISR
Allerdings ist die eine, die du hast, mit 30 Aufrufen in der Sekunde ein
wenig zu schmalbrüstig für einen 7-Segment Multiplex.
Du hast 2 Möglichkeiten
* für das Multiplexing einen eigenen Timer samt ISR abstellen.
* Die Aufrufrate deiner jetzigen ISR erhöhen und dort den 7-Seg
Multiplex mitmachen lassen
Welche Variante du auch immer wählst:
Bei einem Aufruf wird nur auf die jeweils nächste Stelle der Anzeige
weitergeschaltet und NICHT alle 4 Anzeigen in einem Rutsch
durchgeschaltet. Dei jeweils nächste Anzeige kommt erst beim nächsten
ISR Aufruf drann. Auf diese Art leuchten alle Anzeigen immer gleich lang
(minus dem bischen Zeitversatz, der in der ISR für das Zeit hochzählen
vergeht. Das ist aber zu kurz, als dass du es sehen wirst)
> Vielleicht sollte ich während der Abarbeitung des integer_split kein> Signal auf die Anzeigen schicken....
Du solltest den Update der Anzeige von allen anderen Dingen entkoppeln,
indem du ihn in eine ISR verlagerst. Dann brauchst du dir um die Anzeige
keine Sorgen mehr machen. Du beschreibst einfach nur deine Variablen und
die ISR sorgt dafür, dass dieser Inhalt auch zur Anzeige kommt.
> Die überfüllte ISR braucht also u.U. derart lange, dass ich damit den> eigentlich folgenden Overflow Interrupt verpasse?
Ich habs nicht nachgerechnet. Aber mein Bauch sagt: könnte sein. Du hast
ziemlich viele Divisionen in dieser split Funktion.
Edit: Jetzt seh ichs erst. Ich wusste doch, dass mich da etwas stört. Du
zählst nicht 30 ISR Aufrufe ab, sondern 31
ISR(TIMER0_OVF_vect)
{counter++;
if (counter > 30)
Probiers mit einer kleineren Zahl und deinen Fingern aus.
ISR(TIMER0_OVF_vect)
{counter++;
if (counter > 4)
Der Counter hat beim Betreten der ISR die Werte:
0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4
Von einer 0 zur nächsten 0 sind das aber 5 Zählvorgänge und nicht 4
Schreib deine ISR nicht so kryptisch
1
ISR(TIMER0_OVF_vect)
2
{
3
counter++;
4
5
if(counter==30)
6
{
7
counter=0;
8
sec++;
9
if(sec==60)
10
{
11
sec=0;
12
min++;
13
if(min==60)
14
{
15
min=0;
16
hrs++;
17
if(hrs==24)
18
hrs=0;
19
}
20
}
21
}
22
}
So funktionieren alle Zählstufen gleich und es ist leichter sich von der
korekten Funktion zu überzeugen.