Forum: Mikrocontroller und Digitale Elektronik 7 Segment Multiplex + Timer Interrupt?


von Robert I. (robert_i39)


Lesenswert?

Hallo,

Ich bin seit längerem dabei gewisse Werte auf 7 Segment Anzeigen 
wiederzugeben und komme soweit langsam voran.

Mein letzter Erfolg war die Auswertung eines Temperatursensors KTY81 und 
berechung der Temperatur, welche dann auf 2 7 Segment Anzeigen 
wiedergegeben wurde, was aber nicht sehr zufriedenstellend ist da ich 
die Anzeige mit Hilfe von delay Timern realisiert habe und daher ist ein 
Flackern und auch ein nachglimmen der Segmente zu sehen. Welches ich 
durch den Einsatz von Interrupts lösen will. Da ich aber noch nie mit 
Interrupts etwas programmiert habe muss ich mal ein paar Basics hier 
fragen.

Ich habe mir bereits den Artikel hier über Interrupts durchgelesen und 
soweit verstanden. Jetzt bin ich bei der Umsetzung und möchte gerne 
wissen ob ich soweit alles richtig verstanden habe?
--------------------------
Also:
(so möchte ich es realisieren, falls möglich)
2 Stellige 7 Segment Anzeige läuft im Multiplexbetrieb an einem 
MSP430G2553.

Wenn das Programm startet erscheint auf der Anzeige 00.
Dann wird durch einen TimerInterrupt die Anzeige unterbrochen und der 
ADC gelesen und ausgewertet, dann der Wert zurück an die Anzeige 
übergeben.

Der TimerInterrupt soll nach meinem Überlegen her nur alle 3-5 Sekunden 
(muss ich noch versuchen was besser ist) einsetzen, so dass die Anzeige 
nicht dauernd hin und her springt zwischen Werten.

Und das alles läuft while(1) :)
--------------------------

Habe ich dass damit so richtig verstanden, dass der Interrupt die 
Anzeige unterbrechen soll, oder läuft das alles doch anders ab?

Falls es noch wichtig ist, die 7 Segment Anzeigen haben eine Gemeinsame 
Kathode welche über 2 MOSFETs gesteuert wird.

Danke euch

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Robert I. schrieb:

> wiedergegeben wurde, was aber nicht sehr zufriedenstellend ist da ich
> die Anzeige mit Hilfe von delay Timern realisiert habe und daher ist ein
> Flackern und auch ein nachglimmen der Segmente zu sehen. Welches ich
> durch den Einsatz von Interrupts lösen will.

Gut


> Also:
> (so möchte ich es realisieren, falls möglich)
>
> Wenn das Programm startet erscheint auf der Anzeige 00.

Nope

> Dann wird durch einen TimerInterrupt die Anzeige unterbrochen und der
> ADC gelesen und ausgewertet, dann der Wert zurück an die Anzeige
> übergeben.

Genau anders rum.

Der Anzeige Code ist der zeitkritische. Der gehört in den 
Timer-Interrupt!

Den ADC Teil kannst du ruhig in der Hauptschleife lassen. Dort ist er 
gut aufgehoben


Deinen Timer stellst du so ein, dass er ca. 150 bis 200 mal in der 
Sekunde die ISR aufruft. Das ist so ein Kompromiss, weil du anscheinend 
2 Anzeigestellen hast. Da in jedem ISR Aufruf immer die nächste Stelle 
eingeschaltet wird, leuchtet jede Stelle daher 75 bis 100 mal in der 
Sekunde auf, was normalerweise ausreicht, dass das Bild ruhig steht. 
Wenn du zu den Menschen gehörst, die da immer noch ein Flackern 
wahrnehmen, dann gehst du eben mit der ISR AUfruf-Frequenz noch etwas 
höher, auf vielleicht 300 oder 400 Aufrufe. Aber spätestens dann sollte 
die Anzeige stehen wie eine Eins. Man kann auch noch höher gehen, aber 
das bringt dann nichts mehr.

In der ISR wird dann gemacht (Pseudocode, da ich die MSP Syntax nicht 
kenne, wie dort die Hardware angesprochen wird bzw. wie man 
Interrupt-Funktionen vereinbart)
1
volatile uint8_t Digit[2];   // die auszugebenden Muster für die Anzeige
2
3
ISR( ... )                // Interrupt Funktion, vom Timer aufgerufen
4
{
5
  static uint8_t Stelle;
6
7
  bisherige Anzeige komplett abschalten (alle Kathoden auf 'aus')
8
9
  Stelle++;
10
  if( Stelle == 2 )
11
    Stelle = 0;
12
13
  Das Muster für Digit[Stelle] ausgeben
14
  Die Anzeige Stelle durch Schalten der zugehörigen Kathode aktivieren
15
}

Die ISR macht also nichts anderes als die Anzeige erst mal komplett 
abdrehen, danach bestimmen welche der beiden 7_segment_anzeigen als 
nächstes mit Leuchten drann ist. Sie legt dann das Leuchtmuster für die 
LEDs auf die Ausgänge und aktiviert mit der gemeinsamen Kathode genau 
diese Stelle.

