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
Ich bin nicht der Atmel Profi, aber 50ms Anzeige pro Zahl ist etwas kurz
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.
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
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.
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ß
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
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.
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
Alles klar jetzt versteh ich das so langsam! Werde das gleich mal ausprobieren Vielen Dank
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
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!
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.