PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Programmierproblem PropClock



Murus
30.10.2005, 12:58
Hallo zusammen,

meine Propclock funktioniert!
Es ist zwar eine Heissleim-Sekundenkleber-McGyver-Lösung, aber sie funktioniert.... :)

Ich betreibe 8 Leds direkt am AVR (hat Nachteile, ich kann sie nicht gross bestromen, muss sie daher etwas länger leuchten lassen, das ergibt etwas breite Zahlen, aber es geht)

Die Synchronisation zwischen Drehzahl und Anzeige realisiere ich über eine kleine Gabellichtschranke, die mitdreht und durch einen Draht rauscht. Über Schleifer schicke ich 15V in einen 680müF Elko, an dem ein 7805er hängt, der mir den Mega mit Saft versorgt.

Ich habe momentan einen Timer am Laufen. (Timer0) Wenn er überläuft, wird die nächste Spalte "angeleuchtet".
Wenn er 4 mal übergelaufen ist (so breit ist die Zahl 1), stellt er den Timer und die Leds ab. Wenn der Interrupt 0 (da hängt die Lichtschranke) reinkommt, dann wirft er den voreingestellten Timer 0 an, geht wieder an den Anfang der Zahl 1 im Array und übergibt die Kontrolle wieder dem Timer1.

So krieg ich momentan eine schöne Zahl dargestellt.
Eine Anpassung des Timer0 über die Drehzahl (damit die Zahlen immer gleich breit sind) werde ich nicht machen, da ich konstant drehen lasse und sowieso jetzt schon recht langsam drehe, schneller ist nicht möglich, sonst hauts mir wahrscheinlich den Aufbau um die Ohren, aber so gehts gut, die Zahlen flackern nur schwach.

Jetzt möchte ich aber eine Uhr bauen. Mein momentan nicht aktives Hauptprogramm ist noch frei, sowie zwei Timer.
Wie mache ich das? Das kann ich doch irgendwie auch nicht mit einem Timerinterrupt, der mit 1Hz kommt machen, da das sonst doch ein Chaos mit den anderen beiden Interrupts gibt...

Wie könnte man das lösen? Wie berechne ich die Zeit-Zahlen, um sie anzuzeigen? (Anzeigen tu ich in der Timer0-Interruptroutine)

SprinterSB
30.10.2005, 13:23
Meine Uhr-Software haben folgende Struktur:
Timer1 macht den Arbeitstakt. Die Hauptarbeit übernimmt OutCompare1. Die Interruptlast habe ich bei 10kHz, weil ich ne PWM von Hand machen muss. OutComp1 macht mir softwaremässig nen Untertakt von 100Hz (10ms) wo ich Taster nachschaue, 10ms-Timer fur Warteschleifen erniedrige, DCF-Modul abfragen, Zeit weiterzähle alls kein DCF-Signal da ist, etc...
Das Hauptprogramm hat fast nix zu tun. Es übernimmt Initialisierung, Debugausgabe via RS232 und Menüsteuerung zur Konfiguration der Uhr (Weckzeit, Anzeigeeigenschaften, etc). Die meiste Zeit schnarcht es in der Menü-SCHleife und wartet auf EINgaben vom Benutzer.

Timer0 brauche ich nur für RC5-Empfang (ganz praktisch).

Wenn du high efficiency-LEDs nimmst oder super bright, brutzelst du dir mit 10mA fast die Netzhaut ab. An Helligkeit sollte es da nicht mangeln...

Murus
30.10.2005, 13:34
Eeeeh, wie was wo jetzt? Was meinst du mit dem Arbeitstakt? Und was ist OutCompare1? Wo haste die Ausgabe verstaut? Ich hab eben so ganz kleine Leds, die brauchen net viel, dafür net so hell, aber es geht gut, man siehts klar.

SprinterSB
30.10.2005, 13:58
Timer1 hat einen Modus, inder er immer einen Interrupt auslöst, wenn der Wert von Timer1 (TCNT1) mit OCR1A (Output Compare Register 1A) übereinstimmt. Wenn man zusätzlich CTC1 (Clear Timer on Compare Match) aktiviert hat, wird beim Erreichen des Wertes der Zähler wieder auf 0 zurückgesetzt. Damit vermeidet man Ungenauigkeiten wie sie entstehen, wenn man selber den Timerwert zurücksetzt.