Diese eine jetzt eingeschaltete 7-Segment-Anzeige leuchtet dann vor sich 
hin, bis der nächste Interrupt kommt, wo sie abgeschaltet wird und die 
nächste (in deinem Fall: die andere der beiden) 7-Segment Anzeigen 
beschickt und aktiviert wird.

Und so geht es immer reihum. 150-200 mal in der Sekunde wechselt die 
Anzeige, einmal leuchtet die eine, dann die andere. Und da das 
timergestützt funktioniert, passiert es auch dann, wenn in deiner 
Hauptschleife gerade irgendwas gerechnet wird. Die Rechnung wird dann 
einfach unterbrochen, die Anzeige umgeschaltet und dann kann die 
Rechnung weitergeführt werden  -----> nichts flackert, weil die Anzeige 
regelmässig im Takt umgeschaltet wird.


Wichtig:
Im Array Digit hast du bereits das komplette Muster für die ANzeige 
gespeichert. Wenn du die Zahl 23 ausgeben willst, dann steht daher in 
Digit[0] schon die 'umgewandelte' 2, also die Portbelegung, welche LED 
leuchten sollen und welche nicht. Die ISR soll sich da nicht mehr damit 
rumschlagen, welche LED leuchten müssen, wenn an einer Stelle eine 4 
aufleuchten soll. Die ISR schaufelt nur noch LED-Muster an die Anzeige, 
kümmert sich aber nicht darum, was diese Muster darstellen. Wenn dein 
Hauptprogramm in Digit[1] ein Muster hinterlässt, welches einfach nur 
ein einsames '-' (also den mittleren Querstrich) anzeigen lässt, dann 
gibt die ISR das auch genau so aus. Und wenn das Hauptprogramm alle 3 
Querstriche als leuchtend in Digit[1] einträgt, dann gibt die ISR auch 
das genau so aus.



In der Hauptschleife kannst du dann nach Herzenslust rechnen oder 
sonstige Dinge tun. Der Timer, bzw. die Interruptroutine sorgt dafür, 
dass deine Anzeige dauernd sauber sichtbar ist. Um die musst du dich 
dann soweit nicht mehr kümmern. Wenn du eine neue Zahl hast, die 
auszugeben ist, dann schreibst du die entsprechenden LED Muster in 
Digit[0] bzw. Digit[1] und damit ist die Sache für die Hauptschleife 
erst mal erledigt - die ISR sorgt dann schon dafür, dass das ganze dann 
auch auf den 7-Segment Anzeigen sichtbar wird. Sinnvollerweise wird man 
sich natürlich eine Funktion machen, die eine 2-stellige Zahl 
entsprechend aufbereitet
1
uint8_t ledPattern[12] = {  .... die Bytes, welche für die Ziffern
2
                                 0 bis 9 auszugeben sind, sowie
3
                                 ein Pattern für
4
                                   * leer
5
                                   * Error (zb. 'E')
6
                         };
7
8
void output( uint8_t number )
9
{
10
  if( number > 99 )
11
  {
12
    Digit[0] = ledPattern[10];   // leer
13
    Digit[1] = ledPattern[11];   // Error
14
  }
15
  else
16
  {
17
    Digit[0] = ledPattern[ number / 10 ];
18
    Digit[1] = ledPattern[ number % 10 };
19
  }
20
}

: Bearbeitet durch User
von Georg (Gast)


Lesenswert?

Hallo,

ich organisiere das normalerweise so:

Timeinterrupt in der Grössenordnung 1..2 ms, der schaltet jeweils die 
Anzeige 1 Stelle weiter und gibt den entsprechenden 7SegmentCode aus. 
Ist eine Matrixtastatur dran, wird die ebenso in dieser ISR 
weitergeschaltet und abgefragt, und mit der üblichen Software entprellt. 
Das muss aber nicht so oft passieren, jeder 2. oder 4. Aufruf reicht für 
die Tastatur (abzählen). Ebenso kann man den ADC in der ISR abfragen, 
ebenfalls über einen Zähler nur ein paarmal in der Sekunde, weil man 
sowieso nicht schneller kucken kann.

Für solche Fälle ist es durchaus üblich, dass in dieser Timer-ISR alles 
Wesentliche erledigt wird und in main nur die Initalisierung erfolgt und 
die Endlosschleife nur Dinge geringer Priorität erledigt, wie etwa die 
Verarbeitung der eingegeben Befehle. Das muss ja nicht schnell sein, 
weil das Eintippen sowieso Sekunden braucht. Nicht persönlich nehmen, 
aber der User hat die geringste Priorität.

Obige Angaben reichen übrigens für bis zu 8 Digits.

Georg

von Dirk K. (dekoepi)


Lesenswert?

Uhm. Ich schreibe jede ms meine kompletten 4 Ziffern aufs 7-Segment. Und 
alle Minute kommt dann noch auslesen des RTC-Chips dazu ;)
Also 1000 mal wirklich erst an- und dann ausschalten. Spart offenbar gut 
Strom, nach 4 Wochen ist mein Akku noch > halbvoll ;)

