Mit untenstehenden Code versuche ich mein 2-stelliges Display anzusteuern. Inhaltlich stimmt alles. Ziffern von 00 - 99 werden richtig angezeigt. Aber die Ziffer der Einerstelle ist auch leicht (ca. 10% Helligkeit) auf der 10er Stelle zu sehen, und andersrum.
Es stimmt irgendwie das Timing meiner Routine nicht. Habe versucht, durch tauschen der Port-Ausgabezeilen erst die andere auszuschalten, und dann die andere einzuschalten. Aber es ändert sich hierbei nichts.
Die Frequenz ist im Timer1 so eingestellt:
1
//zweite Möglichkeit einen Portpin zu toggeln:
2
if(blinkmerker) blinkmerker=0;
3
else blinkmerker=1;
4
// Die Auswertung von "blinkmerker" wird im Hauptprogramm vorgenommen
5
6
TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen,
7
// da der Timerroutine bei einem Überlauf aufgerufen wird
8
display_ansteuern();
9
10
11
//*********************************
12
void display_ansteuern(void)
13
{
14
// Wenn "blinkmerker" =1 dann wird der PortB1 gesetzt, ansonsten wird er zurückgesetzt.
15
if (blinkmerker)
16
{
17
//PORTB &=~_BV(6);
18
PORTD |= _BV(6);
19
PORTD &=~_BV(7);
20
PORTB = array[(counter % 10)]; // Einerstelle von 'counter' ausgeben
Naja, ich möchte halt die führende Null auch ausgeben. Bei Anzeige Null war bis hierher dann eben nur eine Null. Aber dann ist dieses Digit dann heller, weil er das 10er Dgit nicht ansteuern muss. Ich muss glaub ich auch wenn nix am Display stehen soll das Bitmuster 'leer' einblenden. Aber das später.
Es ändert hier erstmal nichts um die Doppel-Zahl mit 10% Intensität zu lösen.
Viel ist davon nicht zu sehen, aber oft vergessen Leute das 'volatile' bei Variablen, wenn diese anschließend in Interruptroutinen und Hauptprogramm verwendet werden müssen.
Naja, Du mußt schon die Kausalität beachten.
Erst alle Digits dunkel, dann neues Pattern und dann das Digit an.
Oder Pattern dunkel, Digit wechseln, neues Pattern an. Die 3 Schritte müssen die richtige Reihenfolge haben.
Du machst auch ständig teure Softwaredivisionen im Interrupt, das kostet Zeit und solange ist noch das falsche Pattern an.
Man zerlegt den Wert nur einmal, wenn er sich geändert hat und legt die Pattern im Bild-RAM ab. Der Interrupt muß dann nur das jeweilige Byte aus dem RAM ausgeben.
Mach aus „blinkermerker“ statt true/false, 0 bis 5, zB.
If blinkermerker (lustiger Variablenname) 1 bis 4, dann mache nix, bis auf d6 und d7 auf Low zu setzen. (Oder halt auf HIGH; so dass beide Stellen „aus“ sind.) Man könnte auch bei blinkermerker&1 beide Stellen inaktiv schalten.
Was der Atmega bei %10/10 zu rechnen hat, ist mehr, als man meint. Würde ich mir auch noch mal ansehen und ggfls. ändern oder ausm interrupt in die Main verlagern. Kann mich jetzt aber auch irren, weil der komplette Code leider nicht vorliegt. Sonst könnte man sich das im Listing mal ansehen.
Viel Spaß noch.
Ghosting ist das richtige Schlüsselwort. Das hat bei dir 3 Ursachen:
Du schaltest auf das andere Segment um, bevor dessen Wert auf Port B ausgegeben wird. Lösung: Schalte zuerst beide Segmente aus, gebe dann den Wert auf Port B aus, schalte dann das richtige Segment ein.
Der Effekt 1 wird durch die zeitaufwändige Division verstärkt. Berechne den Wert vor der Ausgabe, dann spielt die benötigte Zeit keine Rolle.
Die beiden Transistoren, mit denen du die gemeinsamen Anoden bzw. Kathoden der 7-Segment anzeigen ansteuerst, brauchen Zeit zum Abschalten. Vor allem dann, wenn sie wie üblich (und das ist kein Fehler) übersteuert werden. Wenn du den Vorschlag zu 1 umsetzt, hast du wahrscheinlich schon genug tot-zeit zwischen den beiden Segmenten. Notfalls kannst du ein paar NOP Befehle einfügen.
Die beiden Transistoren, mit
denen du die gemeinsamen Anoden bzw. Kathoden der 7-Segment anzeigen
ansteuerst brauchen Zeit zum abschalten.
Ohne den Schaltplan zu kennen, ist diese Behauptung gewagt. Bei mir schalten die Transistoren schnell genug, da müssen keine zusätzlichen Pausen eingefügt werden.
Ghosting liegt oft nur an einem falschen Programmablauf.
Wenn man mit 1mcd mittlerer Helligkeit pro Segment auskommt, denn der Segmentstrom beträgt auf Grund der 332 Widerstände nur 6mA und teilt sich auf 4 Stellen, 100 Ohm für 20mA peak wären möglich ohne Schaltungsänderung für diese low current Spezialdisplays.
16 Takte für Multiplexing ist wohl ausgeschlossen, zumal in C. Aber meine Vermutung geht ebenfalls in Richtung
... zu hoch. Natürlich gibt es da Ghosting.
Dass nämlich dieses 'TCNT1 = 65536-15' in der ISR steht, nach der Rückkehr aus dieser noch zwei oder drei Befehle im Hauptprogramm ausgeführt werden und dann schon wieder der Interrupt erfolgt.
Mir ist schon klar, dass hier ein Autoreload-Modus sinnvoll wäre. Aber darum ging es mir bei den Ausführungen zum Thema "16-Bit-Integer" nicht. Sondern ums Verständnis, dass bei einem (unsigned) 16-Bit Integer eben 65536 exakt das Selbe ist wie 0. Und dass im Falle des Zählers hier eben das 17. Bit genau das Overflow-Flag ist.
bin erst heim gekommen.
Habe mal den entsprechenden Teil-Plan hier angehängt, und auch den Code, so er mom ist. Er erhebt noch keinen Anspruch auf fehlerfrei. Alles noch Test und in der Entstehung.
Das mit erst dunkeltasten, Digit einlesen und wieder darstellen leuchtet mir ein. Das der Atmega hier aber ein Päuschen braucht, hätte ich nicht vermutet.
Das der Atmega hier aber ein Päuschen braucht, hätte ich nicht
vermutet.
Der brauch es nicht, aber die Anzeigen. Break before make! Man muss ja auch erst eine Schallplatte anhalten, bevor man sie wechseln kann! (Jaja, ein sehr anachronistisches Beispiel).
Habe gerade in den Fuses nachgeschaut. Der läuft auf default mit 1 MHz.
Mit der Einstellung im Timer nehme ich ein leichtes Flackern der Anzeige war. Dies dürften so ca. <40 Hz sein.
bin erst heim gekommen.
Habe mal den entsprechenden Teil-Plan hier angehängt, und auch den Code,
so er mom ist. Er erhebt noch keinen Anspruch auf fehlerfrei. Alles
noch Test und in der Entstehung.
Hier mal ein paar Anmerkungen.
Man muss nicht alle globalen Variablen als volatile deklarieren, nur
die, welche sowohl im Hauptprogramm und Interrupt verwendet werden.
Ein Array mit dem Namen array ist doof. Apfelmus ist Mus aus Äpfeln.
Deine Einrückungen der Klammern ist nicht sonderlich sinnvoll.
Nahezu jede Zeile mit einem Kommentar zu versehen ist Unsinn, selbst für
Anfänger und als Lehrbeispiel ist das fragwürdig.
Um Pulse per Software zu erkennen und auszuwerten, muss man MINDESTENS
eine Flankenerkennung einbauen. Wartepausen sind bääääh.
Kurze Funtionen schreibt man besser direkt in die ISR, das spart bei
sehr hohen ISR-Frequenzen wertvolle Takte. Hier ist das egal.
R2 und R4 sind mit 10k ein wenig zu hochohmig, da sollte man eher 1k
benutzen.
Eingänge entprellt man entweder in Software oder Hardware,siehe
Entprellung.
Hallo Forengemeine, Mitwirkende, bin erst heim gekommen.
Habe mal den entsprechenden Teil-Plan hier angehängt, und auch den Code,
so er mom ist. Er erhebt noch keinen Anspruch auf fehlerfrei. Alles
noch Test und in der Entstehung.
Ich habe leider nicht die Zeit, den ganzen Code bis ins Detail zu analysieren, aber wenn sich am Ende herausstellt, dass es kein Softwarefehler ist, dann könnte es auch ein Hardwarefehler sein oder eine Kombination aus beidem. Ich hatte vor vielen Jahren jemandem geholfen, eine Schaltung zu verbessern, und das sieht bei Dir so ähnlich aus. Aber warten wir mal ab, wie weit Ihr kommt.
PS: wenn man irgendwann mal anfängt, „#define” im Code für immer wiederkehrende Ausdrücke – für z.B. Tasterabfragen und Beeinflussung der Portpins – zu benutzen, wird dieser um Welten leserlicher; für einen selbst und auch für andere
wenn man irgendwann mal anfängt, „#define” im Code für immer
wiederkehrende Ausdrücke – für z.B. Tasterabfragen und Beeinflussung der
Portpins – zu benutzen, wird dieser um Welten leserlicher; für einen
selbst und auch für andere
Zugleich werden Fehlermeldungen schwerer lesbar. Lieber ganz normale Funktionen schreiben. Wenn sie klein sind (z.B. Einzeiler) wandelt der Compiler sie in kompakten inline Code um, verwendet in Fehlermeldungen aber weiterhin den Namen der Funktion.
Dann machst Du vermutlich etwas falsch – kann passieren.
Lieber ganz normale Funktionen schreiben.
Funktionen verbrauchen in der Regel wesentlich mehr Ressourcen als Macros, das kann aber auch mal genau umgekehrt ausfallen – man sollte Funktionen dort verwenden, wo sie sinnvoll sind, und analog dazu – Macros dort einsetzen, wo sie Sinn machen. Zu wissen, wo was wann am besten eingesetzt werden sollte, ist eine Kunst, die nicht jeder beherrscht. Eine Rossource kann übrigens sowohl Speicher als auch Zeit sein – je nachdem was gerade relevant ist.
Wenn sie klein sind (z.B. Einzeiler) wandelt der Compiler sie in
kompakten inline Code um, verwendet in Fehlermeldungen aber weiterhin den
Namen der Funktion.
Man sollte es nach Möglichkeit nicht dem Compiler überlassen, was als Inlinecode und was als nicht-Inlinecode kompiliert/übersetzt wird – dazu gibt es spezielle Methoden und Programmiereingriffe, ansonsten wird das Produkt zu einem Zufallsprodukt. Dass das nicht jeder beherrscht, kann ich aber gut nachvollziehen.
Funktionen verbrauchen in der Regel wesentlich mehr Ressourcen als
Macros, das kann aber auch mal genau umgekehrt ausfallen
So sehr das in kritischen Anwendungsfällen zutreffen mag, so vollkommen irrelevant ist es hier. Es gibt keinen Grund, sich hier durch "premature optimization" ein Bein zu stellen und das andere am Kühlschrank festzukleben.
Es gibt keinen Grund, sich hier durch "premature
optimization" ein Bein zu stellen und das andere am Kühlschrank
festzukleben.
Man sollte aber schon den Programmablauf durchdenken, ob Funktionen unnötig oft ausgeführt werden. Das ständig neue Zerlegen einer Variablen in Ziffern in jedem Interrupt gehört dazu.
In der Praxis wird das Ausgabeinterval oft sogar verlangsamt, so daß max 2..5 Änderungen je Sekunde angezeigt werden. Das ergibt dann eine ruhige Anzeige und der Benutzer kann jeden Wert auch ablesen.
So sehr das in kritischen Anwendungsfällen zutreffen mag, so vollkommen
irrelevant ist es hier. Es gibt keinen Grund, sich hier durch "premature
optimization" ein Bein zu stellen und das andere am Kühlschrank
festzukleben.
Das hat leider nichts mit kritischen Anwendungsfällen oder Optimierung zu tun und relevant ist es leider immer – je früher man es lernt oder zumindest damit schon mal in Kontakt kommt, was ich kurz erwähnt habe, desto besser.
je früher man es lernt oder
zumindest damit schon mal in Kontakt kommt, desto besser.
Sehe ich nicht so. Man muss sich nicht verdrehen, um Code zu optimieren, nur weil man davon ausgeht, daß der verwendete Compiler das nicht besser kann, als es der Compiler, den man 1990 schon kennengelernt hat, konnte.
Das ganze erzeugt nur schlecht lesbaren und schlecht wartbaren Code, der wiederum Anfängern das Erlernen erschwert.
Bevor man etwas optimiert, sollte man das Funktionsprinzip, den Algorithmus verstanden haben. Dann kann man auch selbst beginnen, zu verstehen, /wann/ und wo eine Optimierung überhaupt nötig ist.
Dazu kommt, daß auch simple 8-Bit-Architekturen wie die hier verwendeten AVRs um einiges schneller sind als das, was man in den 80ern mit den ersten primitiven Compilern traktierte (6502/Z80 etc.), und andererseits die Aufgaben oft viel simpler -- hier geht es um ein simples 7-Segment-Display, nicht um ein Computerspiel, das möglichst viel aus der knappen Graphikhardware rausholen soll.
Peters Einwand mit dem Krempel, den man in einer ISR treibt bzw. nicht treiben sollte, ist eine komplett andere Baustelle (und die würde sich auch nicht bessern, wenn man den Compiler mit Macros totwerfen würde).
Wie ich bereits erwähnt habe, es geht nicht ums Optimieren etc, sondern um eine gewisse Struktur des Codes zu erhalten – optimieren kann man später, nachdem alles funktioniert, immer noch, wenn man will. Das ist ein ganz anderer Vorgang.
Habe mal den entsprechenden Teil-Plan hier angehängt
R2 und R4 sind viel zu gross, AVCC sollte angeschlossen werden, 100nF parallel zum NPN können den NPN killen.
Die Schaltung schaltet die Digits mit low durch also:
1
if(blinkmerker)
2
{
3
PORTD|=_BV(7);// 10er aus
4
PORTB=array[counter%10];
5
PORTD&=~_BV(6);// 1er an
6
}
7
else
8
{
9
PORTD|=_BV(6);// 1er aus
10
if(counter>=10)
11
PORTB=array[(counter/10)%10];
12
else
13
PORTB=0;// dunkel
14
PORTD&=~_BV(7);// 10er an
15
}
aber was gibt man sich Mühe dein Programm zu korrigieren, es ist dir offenbar scheissegal was die Leute hier schreiben, du erwartest wohl dass jemand kommt und dir eintippt, denn ausprobiert hast du bisher nichts, postet immer noch denselben code wie zu Beginn.
aber was gibt man sich Mühe dein Programm zu korrigieren, es ist dir
offenbar scheissegal was die Leute hier schreiben, du erwartest wohl
dass jemand kommt und dir eintippt, denn ausprobiert hast du bisher
nichts, postet immer noch denselben code wie zu Beginn.
Was meckerst Du rum?
Es geht mir momentan nur um das Display Problem, und wie ich es lösen kann.
Ich gehe schließlich noch in die Arbeit, umnd komme so wie heute erst jetzt nach hause.
Ich lese schon mit, kann aber nicht so ganz flüssig am Abend gleich Code hämmern, da ich kaputt bin vom Tag.
So nun zum Thema.
Das 'array' hab ich mal umbenannt. In 'Digits' wenns passend erscheint. Den AVCC habe ich in der Schaltung schon auf +Ub, nur im Plan war er nicht eingezeichnet
Die ISR-Routine, habe ich verstanden, halte ich kurz. Der Code dazu wird noch umgekrempelt, wenn das Display richtig funktioniert. Außer der Fehler liegt gleich hierin.
Ich habe mir jetzt die beiden Codes (Original und von Falk) etwas genauer angeschaut – jeder hat so seine eigene Präferenzen, was das Programmieren angeht, ich persönlich würde aber diese Divisionoperationen komplett rausnehmen bzw. umgehen, indem ich sie durch einfache Addition ersetze. Na wie auch immer – ich bin gespannt wie die nächste Version zusammengewürfelt und -gedichtet wird. CTC für den Timer1 mit OCR1A als TOP auf jeden Fall beibehalten, Modus 0 ist hier eine Sackgasse.
Hi
Versuche mal, deine Ausgabe vom Programm zu trennen. Dazu braucht es nicht viel, gibt dir aber die Möglichkeit, jederzeit eine Änderung ohne Aufwand einzupflegen. Du schreibst die Werte, die du ausgeben möchtest, in einen Speicher. (Variablenarray). Bei Änderung oder auch zyklisch holst du die Werte nacheinander aus dem Variablenarray, addierst den Wert auf die Anfangsadresse eines anderen Variablenarray, wo du die Segmente der Anzeige abgelegt hast (Matrixspeicher)und schreibst die so adressierte Matrix in einen Ausgabespeicher an die entsprechende Stelle. Ist relativ einfach, Adresszeiger auf Wertearray setzen, Wert Codieren und in Ausgabepuffer über den Adresszeiger schreiben.
Da ich C nicht beherrsche, kann ich dir keinen Programmcode liefern, aber das Prinzip sollte problemlos umsetzbar sein.
Bei der Ausgabe wiederum arbeitest du komplett unabhängig vom Hauptprogramm in einer Interruptroutine, die du jede ms aufrufst. Dabei brauchst du lediglich einen Zeiger auf die Adresse des Ausgabepuffers, also einen Zähler für die adressierte Matrix und ein Byte, wo die Selektierung der Anzeige durchgeschoben wird. Dieses Selektierungsbit kannst du mit einer Und-Funktion mit einem Null-Byte ausblenden und so die Anzeige dunkel schalten. Egal, ob du eine gemeinsame Kathode oder Anode hast, die Anpassung zur Ausgabe geschieht erst bei der Zuweisung an den Port. Das macht es dir leichter, wenn du z.B. eine gemeinsame Kathode hast und dann mit der umgekehrten Logik arbeiten musst. Ändert sich der Anzeigebaustein, brauchst du nur die Anpassung ändern.
Ich hoffe, meine Skizze und die Erklärung waren hilfreich.
Gruß oldmax
Du bist ja ein gaaaaanz Schneller! Das Thema ist erstens schon lange
erledigt und 2. auch schon mit sinnvollem Quelltext versehen.
Na ja, schnell muss ich nicht mehr sein. Das sind andere und das ist auch ihr gutes Recht. Ob schnell sein immer richtig ist lass ich einfach mal im Raum stehen. Ich hab halt gelesen, das die Diskussion von einem Anfänger eröffnet wurde und manchmal ist auch eine Information am Rande wertvoll. Ein Profi wie du hat mit der Lösung kein Problem und die Lieferung von sinnvollem Quelltext ist ja auch nicht zu bemängeln. Aber du triffst ganz speziell diesen Fall. Da kann man direkt warten, wann die Frage kommt " ich habe eine 4, 6 oder 8 stellige Anzeige...."
Na ja, egal, ich brauch halt immer etwas mehr Zeit. Nun muss ich mich aber beeilen, es wartet noch Arbeit auf mich......
Gruß oldmax
Versuche mal, deine Ausgabe vom Programm zu trennen. Dazu braucht es
nicht viel, gibt dir aber die Möglichkeit, jederzeit eine Änderung ohne
Aufwand einzupflegen.
Hallo Martin,
ich danke Dir hier für die ausführliche Problembehandlung. Habe momentan alle Hände voll zu tun, und deshalb bin ich manchmal langsam.
Ich habe den Screenshoot schon verstanden, und werde dies mal so versuchen. Komme frühestens am Wochenende wieder dazu. Meine Kohle per Monat kommt ja nicht hiervon.
Es war mir nicht klar, warum das eine Digit noch leuchtet, wenn das andere angesteuert wird. Dieses Ghosting ist hier immer noch mein Problem. Nun aber anders, und nun wird das linke Digit immer mit einer leicht leuchtenden '8' hinterlegt. Warum '8'? weiß ich noch nicht.
Die Ausgabe stimmt aber.
Aber nochmal zusammendfassend:
Es werden 2 Stellen benötigt. Zahlenraum von 0-50 Kredite. 99 wird nie erreicht. Der Einwurf wird (im ee-Prom hinterlegt) bei erreichen des Wertes gesperrt. Einziges Feature wir im Pausenbetrieb, Zähler auf 0 ist sein, ein kleines Lauflicht-Spiel mit den Segmenten.
Es gibt einen Ausgang, der High wird, sobald Kredit >0 ist.
Der uC muss nur auf 3 Eingänge reagieren, und entsprechende Kredite, die im ee-Prom hinterlegt sind auf addieren. Auch auf ein Signal zur Subtraktion bis hinab auf 0 muss er reagieren.
Eine kleine Service-Routine, für diverse Einstellungen ist dann vorhanden.
Das wars dann auch schon. Manipulation ist noch ein Thema, habe ich aber in einem anderen Projekt schon gelöst, abgedeckt.
Dass nämlich dieses 'TCNT1 = 65536-15' in der ISR steht, nach der
Rückkehr aus dieser noch zwei oder drei Befehle im Hauptprogramm
ausgeführt werden und dann schron wieder der Interrupt erfolgt.
Die Interupt-Routine habe ich mir aus dem 'Tutorial' geholt, und etwas angepasst.
Aber bitte wenn möglich, erklärt mir, das mit den Werten.
Wenn ich nämlich dieses TCNT1 = 65536-15, was ja den Überlauf erzeugt, und den Int auslöst, hier in die Initialisierung schiebe, oder nur dort habe läuft das nicht mehr.
Warum braucht die Routine dies immer wieder?
Am Ende werde ich 2 Interupt-Routinen benötigen.
Display
Für 'Watch-Dog' Routine, und der Ansteuerung einer LED dafür.
Um Pulse per Software zu erkennen und auszuwerten, muss man MINDESTENS
eine Flankenerkennung einbauen. Wartepausen sind bääääh.
Ich habe meiner Meinung dies drin (Wartepause), um erst bei fallender Flanke den Kredit zu erhöhen. Ist mal was kaputt, erzeugt mir die Routine dann nicht unendlich Kredite.
Um Pulse per Software zu erkennen und auszuwerten, muss man MINDESTENS
eine Flankenerkennung einbauen. Wartepausen sind bääääh.
Ich habe meiner Meinung dies drin (Wartepause), um erst bei fallender
Flanke den Kredit zu erhöhen. Ist mal was kaputt, erzeugt mir die
Routine dann nicht unendlich Kredite.
Mit dem sinnerfassenden Lesen hast du es wohl nicht so? Was hast du an
"Wartepausen sind bääääh."
nicht verstanden?
Und in meinem Beitrag ist ein vollständiger, aufgeräumter Quelltext vorhanden, der alle Probleme lösten sollte. Hast du den mal compiliert und auf deinen Controller programmiert?
Und in meinem Beitrag ist ein vollständiger, aufgeräumter Quelltext
vorhanden, der alle Probleme lösten sollte.
Oh, .... danke Dir. Hab ich erst grad gesehen. Werde ich gleich mal im Studio durchlaufen lassen. Aber auf den Atmega kann ich diesen erst morgen schieben.
Danke Dir, mile Grazie.
Wie gesagt, dass mit den Timer, Interupts, da bin ich erst noch am verinnerlichen.
Bin eigentlich überrascht, warum das nicht immer anstandslos funktioniert.
In meinem Timer Projekt machte ich genau das und steuerte im ganzen 56 LEDs im MUX Betrieb an. Allerdings über SPI um Pins zu sparen (Pro-Mini). Da gab es ein Vier-Digit LED-Display und ein Bargraph und sechs individuelle LEDs. Siehe Bild:
Die Segmente, Bargraph und LEDs werden von einem 74HC164 angesteuert und die Spalten über einen zweiten 164 und 138 Encoder über Treibertransistoren.
Eine T2 ISR steuerte die Leds über SPI mit 8MHz Takt an und das passierte komplett im Hintergrund. Ein volatile globales Array enthielt die ASCII Daten, die man gemütlich beschreiben konnte. Es gab absolut keine Probleme damit und es lief beim ersten Mal einschalten. Ghosting kam nicht vor.
SPI hat den großen Vorteil, die Anzeigeeinheit auch abgesetzt mit nur wenigen Drahtverbindungen betreiben zu können.
Ich entschloß mich für ASCII aus Bequemlichkeit und kann alle bei 7-Segment möglichen Sonderzeichen wie Buchstaben anzeigen.
Da der Refresh als Timer ISR ausgeführt wird, merkt man im übrigen Programablauf nichts in der Anzeige, wenn der uC andere Tätigkeiten ausführen muß. Auch bei Serial Datenübertragungen gibt es keine Reaktionen.
Du hast aber auch ziemlich viel Code in der ISR. Dachte das soll man
vermeiden?
"Viel" ist relativ. Entscheidend ist die Laufzeit der ISR. DIe muss logischerweise kürzer sein als die Periodendauer. Wenn das ggegben ist, ist alles OK. Siehe Interrupt. "Kurz" heißt nicht, daß man krampfhaft nur drei Anweisungen in eine ISR schreiben darf. Ich habe schon ISRs benutzt, da steht Code mit 1-2 Seiten drin. Trotzdem ist sie schnell genug in der AUsführung.
Über die NPN-BJTs in der High-Side Steuerung der Digits kann man
unterschiedlicher Meinung sein.
Kann man. Man verliert ca. 400-500mV mehr als bei Nutzung von PNPs in Emitterschaltung. Das ist meist unkritisch. Es bleibt genug Spannung für die Vorwiderstände.
Nochmal zur ISR. Die läuft hier mit 200Hz, das sind 5ms Periodendauer. Die CPU läuft mit 1MHz, macht in der Zeit also 5000 Takte. Die ISR braucht geschätzt ca. 30-50 Takte, also max. 1% der CPU-Leistung. Das ist mal sicher kurz genug ;-)
Aber bitte wenn möglich, erklärt mir, das mit den Werten.
Wenn ich nämlich dieses TCNT1 = 65536-15, was ja den Überlauf erzeugt,
und den Int auslöst, hier in die Initialisierung schiebe, oder nur dort
habe läuft das nicht mehr.
Warum braucht die Routine dies immer wieder?
Steht eigentlich alles relativ gut im Datenblatt des Timers Deines µControllers beschrieben – muss man sich halt etwas Mühe geben, es lesen und das Verständnis für die permanente Register-Akrobatik des Timerzählers im Modus 0 ergibt sich dann irgendwann mal, natürlich nicht unbedingt beim ersten Durchlesen. Diese Akrobatik des Counters ist aber normalerweise nicht nötig und außerdem etwas riskant, da man sich auch einen schwerauffindbaren Fehler einhandeln kann – für komplexere Zählervorgänge gibt es beispielsweise eben den CTC-Modus, wo man nichts immer wieder von Hand beeinflussen muss, und das sollte man auch nutzen. Im Anhang die zwei passenden Auszüge aus dem Datenblatt des 328P, beim ATMEGA8 wird es bestimmt sehr ähnlich aussehen; und auch sehr ähnlich funktionieren.