Den Interrupt den ich verwende heisst "Timer/Counter1 Compare Match A".
Die Anzeige aktualisiere ich in diesem Interrupt, der mit einer Rate von 10kHz kommt. Alle 100 Interrupt-Ereignisse erledige ich das Zeug, das nur alle 10 ms dran kommen muss. In dem Interrupt mach ich auch die Frequenz für nen kleinen Lautsprecher (Rückmeldung für Tastendruck), allerdings nur bei 2-3kHz. Die verschiedenen Frequenzen von 3kHz oder 100Hz realisiere ich einfach über Zählvariablen, die ich hochzähle.
Für den 100Hz-Interrupt wird eine Variable auf 0 gesetzt und in jedem Interrupt eins erhöht. Bin ich bei 100, dann wird sie auf 0 zurückgesetzt und der Job, der alle 10ms dran ist, ausgeführt.

Murus
30.10.2005, 14:26
Ahaaa, jetzt seh ichs...
Hmmm
Bei dir kommt der Interrupt alle 10kHz... Zeigst du dann jedesmal die nächste Led-Reihe an?
Aber wenn du dann ab und zu noch andere Sachen abfragst, dann gibt es doch ab und zu Lücken zwischen den Zahlen...???
Du hast das Ding ja über Funk gelöst..
Wie könnte ich das machen? Ich möchte die Zeit über einen Timer berechnen, nur wie? Einen Timer verwende ich ja eben zum Anzeigen.. ??

SprinterSB
30.10.2005, 17:13
Du kannst auch Timer1 verwenden um die Zeit zu machen und Timer0 für die Anzeige. Jedanfalls ist das eine Lösung, wo du nicht viel oder nix in deiner Anzeigeroutine ändern musst.

Timer1 macht dir die Zeit, könnte sogar nur 1 Interrupt alle Sekunde machen (über Prescaler). Das reicht schon. Die Zeit schreibst du in Variablen: Sekunde, Minute, Stunde, Wochentag, was du anzeigen willst und benutz das in Timer0. Ob du sukzessive Interrupts erlaubst, kommt drauf an wie stark deine Echtzeitanforderung ist. Falls Timer0 den Timer1-Interrupt unterbrechen darf, könnte passieren, daß zwischen Minute hochzählen und Stunde zählen Timer0 zuschlägt. Nach 12:59 wird dann nicht 13:00 angezeigt sondern fälschlicherweise 12:00. Da gibt es 2 Möglichkeiten mit umzugehen:
- du ignorierst es. Die falsche Zeit wird nur sehr kurz angezeigt. Beim nächsten Umlauf stimmt wieder alles. Wahrscheinlich fällt das nicht mal auf.
- für die Zeit wo die Daten inkonsisten sind, deaktivierst du Timer0 Interrupt oder alle Interrupts. Danach werden die IRQs wieder freigegeben. IRQ gehen dabei keine verloren.

Lücken gibt es bei mir nicht. Meine Anzeige ist kein Rotor, aber ich muß auch fix sein, sonst flackert die Anzeige.

Du kannst auch alles in einer ISR machen. Erst die Anzeige setzen bzw die neue LED-Spalte. Danach machst du sonstiges Zeug das ansteht. Dadurch hast du die LED-Aktualisierung immer im gleichen Zeitraster. Daß die Zeit erst danach angepasst wird stört nicht. Wenn dein Rotor mit 50 U/s dreht und du in 7.2°-SChritten aktualisieren musst (1/50 Vollwinkel), brauchst du eine IRQ-Rate von 2.5kHz. Bei 16MHz MCU-Takt hast du 6400 Zyklen bis wieder ein IRQ kommt. Massig Zeit!

Murus
30.10.2005, 17:20
Jo, hab das AVR-Zeitgefühl noch net so drauf... :)

Ich habs jetzt mal mit Timer1, der 1 Interrupt/s liefert gemacht... funktioniert gut!
Tja, dann gehts weiter mit verbessern und experimentieren...

Herzliche Grüsse und vieelen Dank!!

Mario

SprinterSB
30.10.2005, 17:59
Wenn ich's richtig verstanden hab, ist der Aufbau so:

15V -- Schleifer -- Elko -- 7805 -- µC + LEDs ?

Wie laut ist das Teil eigentlich? Und mit welcher Drehzahl arbeitest du?

Murus
30.10.2005, 18:49
Ja, Aufbau stimmt....

Das Ding ist höllisch laut, Bürstenmotor halt.
Drehzahl weiss ich net, aber ziemlich langsam, das Ding ist net gewuchtet, funktioniert aber gut.

Soll natürlich keine Uhr zum Zeitablesen werden, mehr ein Experimentierträger.

Murus
13.11.2005, 20:07
Also, ihr mit den hohen Mehrwertsteuern,

