Forum: Mikrocontroller und Digitale Elektronik Atmega - Multiplex-Display-Ansteuerung fehlerhaft


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Hallo Forengemeinde,

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
21
     }       
22
  else
23
     {
24
     if (counter>0)
25
     {
26
      //PORTB |= _BV(6); 
27
      PORTD &=~_BV(6);
28
      PORTD |= _BV(7); // Toggle 10er-Stelle
29
      PORTB = array[((counter - (counter % 10))/10)];  // Zehnerstelle von 'counter' ausgeben
30
       }
31
     }
32
}

Im array sind die Bitmuster zu den Zahlen 0-9.
Was ist da falsche?

Nicht meckern wegen der Formatierung. Im Atmel-Studio passt es.

von Andreas H. (ahz)


Lesenswert?

Thomas S. schrieb:
> if (counter>0)

Sicher dass das nicht "counter > 9" sein soll?
Dein Programm gibt die Zehnerstelle aktuell auch als Einerstelle aus.

BTW: Man kann auch Dateien als Anhang einstellen. Und dann VOLLSTÄNDIG.

/regards

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Andreas H. schrieb:
> Sicher dass das nicht "counter > 9" sein soll?

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.

von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Thomas S. schrieb:
> Mit untenstehenden Code versuche ich (...)

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.

von Andreas H. (ahz)


Lesenswert?

Thomas S. schrieb:
> Es ändert hier erstmal nichts um die Doppel-Zahl mit 10% Intensität zu
> lösen.

Dann bau doch mal einen Delay zwischen der PortD Umschaltung und dem 
Ausgeben auf PortB ein. Evtl sind die PortD Pins noch nicht stabil 
gesetzt.

/regards

von Christian M. (christian_m280)


Lesenswert?


von Peter D. (peda)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?


von Axel R. (axlr)


Lesenswert?

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.

von Monk (roehrmond)


Lesenswert?

Ghosting ist das richtige Schlüsselwort. Das hat bei dir 3 Ursachen:

1) 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.

2) 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.

3) 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.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Monk schrieb:
> 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.

von Monk (roehrmond)


Lesenswert?

Peter D. schrieb:
> Ohne den Schaltplan zu kennen, ist diese Behauptung gewagt

War nicht als Behauptung gemeint, sondern als mögliche Fehlerquelle. 
Ohne Schaltplan habe ich tatsächlich geraten, wie die Schaltung aussehen 
könnte.

von Michael B. (laberkopp)


Lesenswert?

Thomas S. schrieb:
> Aber die Ziffer der Einerstelle ist auch leicht (ca. 10% Helligkeit) auf
> der 10er Stelle zu sehen

Thomas S. schrieb:
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
   }

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Hier mal ein einfacher Schaltplan für 4 Digits.
Ich hab die Segmente für ein einfacheres Layout umsortiert.

von Michael B. (laberkopp)


Lesenswert?

Peter D. schrieb:
> einfacher Schaltplan für 4 Digits.

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.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen ...

?
  Wie hoch ist die Multiplex-Frequenz?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas S. schrieb:
> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen
Nur mal zum Nachdenken: was ist 65536 in "16 Bit binär"?
- https://www.google.com/search?q=65536+binär

> // da der Timerroutine bei einem Überlauf aufgerufen wird
Und zwar bei einem Überlauf nach 0. Also müsste da stehen:
1
   TCNT1 = 0-15;
Oder wie es der Rest der Welt macht:
1
   TCNT1 = -15;


S. L. schrieb:
>> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen ...
> Wie hoch ist die Multiplex-Frequenz?
1 MHz, wenn der µC mit den üblichen 16MHz läuft und der Prescaler auf 1 
sitzt. Das wäre um den Faktor 10000 zu hoch. Natürlich gibt es da 
Ghosting.

: Bearbeitet durch Moderator
von Mi N. (msx)


Lesenswert?

Lothar M. schrieb:
> S. L. schrieb:
>>> TCNT1 = 65536-15; // Die 15 werden von 65536 abgezogen ...
>> Wie hoch ist die Multiplex-Frequenz?
> 1 MHz, wenn der µC mit den üblichen 16MHz läuft und der Prescaler auf 1
> sitzt.

