Forum: Mikrocontroller und Digitale Elektronik 7 Segment Ansteuerung grundlegend falsch?


von Robin F. (gehacktes)


Angehängte Dateien:

Lesenswert?

Morgen,

ich möchte gern zwei bzw. später drei 7 Segmentanzeigen ansteuern.
Zu Testzwecken habe ich erstmal einen einfachen Zähler programmiert.
Nun habe ich mein Programm geschrieben, allerdings krieg ich das mit dem 
multiplexen nicht hin. Es ist immer ein flackern auf den Anzeigen, aber 
ich sehe den Fehler einfach nicht.

Ich vermute das ich irgendwie ein Problem mit den Interrupts habe oder 
ein grundlegenden Fehler in dem Code?

Es wäre nett, wenn Ihr einmal drüber gucken könnt und mir ein paar Tipps 
geben könnt bzw. Hilfestellung, damit ich das Problem beheben und 
verstehen kann.

Liebe Grüße

von foobar (Gast)


Lesenswert?

Ich bin nicht der Atmel Profi, aber 50ms Anzeige pro Zahl ist etwas kurz

von MaWin (Gast)


Lesenswert?

Na ja, 50ms warten pro Ziffer bedeutet 10Hz Mulitplexfrequenz,
5msec wären besser.

Im Prinzip hätte dein Programm alos funktioniert.