die Propclock zeigt mir jetzt die Sekunden und Minuten an. Zeit kann nicht eingestellt werden, da ich keine Taster dran hab, sie zählt einfach hoch und zeigt die Minuten und Sekunden an.
Timer1 erzeugt mir jede Sekunde einen Interrupt, in dem hochgezählt wird. 1800 mal in der Sekunde kommt ein Timer0-Interrupt, in dem die nächste Spalte angezeigt wird. Das Ganze funktioniert nicht schlecht, ab und zu flackert die Anzeige, da sich die Interrupts in die Wege kommen. (eine Zahl bleibt zu lange stehen). Zudem sind die "Pixel" etwas lang, da meine Leds nicht grad dolle hell sind, ich betreibe sie direkt am AVR, kann also net zuviel Strom geben.
Aber sonst funktioniert. Hier ist mal der Code, für alle, die Interesse haben:

$regfile = "m8def.dat"
$crystal = 1000000

Dim A As String * 2 ' Wird gebraucht, um die Zahlen in einen String zu verwandeln
Dim I As String * 2 ' Eine Ziffer des Strings/der Zahl
Dim O As String * 2 ' Eine Ziffer des Strings/der Zahl
Dim Z As Byte ' Zählvariable für die Sekunden
Z = 0
Dim M As Byte ' Absolute Ziffer des Strings
Dim N As Byte ' Absolute Ziffer des Strings
Dim G As Byte ' Startzahl zweite Ziffer der ersten Zahl(Sekunden)
Dim U As Byte ' Startzahl erste Ziffer der ersten Zahl (Sekunden)
Dim W As Byte ' Stopzahl erste Ziffer der ersten Zahl (Sekunden)
Dim H As Byte ' Stopzahl zweite Ziffer der ersten Zahl (Sekunden)
Dim K As Byte ' Zählvariable, zur Bestimmung, wann die nächste Zahl dargestellt wird
Dim J As Byte ' Kopie von U
Dim E As Byte ' Kopie von W
Dim T As Byte ' Kopie von G
Dim P As Byte ' Kopie von H
Dim B As Byte ' Zählvariable für die Minuten
B = 0
Dim C As Byte ' Startzahl erste Ziffer der zweiten Zahl (Minuten)
Dim V As Byte ' Stopzahl erste Ziffer der zweiten Zahl (Minuten)
Dim F As Byte ' Stopzahl zweite Ziffer der zweiten Zahl (Minuten)
Dim S As Byte ' Startzahl zweite Ziffer der zweite Zahl (Minuten)
Dim Q As Byte ' Kopie von C
Dim R As Byte ' Kopie von V
Dim D As Byte ' Kopie von S
Dim Y As Byte ' Kopie von F
Dim Lk As Byte ' Variable für die For-Schleife für den Doppelpunkt
Dim Mn As Byte ' Variable für die Stelle im String, wo ausgelesen wird


Ddrb = &B11111111 ' Led-Ausgänge (ganzer Port B)
Ddrd.2 = 0 ' Lichtschranken-Eingang
Portd.2 = 1 ' Pullup vom Lichtschranken-Eingang angeschaltet

Dim X(51) As Byte ' In diesem Array werden die einzelnen Ziffern definiert

X(1) = &B11111111
X(2) = &B10000001
X(3) = &B10000001
X(4) = &B11111111
X(5) = &B00000000

X(6) = &B00100000
X(7) = &B01000000
X(8) = &B11111111
X(9) = &B00000000
X(10) = &B00000000

X(11) = &B01100111
X(12) = &B10001001
X(13) = &B10010001
X(14) = &B01100001
X(15) = &B00000000

X(16) = &B11100011
X(17) = &B10001001
X(18) = &B10011001
X(19) = &B11111111
X(20) = &B00000000

X(21) = &B00010000
X(22) = &B00110000
X(23) = &B01010000
X(24) = &B11111111
X(25) = &B00000000

X(26) = &B11100111
X(27) = &B10100001
X(28) = &B10100001
X(29) = &B10111111
X(30) = &B00000000

X(31) = &B00111111
X(32) = &B01001001
X(33) = &B10001001
X(34) = &B10000111
X(35) = &B00000000

X(36) = &B10000111
X(37) = &B10001000
X(38) = &B10010000
X(39) = &B11100000
X(40) = &B00000000

X(41) = &B11111111
X(42) = &B10010001
X(43) = &B10010001
X(44) = &B11111111
X(45) = &B00000000

X(46) = &B11110001
X(47) = &B10010001
X(48) = &B10010001
X(49) = &B11111111
X(50) = &B00000000