Auch eine frisch geputze Glaskugel garantiert keine brauchbare 
Information ;-)

von S. L. (sldt)


Lesenswert?

> 1 MHz, wenn der µC mit den üblichen 16MHz läuft

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.

von Falk B. (falk)


Lesenswert?

Lothar M. schrieb:
> Oder wie es der Rest der Welt macht:   TCNT1 = -15;

Nicht mal das. Der AVR ist kein oller 8051, bei dem das noch nötig war. 
Man nutzt den CTC Modus, dann muss man gar nicht nachladen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Angehängte Dateien:

Lesenswert?

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.

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.

von Falk B. (falk)


Lesenswert?

Thomas S. schrieb:
> 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).

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

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.

Beitrag #7699326 wurde vom Autor gelöscht.
von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Thomas S. schrieb:
> 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.

Beitrag #7699342 wurde vom Autor gelöscht.
von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Thomas S. schrieb:
> 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

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Gregor J. schrieb:
> 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.

: Bearbeitet durch User
von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Monk schrieb:
> Zugleich werden Fehlermeldungen schwerer lesbar.

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.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Gregor J. schrieb:
> 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.

von Peter D. (peda)


Lesenswert?

Harald K. schrieb:
> 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.

von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Harald K. schrieb:
> 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.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Gregor J. schrieb:
> 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).

von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

Harald K. schrieb:
> um Code zu optimieren (...)

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.

von Michael B. (laberkopp)


Lesenswert?

Thomas S. schrieb:
> 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.

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Michael B. schrieb:
> 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.

: Bearbeitet durch User
von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Lesenswert?

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.

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Thomas S. schrieb:
> Ich gehe schließlich noch in die Arbeit, umnd komme so wie heute erst
> jetzt nach hause.

Du hast Firma: Chipwerkstatt angegeben. Möglicherweise weckt das 
profi-mäßige Erwartungen.

von Martin V. (oldmax)


Angehängte Dateien:

Lesenswert?

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

von Falk B. (falk)


Lesenswert?

Martin V. schrieb:
> Versuche mal, deine Ausgabe vom Programm zu trennen.

Du bist ja ein gaaaaanz Schneller! Das Thema ist erstens schon lange 
erledigt und 2. auch schon mit sinnvollem Quelltext versehen.

Beitrag "Re: Atmega - Multiplex-Display-Ansteuerung fehlerhaft"

von Martin V. (oldmax)


Lesenswert?

Hi
Falk B. schrieb:
> 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

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Martin V. schrieb:
> 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.

S. L. schrieb:
> 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.
1. Display
2. Für 'Watch-Dog' Routine, und der Ansteuerung einer LED dafür.

Falk B. schrieb:
> 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.

von Falk B. (falk)


Lesenswert?

Thomas S. schrieb:
> Falk B. schrieb:
>> 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?

von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Falk B. schrieb:
> 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.

von Gerhard O. (gerhard_)


Lesenswert?

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:

Beitrag "Re: Zeigt her eure Kunstwerke (2017)"
Beitrag "Re: Zeigt her eure Kunstwerke (2017)"

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.

: Bearbeitet durch User
von Thomas S. (Firma: Chipwerkstatt) (tom_63)


Lesenswert?

Hallo Falk,

habs doch noch auf den Chip geschoben. Perfekt *****

Du hast aber auch ziemlich viel Code in der ISR. Dachte das soll man 
vermeiden?

von Rainer W. (rawi)


Lesenswert?

Peter D. schrieb:
> Hier mal ein einfacher Schaltplan für 4 Digits.

Über die NPN-BJTs in der High-Side Steuerung der Digits kann man 
unterschiedlicher Meinung sein.

von Falk B. (falk)


Lesenswert?

Thomas S. schrieb:
> 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.

von Falk B. (falk)


Lesenswert?

Rainer W. schrieb:
> Ü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.

von Falk B. (falk)


Lesenswert?

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 ;-)

von Gregor J. (Firma: Jasinski) (gregor_jasinski)



Lesenswert?

Thomas S. schrieb:
> 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.

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
Noch kein Account? Hier anmelden.