Aber die Schleife for(i in Anzeige ist überflüssig, denn
Anzeige wird eh unendlich oft aufgerufen.

und WENN du schon einen Interrupt verwendest (zum Zählen
bis 99), dann solltest du lieber den (100 mal schnelleren)
Interrupt fürs Multiplexen der jeweils NÄCHSTEN Stelle
verwenden, und in main ganz langsam (Sekundentakt) var und
var1 hochzählen.

von Arno (Gast)


Lesenswert?

Das einzige, das mir - neben der schon genannten unnötigen for-Schleife 
in Ausgabe() - auffällt ist, dass du F_CPU eigentlich vor dem include 
von delay.h definieren solltest.

Ansonsten sehe ich das wie MaWin - das sollte eigentlich passen, bis auf 
die zu niedrige Multiplexfrequenz, die zum Flackern führt.

Aber fang doch mal damit an, die Wartezeit hoch zu setzen, dann 
müsstest du die beiden Stellen eigentlich nacheinander aufleuchten 
sehen. Und ich würde zuerst mal das Zählen weglassen (du kannst ja in 
dem kompakten Programm den Interrupt einfach deaktivieren), bis die 
Ausgabe funktioniert.

MfG, Arno

von Karl H. (kbuchegg)


Lesenswert?

Umdrehen!

Das Multiplexen der Anzeige kommt in die ISR.

Mit einer for-Schleife und Wartezeiten zum Umschalten ist das für 
praktische Zwecke nicht brauchbar, auch wenn es in deinem speziellen 
Fall funktioniert.
Multiplexen immer im Timer-Interrupt, damit die Anzeige auch dann 
'läuft', wenn das Programm anderweitig beschäftigt ist.

von Robin F. (gehacktes)


Lesenswert?

Erstmal danke für die wirklich schnellen Antworten.

Ich habe jetzt das Multiplexen in die ISR gepackt und das Zählen in die 
main, die F_CPU nach oben unter #include <avr/io.h> gepackt, den 
prescaler auf 64 gestellt und probiere gerade mit den wait1 rum. 
Allerdings habe ich immer noch das flackern drauf aber es wird 
schneller...geht aber nicht weg.

Mal gucken was ich noch ändern kann?!

Gruß

von Karl H. (kbuchegg)


Lesenswert?

1
...
2
const uint8_t ausgabe[10] = { ZERO, ONE, TWO, THREE, FOUR, FIFE, SIX, SEVEN, EIGHT, NINE };
3
const uint8_t driver[2] = { 1 << PD5, 1 << PD6 };  // die Stellentreiber
4
uint8_t nextDigit = 0;
5
volatile uint8_t digit[2];   // die Muster für die beiden Stellen
6
7
ISR(TIMER1_COMPA_vect)
8
{
9
  PORTD &= ~( driver[nextDigit] );   // aktuell leuchtende Stelle abschalten
10
11
  nextDigit++;                       // welches ist die nächste Stelle
12
  if( nextDigit == 2 )
13
    nextDigit = 0;
14
15
  PORTB = digit[nextDigit];          // das Muster dieser Stelle anlegen
16
  PORTD |= driver[nextDigit];        // ... und die Stelle aktivieren
17
}
18
19
//
20
// eine Zahl in die beiden anzuzeigenden Stellen zerlegen und das für jede
21
// Stelle anzuzeigende Muster bestimmen und der ISR zur Verfügung stellen
22
//
23
void puti( uint8_t wert )
24
{
25
  uint8_t einer;
26
  uint8_t zehner;
27
28
  if( wert > 99 )
29
    wert = 99;
30
31
  zehner = wert / 10;
32
  einer = wert % 10;
33
34
  digit[1] = ausgabe[zehner];
35
  digit[0] = ausgabe[einer];
36
}
37
38
int main()
39
{
40
  uint8_t i = 0;
41
  ....
42
43
  ... timer einschalten und ISR aktivieren ....
44
45
  while( 1 )
46
  {
47
    puti( i++ );
48
    _delay_ms( 500 );
49
  }
50
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Robin F. schrieb:
> Erstmal danke für die wirklich schnellen Antworten.
>
> Ich habe jetzt das Multiplexen in die ISR gepackt

Du hast das wahrscheinlich nur verschoben. Und das ist falsch.
In die ISR kommt keine for-Schleife mehr und auch kein Wait.
Der Timer sorgt durch die regelmässigen ISR aufrufe selbst dafür, dass 
eine Stelle nach der anderen aktiviert wird.
Genau das ist der Trick: Jede Stelle leuchtet in der Zeit in der das 
Programm NICHT in der ISR ist. Bei Verlassen der ISR leuchtet die 
Stelle, beim nächsten Aufruf der ISR wird die Stelle abgeschaltet und 
die nächste aktiviert, die dann wieder leuchtet bis die ISR das nächste 
mal aufgerufen wird (und in der Zwischenzeit kann das Programm ganz was 
anderes machen).

Auf die Art verbraucht dir das Multiplexen so gut wie keine Rechenzeit 
(auch nicht für Warteschleifen) und die Anzeige steht trotzdem ohne 
flackern.

> prescaler auf 64 gestellt und probiere gerade mit den wait1 rum.

Falscher Ansatz.
Im ganzen Multiplexen kommt keine Warterei vor. Wenn du da irgendwo ein 
Wait hast (ausser vielleicht ein ganz kurzes nach Abschalten der 
angezeigten Stelle) dann kann ich dir auf den Kopf zusagen, dass deine 
Implementation schlecht (mir liegt eigentlich ein anderes Wort auf der 
Zunge, welches mit 'sch' beginnt und mit 'eisse' endet) ist.

von Karl H. (kbuchegg)


Lesenswert?

PS: den Timer wirst du natürlich anders einstellen, so dass du circa. 
100 bis 150 Aufrufe pro Sekunde hast. Können auch mehr sein, dass ist 
nicht kritisch.

Du wirst wahrscheinlich auch nicht den leistungsfähigen Timer 1 dafür 
nehmen wollen, sondern kannst ruhig auch den Timer 0 dafür hernehmen 
(wenn dein µC den hat und der einer der schwächeren 8 Bit Timer ist). Es 
geht nur darum, dass die ISR zuverlässig x mal in der Sekunde aufgerufen 
wird. Nicht mehr. Wobei das x ziemlich unkritisch ist und du eigentlich 
nur über einer unteren Grenze bleiben musst, damit nichts flackert. Aber 
ob du jetzt 150 oder 178 ISR-Aufrufe pro Sekunde hast (bei 2 Stellen) 
ist Jacke wie Hose. Und wenn es 800 sind, ist es auch wurscht. Nur 
weniger als ca 50 dürfen es nicht sein, denn dann siehst du das bereits 
ganz leicht flackern.

: Bearbeitet durch User
von Robin F. (gehacktes)


Lesenswert?

Alles klar jetzt versteh ich das so langsam!
Werde das gleich mal ausprobieren


Vielen Dank

von Martin V. (oldmax)


Lesenswert?

Hi
Abgesehen davon, das ich in Assembler arbeite, ist meine Vorgehensweise 
für Multiplexen rel. einfach. Der Timer erzeugt einen Interrupt in jeder 
mSek. Hier wird erst mal der Gemeinsame abgeschaltet, alle Segmente aus. 
Dann wird der neue Code in die Segmentausgabe geschoben und danach der 
gemeinsame wieder angesteuert. Das ergibt ein schönes gleichmäßiges 
Leuchten der Segmente. Damit die ISR nicht zu groß wird, steht der 
Zahlencode in einem Ausgabepuffer. Die Multiplexroutine holt also nur 
passend zur Anzeigestelle den Code und gibt diesen aus. Den Code aus 
einer Zahl aufbereiten und in den Puffer zu schreiben, dafür hat die 
Main alle Zeit der Welt. Die Abschaltung des Gemeinsamen solltest du 
vornehmen, damit du kein Überleuchten der Segmente bekommst.
Grußß oldmax

von Robin F. (gehacktes)


Lesenswert?

So ich hab das ganze nochmal umprogrammiert.

Es funktioniert jetzt...allerdings musste ich noch etwas mit dem 
Interrupts spielen. Da ich noch nicht so die Ahnung von Interrupts habe 
und somit auch noch nicht die ganzen Regeln und Kniffe kenne, habe ich 
auf der Internetseite: 
https://sites.google.com/site/qeewiki/books/avr-guide/timer-on-the-atmega8 
ganz unten das Interrupt raus geschrieben und etwas abgeändert.
1
ISR (TIMER2_COMP_vect)
2
{
3
...
4
}
5
6
int main(){
7
...
8
OCR2 = 62;
9
10
    TCCR2 |= (1 << WGM21);
11
    // Set to CTC Mode
12
13
    TIMSK |= (1 << OCIE2);
14
    //Set interrupt on compare match
15
16
    TCCR2 |= (1 << CS22);
17
    // set prescaler to 64 and starts PWM
18
19
    sei();
20
    // enable interrupts
21
...
22
}

Da es jetzt funktioniert, kann ich damit spielen.

Vielen Dank nochmal!

von Karl H. (kbuchegg)


Lesenswert?

Wenn du dich ganz einfach an den Overflow-Interrupt gehängt hättest, 
müsstest du da keinerlei Klimmzüge mit CTC Modus, OCR Wert und 
dergleichen machen.

Einfach den Timer mittels Vorteiler starten.
Den Overflow-Interrupt freigeben
und eine ISR für den Overflow Interrupt schreiben.

FAQ: Timer

1
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector
2
{
3
 ...
4
}
5
 
6
int main()
7
{
8
9
  TIMSK |= (1<<TOIE0);  // den Overflow Interrupt des Timers freigeben
10
  TCCR0 = (1<<CS01);    // Vorteiler 8, jetzt zählt der Timer bereits
11
 
12
 
13
  sei();                // und Interrupts generell freigeben
14
 
15
 
16
  while( 1 )
17
  {
18
  }
19
}

: Bearbeitet durch User
von Robin F. (gehacktes)


Lesenswert?

Ah ok. Dann werde ich mich da nochmal belesen.

Will jetzt aber eh einmal mehr über Interrupts erfahren. Die bereiten 
mir doch einige Schwierigkeiten.

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.