von Peter D. (peda)


Lesenswert?

Robert I. schrieb:
> Habe ich dass damit so richtig verstanden, dass der Interrupt die
> Anzeige unterbrechen soll

Nö, es ist genau umgekehrt.
Die schnellen Sachen gehören in den Interrupt, also das Multiplexen.
Der Mensch ist da sehr empfindlich, auch ein Flackern alle 3s stört ihn.

Der ADC und die Ausgabewandlung kommen ins Main. Mehr als 2..5 Messungen 
je s kann der Mensch eh nicht verarbeiten. Daher ruhig noch ein Delay 
ins Main (200..500ms), damits ergonomisch ist.
Falls das Main noch anderes machen soll, dann im Timerinterrupt alle 
200..500ms ein Flag setzen, damit das Main den ADC anstoßen kann.

von Robert I. (robert_i39)


Lesenswert?

Danke euch vielmals für die Antworten.

@Karl Heinz : Besonderen Dank an dich , ist eine mega Hilfe wie du das 
geschrieben hast. Besten Dank.

: Bearbeitet durch User
von Robert I. (robert_i39)


Lesenswert?

Ach ja noch was.

Gibt es eigentlich im allgemeinen große Unterschiede in der 
Programmierung wenn man ein Batteriebetriebenes Gerät verwirklichen will 
?


Gruß

von Karl H. (kbuchegg)


Lesenswert?

Robert I. schrieb:
> Ach ja noch was.
>
> Gibt es eigentlich im allgemeinen große Unterschiede in der
> Programmierung wenn man ein Batteriebetriebenes Gerät verwirklichen will
> ?

Batteriebetrieben und 7-Segment-LED Anzeigen beissen sich grunsätzlich.
Der Hauptstrom geht in die Anzeige. Die paar mA, die du beim µC sparen 
kannst, machen das Kraut nicht wirklich fett. Wenn du die Anzeigedauer 
deiner Anzeigen halbierst, zb so
1
...
2
  static uint8_t Stelle;
3
4
  bisherige Anzeige komplett abschalten (alle Kathoden auf 'aus')
5
6
  Stelle++;
7
  if( Stelle == 4 )    // <---- 2 fiktive Stellen, in denen einfach nichts
8
    Stelle = 0;        // leuchtet
9
10
  if( Stelle < 2 )
11
  {
12
    Das Muster für Digit[Stelle] ausgeben
13
    Die Anzeige Stelle durch Schalten der zugehörigen Kathode aktivieren
14
  }
15
}

dann bringt das erst mal am allermeisten, dafür wird aber auch die 
Anzeige dunkler.

Aber natürlich gibt es auch bei einem µC Möglichkeiten, wie man sparen 
kann. Man kann den µC "schlafen legen". Dann verbraucht er nur noch 
winzigste Mengen an Strom, wacht 200 mal in der Sekunde auf um die 
Anzeige zu multiplexen und sich danach wieder schlafen zu legen.

Aber eines nach dem anderen. Sleep Modi werden (genauso wie Watchdog) 
erst zum Schluss eingebaut. Erst mal muss der Rest funktionieren.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Robert I. schrieb:
> Gibt es eigentlich im allgemeinen große Unterschiede in der
> Programmierung wenn man ein Batteriebetriebenes Gerät verwirklichen will
> ?

Nö, zuerst kümmert man sich nur um die Funktionalität.

Später kann man analysieren, ob und wo sich Strom sparen läßt.
Z.B. größere Vorwiderstände/Tastverhältnis für die LEDs (= dunkler), 
Ausschaltfunktion nach xx Minuten.

Beitrag "AVR Sleep Mode / Knight Rider"

von lowpower (Gast)


Lesenswert?

Robert I. schrieb:
> Gibt es eigentlich im allgemeinen große Unterschiede in der
> Programmierung wenn man ein Batteriebetriebenes Gerät verwirklichen will
> ?

ja, wenn die MCU nichts zu tun hat, ab in einen der Sleep-Modes, aus dem 
sie mit einen weiteren Timer oder einen anderen (vielleicht externen 
Interrupt, z.B. Taste betätigt) geweckt wird.

Und wenn deine 7-Segmentanzeigen aus LEDs bestehen, dann auch hier die 
Anzeige ausschalten, wenn keiner drauf schaut...

von Falk B. (falk)


Lesenswert?


von Georg (Gast)


Lesenswert?

lowpower schrieb:
> Und wenn deine 7-Segmentanzeigen aus LEDs bestehen, dann auch hier die
> Anzeige ausschalten, wenn keiner drauf schaut...

Vieeel besser: LCD verwenden.

Georg

von Dirk K. (dekoepi)


Lesenswert?

Wenn eine Laufzeit von geschätzt 2 Monaten reicht (Akku ist nach einem 
Monat jetzt von 4,0xV auf 3,72V gefallen), geht das auch mit 1000 Mal 
die Sekunde kurz Aufblinken lassen. Ist bei Sonneneinfall nicht lesbar, 
aber bei Bewölkung schon.

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.