Hallo! Ich programmiere beruflich und kenne den 8051-Typ AT89C51CC03 von Atmel ziemlich gut, stehe hier aber vor einem Rätsel. Folgendes ist gegeben: 1. Timer 0 auf 20ms, Timer 1 auf 1ms, beide 16 Bit und auch sonst identisch konfiguriert. 2. Timer 0 hat Prio 0 und Bank 0, Timer 1 hat Prio 3 und Bank 3 3. Timer 0 dient als Zeitgeber für Zeitkreise >20ms 4. Timer 1 refresht ein 4stelliges LED-Display Wenn ich nun allen anderen Programmcode ausblende, so daß nur die beiden Timer laufen, in ihre Interruptroutinen springen, dort gestoppt, neu geladen und wieder gestartet werden, dann flackert die LED-Anzeige, weil Timer 1 "gestört" wird. Obwohl er die höchste Prio hat, entstehen Zeitlücken von ca. 20ms. Im Emulator (DProbeHS von Hitex) kann ich den Code stoppen und Timer 0 anhalten. Danach flackert die Anzeige nicht mehr und der Interrupt von Timer 1 wird konstant mit 1ms angesprungen. Diese Wechselwirkung ist mir und meinen Kollegen völlig neu und unverständlich. Alle anderen Interruptquellen wurden testweise ausgeschaltet und sind nicht die Ursache. Timer 0 scheint also Timer 1 zu stören, obwohl es von der Logik her nicht sein dürfte. Hat das schon mal jemand erlebt und dafür eine Lösung gefunden? Wäre sehr dankbar, denn wir zerbrechen uns hier schon seit zwei Tagen den Kopf darüber.
Wenns flackert, entspricht der Code nicht Deiner Umschreibung. Den echten Code posten ist daher immer besser. Einige Tips: 1. Laß das Bank Zuweisen weg, wenn Du dafür keinen triftigen Grund nennen kannst. Du mußt dann alle daraus aufgerufenen Funktionen auch auf die Bank setzen bzw. bankunabhängig machen. In der Regel spart es auch nichts. 2. Es ist ungenau, dem Timer einen festen Wert zuzuweisen, denn damit wird die Interruptlatenz vergessen. Dein Timer läuft also prinzipiell langsamer. Abhilfe: Du addierst den Wert zum Timer. Der Timer enthält nämlich exakt die nach dem Überlauf bis zur Behandlung vergangene Zeit. Die für die Addition nötige Zeit (entnimmt man dem Assemblerlisting) muß man noch abziehen. Peter
1ms für einen Display-Refresh? Wozu das denn? Oder wird das Display gemultiplext? Timer 0 und Timer 1 werden über die gleichen Register gesteuert, kann es sein, dass hier was schief läuft? Ralf
Danke für die Tips. Peter Dannegger schrieb: > 1. > Laß das Bank Zuweisen weg, wenn Du dafür keinen triftigen Grund nennen > kannst. > Du mußt dann alle daraus aufgerufenen Funktionen auch auf die Bank > setzen bzw. bankunabhängig machen. > In der Regel spart es auch nichts. Ums Sparen geht's hier nicht. Die unterschiedlichen Bänke für Interruptroutinen sind nötig. Vor allem, wenn Prio 3 die Prio 2 unterbricht. Ich kann Timer 1 natürlich nicht sperren, wenn der Prozessor im Interrupt von Timer 0 ist. Das paßt schon. Mache das nicht zum ersten Mal. > 2. > Es ist ungenau, dem Timer einen festen Wert zuzuweisen, denn damit wird > die Interruptlatenz vergessen. Dein Timer läuft also prinzipiell > langsamer. > Abhilfe: Du addierst den Wert zum Timer. > Der Timer enthält nämlich exakt die nach dem Überlauf bis zur Behandlung > vergangene Zeit. > Die für die Addition nötige Zeit (entnimmt man dem Assemblerlisting) muß > man noch abziehen. Das ist nicht das Problem. Natürlich mache ich das so. Hmm, vielleicht drücke ich mich unklar aus.
Ralf schrieb: > 1ms für einen Display-Refresh? Wozu das denn? Oder wird das Display > gemultiplext? > > Timer 0 und Timer 1 werden über die gleichen Register gesteuert, kann es > sein, dass hier was schief läuft? > > Ralf Gemultiplext, ja. Macht man meistens so. Du meinst die SFRs? Nein, läuft nichts schief. Wir machen das schon ein paar Jahre und kennen diesen Atmel in- und auswendig. Nur, so ein Problem hatten wir nie. Man kann mit den paar Registern nicht viel falsch machen. Außerdem ist der Hitex-Emulator in der Lage, mir anzuzeigen, was die Register darstellen. Und da stimmt alles. Nochmal in anderen Worten: Prozessor startet -> Timer werden initialisiert und gestartet -> Timer 1 Interrupt schreibt pro Durchlauf eins der 4 LED-Segmente, also 4ms für alle (250Hz) -> Anzeige flackert mehrfach pro Sekunde weil der Timer 1 in seiner Ausführung gestört wird -> Prozessor anhalten, Timer 0 stoppen -> Timer 1 läuft ohne Störung und Anzeige flackert nicht mehr. Zusatz: Selbst wenn ich alle anderen Interrupts ausschalte, also auch den von Timer 0, flackerts. Das entsteht nur dadurch, daß beide Timer gleichzeitig laufen.
Ralf schrieb: > 1ms für einen Display-Refresh? Wozu das denn? Oder wird das Display > gemultiplext? Ach so, ganz vergessen. Mehrere Gründe: zum Einen teste ich noch, zum Anderen läßt sich damit die Helligkeit regeln. Sind bei 1ms eh schon zu hell. Ist halt ne Softwarelösung, damit man die Hardware nicht nachträglich ändern muß. Leider hatte damals keiner einen PWM zur Helligkeitssteuerung hergenommen.
wenn das so ist wie du beschreibst kann das nur daran liegen, dass dein Display Interrupt nicht auf der hohen Priorität läuft. Lösungen: Entweder die Prioritäten richtig vergeben oder den Display Interrupt an Timer 0 hängen. Der Fehler kommt meines Erachtens daher, dass dein 20ms Interrupt den Mux Interrupt unterbricht. Wir Peter bin ich allerdings auch der Meinung, das ein Regbank Switch nur in Ausnahmefällen was bringt. Thomas
1 | #define RELOAD_T1_LOW (RELOAD_T1%256)
|
2 | #define RELOAD_T1_HIGH (RELOAD_T1/256)
|
3 | |
4 | static void ISR_Timer1(void) interrupt TIMER1_vektor using TIMER1_USING_REG |
5 | {
|
6 | TR1=0; |
7 | TL1+=RELOAD_T1_LOW; |
8 | if(!CY) |
9 | {
|
10 | TH1+=RELOAD_T1_HIGH; |
11 | }
|
12 | else
|
13 | {
|
14 | TH1+=(RELOAD_T1_HIGH+1); |
15 | }
|
16 | TR1=1; |
17 | v_Refresh_Display(); |
18 | }
|
Der Code für Timer 0 ist vom Reload her identisch.
Thomas schrieb: > wenn das so ist wie du beschreibst kann das nur daran liegen, dass dein > Display Interrupt nicht auf der hohen Priorität läuft. > > Lösungen: > Entweder die Prioritäten richtig vergeben oder den Display Interrupt > an Timer 0 hängen. > Thomas Danke für die Hilfe, aber bitte richtig lesen. Timer 1 hat Prio 3 und Timer 0 hat Prio 0. Werde morgen mal einen Screenshot von Hitop einbinden, bin gerade auf einem anderen Rechner.
Hitop-Screenshot, wo man Prio und Timer-Settings sehen kann. Da nicht jeder Hitop/Hitex kennt: das ist, zusammen mit der Hardware DProbeHS ein 100%iger Emulator. Daher kann ich auch den Prozessor anhalten und z. B. Timer 0 stoppen/starten.
Maik Staberock schrieb: > Ums Sparen geht's hier nicht. Die unterschiedlichen Bänke für > Interruptroutinen sind nötig. Vor allem, wenn Prio 3 die Prio 2 > unterbricht. Das ist Quatsch. Die Bankzuweisung sagt dem Compiler nur, daß er die Register nicht sichern muß. Bringt aber nur was, wenn der Interrupt auch viele Register benötigt. Die Bankzuweisung kann aber böse Seiteneffekte haben, wenn der Interrupt Unterfunktionen aufruft. Und wenn verschiedene Level die gleiche Bank benutzen. Peter
Maik Staberock schrieb: > Zusatz: Selbst wenn ich alle anderen Interrupts ausschalte, also auch > den von Timer 0, flackerts. Das entsteht nur dadurch, daß beide Timer > gleichzeitig laufen. Dann mußt Du mal ein komplettes flackerndes Programm zeigen. Ich benutze auch regelmäßig beide Timer und die haben sich noch nie beeinflußt. Ich hab auch schon T0,T1 im 3-Timer Mode benutzt (TL0,TH0,T1), keine Probleme. Hast Du alle 5 Timer des CC03 in Benutzung (T0,T1,T2,PCA,CANTIM)? Peter
Peter Dannegger schrieb: > Das ist Quatsch. Die Bankzuweisung sagt dem Compiler nur, daß er die > Register nicht sichern muß. Bringt aber nur was, wenn der Interrupt auch > viele Register benötigt. Ahso. Dann machen wir Programmierer das sicher nur seit Jahren falsch. Warum macht man eine Bankzuweisung bei einem Interrupt? Weil ein Interrupt, der eine niedrigerpriorisierten unterbricht, dessen Registerdaten verändern würde! Warum sonst? Das kann der Compiler nicht wissen. Natürlich verwenden nur gleiche Level die gleiche Bank. Und für die Unterroutinenaufrufe haben wir ja noch #pragma NOAREGS. Das paßt schon, seit Jahren. Mit mehreren Leveln und vielen Hardware-Interrupts, auf vielen Geräten. Die Bankzuweisung mag dem Compiler zwar sagen, daß er die Register nicht sichern muß, aber a) isses sicherer so und b) schneller, weil er nur die Bank umschalten muß und nicht die ganze Registerbank auf den Stack schieben. Abgesehen davon ist das nicht das Problem hier. Sonst wäre das einfacher zu lösen.
Kann Peter nur zustimmen, eine Bankumschaltung sollte man sich schenken.
> Wir machen das schon ein paar Jahre und kennen diesen > Atmel in- und auswendig. Nur, so ein Problem hatten wir nie. Okay, bezweifelt hier auch keiner, dass Erfahrung vorhanden ist (ein Anfänger hat sicher keinen Zugriff auf einen Emulator :) Aber ich habe ebenfalls jahrelange Erfahrung, und bin auch schonmal auf die einfachen Sachen nicht gekommen ;) Deswegen frage ich einfach mal, ob Punkt 3 aus http://www.atmel.com/dyn/resources/prod_documents/doc4293.pdf hier eine Rolle spielen könnte? Ralf
Peter Dannegger schrieb: > Dann mußt Du mal ein komplettes flackerndes Programm zeigen. Nee, das wäre zuviel für hier. Ich schrieb doch oben, daß ich alles andere abschalten kann im Emulator, meinetwegen auch per Code. Der Effekt ist derselbe. Es laufen nur noch Timer 1 und Timer 0. Interrupts von anderen Quellen stören nicht, aber selbst wenn nur Timer 0 Interrupt erlaubt ist, tritt der Fehler noch auf. Wie gesagt, er tritt auf, sobald Timer 0 nur läuft. > Hast Du alle 5 Timer des CC03 in Benutzung (T0,T1,T2,PCA,CANTIM)? Nein. CAN ist momentan deaktiviert, PCA läuft auf 1/2 Clock. T2 kann ich anhalten -> der Fehler bleibt. Sobald ich T0 stoppe, flackert nichts mehr. Das "Flackern" äußert sich in ungleichmäßiger Dauer (10-20ms sind die LED-Anzeigen aus) und Abständen, tritt aber mehrmals pro Sekunde auf.
Maik Staberock schrieb: > Ahso. Dann machen wir Programmierer das sicher nur seit Jahren falsch. Nicht direkt falsch. Wenn der Interrupt nur 2 Register verwendet, wird trotzdem eine ganze Bank reserviert. Du verschenkst also 6 Byte (5%) schnellen direkten SRAM, die woanders vielleicht nützlicher wären. > Warum macht man eine Bankzuweisung bei einem Interrupt? Weil ein > Interrupt, der eine niedrigerpriorisierten unterbricht, dessen > Registerdaten verändern würde! Warum sonst? Das kann der Compiler nicht > wissen. Falsch! Der Compiler sichert alle verwendeten Register. Erst mit der Bankzuweisung schaltest Du das ab. Du bist dann aber in der Pflicht, Dich selber darum zu kümmern, daß keine andere Priorität die selbe Bank benutzt. Auch hast Du Dich darum zu kümmmern, daß Unterfunktionen nicht versehentlich Bank 0 annehmen. Du hast dem Compiler ja die Registersicherung abgeschaltet. > Und für die > Unterroutinenaufrufe haben wir ja noch #pragma NOAREGS. Einmal vergessen und Peng. Ich bin da mal mit reingefallen, seitdem lasse ich das. Der Compiler macht keine Schusselfehler. Also ist es deutlich sicherer, ihm die Registerverwaltung auch im Interrupt zu überlassen. > Abgesehen davon ist das nicht das Problem hier. Sonst wäre das einfacher > zu lösen. Ohne Code lassen sich solche Probleme fast nie lösen. Peter
@Peter Da ist was dran. Naja, mein Softwareleiter sieht das sicher anders, aber so wie wir es machen, geht's auch. Dein System ist nur einfacher.
Hallo, was ich nicht verstehe: ein Hitex-Emulator kann doch sicher tracen, meiner konnte das schon vor 25 Jahren. Also mach ein Trace vom Programmablauf, da kannst du zweifelsfrei nachlesen, ob während der ISR von Timer 1 ein Timer 0 Interrupt auftritt, bzw. ob aus T1 ISR in T0 ISR gesprungen wird. Warum hier lange diskutieren, wenn du nur nachschauen müsstest? Gruss Reinhard
Ralf schrieb: > Deswegen frage ich einfach mal, ob Punkt 3 aus > http://www.atmel.com/dyn/resources/prod_documents/doc4293.pdf hier eine > Rolle spielen könnte? Oh-oh... Das war es. Wie du schon sagtest, manchmal kommt man auf die einfachsten Sachen nicht. Shame on me! Super, vielen Dank euch! Selbst mein Softwarechef ist da nicht drauf gekommen.
Reinhard Kern schrieb: > Hallo, > > was ich nicht verstehe: ein Hitex-Emulator kann doch sicher tracen, Ja, mit der DBox zusammen. Aber die ist sehr umständlich und da ich die so gut wie nie brauche, kenne ich mit damit nicht aus.
Warum 2 Timer Interrupts ? Das ganze kann man doch auch mit dem 1mS Timer Interrupt erschlagen. Dort eine Variable bis 20 zaehlen lassen und dann den Code fuer die 20mS Routine ausfuehren. Und schon hast du ein Problem weniger. Gruss Helmi
Helmut Lenzen schrieb: > Warum 2 Timer Interrupts ? > Das ganze kann man doch auch mit dem 1mS Timer Interrupt erschlagen. > Dort eine Variable bis 20 zaehlen lassen und dann den Code fuer die 20mS > Routine ausfuehren. Und schon hast du ein Problem weniger. Generell eine gute Idee. Aber wenn die Ausführung des Codes vom 20ms Timer nun selbst 2ms dauern würde, dann... ;o)
Maik Staberock schrieb: > Generell eine gute Idee. Aber wenn die Ausführung des Codes vom 20ms > Timer nun selbst 2ms dauern würde, dann... ;o) Dann nuetzen dir auch 2 Timer nix. Denn die Zeit muss ja irgendwo herkommen. Der Prozessor teilt sich ja nicht auf. Und genau da wird dein flackern herkommen. Nur das das mit einem Timer immer syncron auftritt. Du muesstes dann die eine Routine die 2mS verbraucht in kleinere Haeppchen zerlegen und die in den 1mS verschachteln. Gruss Helmi
@Helmi Mein Problem ist doch gelöst, wie ich oben schrieb. Es ist zwar unlogisch, dieses Verhalten, aber ein Bug des Prozessors. Zu deiner Aussage: Klar teilt sich der Prozessor auf, sozusagen. Der Interrupt mit Prio3 unterbricht den Interrupt mit Prio 0. Wenn ich im Interrupt des 20ms-Timers Code aufrufe, dessen Ausführung, sagen wir mal, 5ms benötigt, dann würde dein Vorschlag nicht funktionieren. Der separate 20ms-Timer hat damit kein Problem. Bei mir ist der 1ms-Timer höherpriorisiert und nur für das Refreshing der Anzeige zuständig. Der 20ms Timer wiederum ist Zeitbasis für Zeitkreise bis 65535 * 20ms, was einen guten Spielraum darstellt. Der 1ms-Timer ist jetzt neu in Gebrauch, um die Anzeige ruhiger zu machen. Vorher wurde die einmal pro Main()-Durchlauf aktualisiert und der ist ca. 2ms. Da es aber nicht immer genau 2ms waren, traten leichte Helligkeitsschwankungen auf. Jetzt nicht mehr.
Maik Staberock schrieb: > Der 1ms-Timer ist > jetzt neu in Gebrauch, um die Anzeige ruhiger zu machen. Vorher wurde > die einmal pro Main()-Durchlauf aktualisiert und der ist ca. 2ms. Da es > aber nicht immer genau 2ms waren, traten leichte Helligkeitsschwankungen > auf. Jetzt nicht mehr. Ich habs noch nie probiert, ne Multiplexanzeige ohne Timerinterrupt zu machen. Ich kenne derartige Probleme daher nicht. Man sieht aber öfters Fragen von Anfängern, warum es flackert oder Digits unterschiedlich hell sind. Ein Blick auf die Source verrät dann sofort, warum. In der Regel gehen damit auch Probleme bei der Tastenentprellung Hand in Hand. Die Erratas mit der Takteinstellung sind mir auch schon aufgefallen. Ich betreibe daher immer CPU und Peripherie mit der selben Taktrate. Peter
Helmut Lenzen schrieb: > Dann nuetzen dir auch 2 Timer nix. Denn die Zeit muss ja irgendwo > herkommen. Der Prozessor teilt sich ja nicht auf. Der höher priorisierte Interrupt nimmt sie sich einfach. Er kann beliebig oft das Main und alle niedrigeren Interrupts unterbrechen. Das ist anders, als z.B. beim AVR. Da müssen sämtliche Interrupts aufeinander Rücksicht nehmen. Keiner kann einen anderen unterbrechen. Die maximale Interruptlatenz ergibt sich beim AVR daher aus der Summe sämtlicher Interrupthandler. Da gilt also ausnahmslos: fasse Dich kurz. Peter
Peter Dannegger schrieb: >> Der höher priorisierte Interrupt nimmt sie sich einfach. > Er kann beliebig oft das Main und alle niedrigeren Interrupts > unterbrechen. > Das ist anders, als z.B. beim AVR. Da müssen sämtliche Interrupts > aufeinander Rücksicht nehmen. Keiner kann einen anderen unterbrechen. Hallo Peter, das ist ein wichtiger Unterschied in verschiedenen Architekturen (und oft ein übersehenes Problem), aber man ist dem nicht auf Gedeih und Verderb ausgeliefert. Man kann z.B. beim mit etwas Tricks auf dem Stack die ISR gleich wieder verlassen, aber an eine Programmstelle zurückkehren, die als normales Unterprogramm ausgeführt wird (und tut was die ISR tun soll) und an die unterbrochene Stelle des Hauptprogramms zurückkehrt. Dabei kann man Interrupts wieder zulassen und so nested Interrupts per Software erzeugen, die der Prozessor garnicht kann. Voraussetzungen: 1. genaues Verständnis der Interrupt Steuerung und Priorisierung 2. genaue Kenntnisse über Subroutinen/ISR-Aufrufe, Rückkehr und Stackverwaltung dabei. Umgekehrt ist es einfacher: man kann ja zum Beginn einer ISR alle IRQs abschalten und erst am Ende wieder zulassen. Klar ist, dass ein Programm mit nested Interrupts wesentlich komplexer abläuft und daher auch viel mehr Fehler enthalten kann. Gruss Reinhard
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.