X(51) = &B00100100



On Timer0 Timerinterrupt ' Timer0 löst mit etwa 1800Hz aus. Bei jedem Interrupt wird die nächste Spalte angezeigt
Config Timer0 = Timer , Prescale = 8
Timer0 = 190
Enable Timer0
Enable Interrupts

On Timer1 Zeitinterrupt ' Timer 1 löst jede Sekunde aus und lässt die Sekunden/Minuten hochzählen
Config Timer1 = Timer , Prescale = 64
Timer1 = 49911
Enable Timer1

On Int0 Lichtinterrupt ' Bei diesem Interrupt wird die Anzeige neu aufgesetzt: Die momentanen Zahlen werden wieder neu dargestellt
Config Int0 = Rising
Enable Int0

Mn = 2

A = Str(b) ' Hier werden die Ziffern zerlegt und jeder Ziffer werden zwei Werte (Start-Stop) im Array zugeordnet
If B < 10 Then
U = 1
W = 6
Mn = 1
Goto Klein3
End If
O = Mid(a , 1 , 1)
N = Val(o)

U = N * 5
U = U + 1
W = U + 5

Klein3:
I = Mid(a , Mn , 1)
M = Val(i)

G = M * 5
G = G + 1
H = G + 5

Mn = 2
A = Str(z)
If Z < 10 Then
C = 1
V = 6
Mn = 1
Goto Klein4
End If
O = Mid(a , 1 , 1)
N = Val(o)

C = N * 5
C = C + 1
V = C + 5

Klein4:
I = Mid(a , Mn , 1)
M = Val(i)

S = M * 5
S = S + 1
F = S + 5

J = U ' Hier werden Kopien der Start-Stop-Werte angelegt.
E = W
T = G
P = H
Q = C
R = V
D = S
Y = F
K = 0


Do ' In der Hauptschleife wird nichts ausgeführt.
Loop

Timerinterrupt: 'Timer-Überlauf-Routine: Nächste Array Spalte anzeigen
Timer0 = 190


Portb = X(u) ' Die momentane Array-Spalte wird angezeigt
Incr U

If U = W Then ' Hier wird die zweite Ziffer der momentanen Zahl unter U und W gespeichert.
Incr K
U = G
W = H
End If

If K = 2 Then ' Hier wird die zweite Zahl unter U/W und G/H eingestellt.
Incr K
U = C
W = V
G = S
H = F
nop
nop
nop
nop
nop
For Lk = 1 To 5 Step 1 ' Hier wird der Doppelpunkt dargestellt.
Portb = X(51)
Next
End If

If K = 5 Then ' Wenn beide Zahlen dargesellt sind, wird der Port B abgeschaltet und die Interrupts von Timer1 gestoppt.
Portb = &B00000000
Disable Timer0
End If

Return

Lichtinterrupt: ' Lichtschranken-Interrupt-Routine: Im Array wieder von vorne beginnen

Timer0 = 190
Enable Timer0


U = J ' Die Variablen für die Zahlen werden aus den Kopien wiederhergestellt.
W = E
G = T
H = P

C = Q
V = R
S = D
F = Y

K = 0
Return

Zeitinterrupt: ' Wird jede Sekunde ausgelöst und lässt die Sekunden/Minuten hochzählen.
Timer1 = 49911
Incr Z

If Z = 60 Then ' Nach einer Minute wird zur Minuten-Variable eins dazugezählt
Z = 0
B = 0
Incr B
End If


Mn = 2

A = Str(b) ' Hier werden die neuen Start-Stopwerte der beiden Zahlen berechnet.
If B < 10 Then
U = 1
W = 6
Mn = 1
Goto Klein1
End If
O = Mid(a , 1 , 1)
N = Val(o)

U = N * 5
U = U + 1
W = U + 5

Klein1:
I = Mid(a , Mn , 1)
M = Val(i)

G = M * 5
G = G + 1
H = G + 5

Mn = 2
A = Str(z)
If Z < 10 Then
C = 1
V = 6
Mn = 1
Goto Klein2
End If
O = Mid(a , 1 , 1)
N = Val(o)

C = N * 5
C = C + 1
V = C + 5

Klein2:
I = Mid(a , Mn , 1)
M = Val(i)

S = M * 5
S = S + 1
F = S + 5

J = U ' Hier werden wieder die neuen Kopien angelegt, damit sie im Lichtinterrupt neu aufgesetzt werden können.
E = W
T = G
P = H
Q = C
R = V
D = S
Y = F
K = 0
Return
End 'end program