Hallo, mit einem ATTINY 2313 habe ich einen vierstelligen 7-Segment Countdowntimer realisiert. Der HW-Aufbau sieht so aus: die gemeinsamen Anoden der Stellen hängen über je einen NPN Transistor mit Kollektorvorwiderstand an VCC und über je einen Basiswiderstand an den POTD Pins. Die Kathoden der Segmente sind über je einen Vorwiderstand an die PORTB Pins angeschlossen (POTB.0=a, ..., POTB.6=g). Mit PORTD.5 frage ich einen Schalter ab. Die Wesentlichen Ausschnitte des Codes sind unten angefügt. Minuten und Sekunden zählen schön runter und werden auch ordentlich angezeigt. Alle Segmente die eine Zahl bilden sollen, leuchten auch hell. ABER! Einige der Segmente, die dunkel sein sollen leuchten trotzdem schwach mit und zwar in wechselnden wiederkehrenden Mustern. Wie bekomme ich das Geisterleuchten weg? Liegt es an der SW, ev. in stellenanzeige() die Port-Initialisierungssequenz optimieren? Oder hat der Proz ne Macke (ich hab den von Pollin gekauft, vielleicht ist der sowas wie Zweite-Wahl-Ramsch...)? Helft mir mal kurz auf die Sprünge, danke! /*********************************************************************** */ /* Initialisierung */ /*********************************************************************** */ void init_bd (void) { // Port Initialisierung: // PORTB Ausgang für die einzelnen Segmente, Seg.x leuchtet, wenn PINx==LO // PORTD 0-4 Ausgäne für 7seg Anoden, Pin 5 als Eingang für Tilt-Schalter PORTB = 0xFF; // Alles auf hi, damit nichts leuchtet! PORTD = 0x10; // Ausgang 0-4 auf lo, damit nichts leuchtet! PD5 pullup ein für tilt_Schalter DDRB = 0xFF; // Alles auf Ausgang DDRD = 0x0F; // 0-3 auf Ausgang, PD4,5,6 auf Eingang /* Timer Initialisierung */ TCCR1A = 0; TCCR1B = (1<<WGM12) | (1<<CS12) /*| (1<<CS11) | (1<<CS10)*/; TIMSK |= (1<<OCIE1A); // Compare Interrupt aktivieren OCR1A = TWAIT_1P0_S; // Compare-Wert auf eine Sekunde sei(); // Initialisierung der Startwerte zustand = COUNTDOWN; minuten = MAXMINUTEN; sekunden = MAXSEKUNDEN; time_cnt = 0; tilt = 0; digit = 0; } /*********************************************************************** */ /* Abfrage des TILT-Schalters */ /*********************************************************************** */ void poll_tilt (void) { // Abfrage, ob TILT-Schalter gedrückt ist if (PIND & (1<<PIND5)) { tilt = 1; } } /*********************************************************************** */ /* Verwaltung der Zustände, Zeitberechnung und Aktionen */ /*********************************************************************** */ void zustandsautomat (void) { bla; bla; bla; // Umsetzung der Dezimal- in Binärzahlen zur Erleuchtung der LED-Segmente for (i = 0; i < ANZSTELLEN; i++) { bin_out[i] = SEGMENTE[dez_out[i]]; // Binärmuster für Ausgabe } bla; } /*********************************************************************** */ /* Ausgabe auf 7-Segmentanzeige */ /*********************************************************************** */ void stellenanzeige(void) {// Ausgabe der Binärwerte auf PORTB und Weiterschalten der Stelle durch PORTD PORTB = bin_out[digit]; PORTD = (PORTD & 0xE0) | (1<<digit); if (digit < (ANZSTELLEN-1)) { digit++; } else { digit = 0; } } /*********************************************************************** */ /* Hauptschleife */ /*********************************************************************** */ void bd_loop(void) { // Hauptschleife for (;;) { // Abfrage des Tilt-Schalters poll_tilt(); // Zustände verwalten, Zeit herunterzählen und Binärmuster erzeugen zustandsautomat(); // Anzeige verwalten, Binärmuster auf PORTB, Stellen weiterschalten stellenanzeige(); } // ende Hauptschleife } int main( void ) { while(1) { init_bd(); bangdead_loop(); } }
Hier
1 | void stellenanzeige(void) |
2 | {// Ausgabe der Binärwerte auf PORTB und Weiterschalten der Stelle durch PORTD |
3 | PORTB = bin_out[digit]; |
4 | PORTD = (PORTD & 0xE0) | (1<<digit); |
schaltest du das neue Muster für die Anzeigen schon auf die LED, während noch die vorhergehende Stelle selektiert ist. Und das siehst du -> zuerst die aktuelle Anzeige dunkel schalten dann die nächste Stelle selektieren dann diese Stelle durch Ausgabe des Musters zum Leuchten bringen
1 | PORTB = 0x00; // oder 0xFF je nachdem wie deine LED angeschlossen sind |
2 | PORTD = (PORTD & 0xE0) | (1<<digit); |
3 | PORTB = bin_out[digit]; |
schau dir doch mal das geisterleuchte an... du wirst die leds der zuletzt gemultiplexten ziffer erkennen
Mach da mal nen cli (); sei (); davor. Denke das du die Ausgabe per ISR rausschreibst aber einfach ins Array schreibst wann du willst ? oder ? for (i = 0; i < ANZSTELLEN; i++) { bin_out[i] = SEGMENTE[dez_out[i]]; // Binärmuster für Ausgabe } Ist nen Schnellschuß... schau halt mal.
Hallo, bei hoher Taktfrequenz brauchst Du auch noch zusätzlich 2-4 us Delay bis deine Transistoren tatsächlich ausgeschaltet (Sperrverzögerungszeit) sind also: PORTB = 0x00; // oder 0xFF je nachdem wie deine LED angeschlossen sind PORTD = (PORTD & 0xE0) | (1<<digit); Delay(4 us); PORTB = bin_out[digit];
@Anja (Gast) >bei hoher Taktfrequenz brauchst Du auch noch zusätzlich 2-4 us Delay bis >deine Transistoren tatsächlich ausgeschaltet (Sperrverzögerungszeit) Die Sperrverzögerungszeit ist unabhängig von der Taktfrequenz. Wenn die Schaltstufen was taugen, haben die keine (so hohe) Sperrverzögerungszeit. MFG Falk
Merke: Nur Anfänger halten es für möglich, dass es auch an einer "Macke im Proz" liegen könnte. Alle erfahrenen Entwickler wissen, dass der Grund immer im eigenen Programm zu finden ist. Die Frage lautet nie "der Proz oder der Code?" sondern stets "im Code - nur wo?".
Hallo Falk, bei niedriger Taktfrequenz ist die Zeitdauer zwischen setzen von PortD bis setzen PortB eh schon größer als die Sperrverzögerungszeit. Mit 4 us sollte man dann hoffentlich auf der sicheren Seite sein. Geisterlicht will ja ausschließen daß es keinen Hardwaredefekt hat.
Hallo und danke für die Antworten! Also, wenn ich das jetzt richtig verstehe, muß ich die Segmente erst ausschalten, bevor ich die Stelle wechsle? Das ginge mit PORTB = 0xFF (sind ja die Kathoden). Das probiere ich mal aus. Fragen: @Bernd Wie ist das gemeint mit sie() und cli()? Die Anzeige wird in der Hauptschleife ausgegeben. Den Compare-Interrupt benutze ich, um einen Zeitzähler hochzusetzen, der dann im Zustandsautomat in der Hauptschleife ausgewertet wird. @Anja Ich verwende den internen 8MHz Oszillator. Die Anzeige wird vermutlich alle ca. 1000 Taktzyklen von Stelle zu Stelle weitergeschaltet. Sollte das für die BC548 nicht ausreichen? @Michael M. Wahrscheinlich hast Du Recht, aber, so schnell kann ich gar nicht gucken, wie das flimmert ;-) Also noch Mal danke an alle!
@Gast >Merke: Nur Anfänger halten es für möglich, dass es auch an einer "Macke >im Proz" liegen könnte. Ja, das merke ich mir. Wollte einen kleinen Scherz machen mit dem Pollin-Proz :-)
Hallo Geisterlicht, > Ich verwende den internen 8MHz Oszillator. Die Anzeige wird vermutlich > alle ca. 1000 Taktzyklen von Stelle zu Stelle weitergeschaltet. Sollte > das für die BC548 nicht ausreichen? Ups... Also: die Wechselfrequenz von Stelle zu Stelle würde ich nicht viel höher machen als unbedingt nötig: also ca 50-100Hz * Anzahl der Stellen damit die Anzeige nicht flimmert. Also bei Dir so maximal 400 Hz bzw. alle 2,5 Millisekunden. Bei 8MHz / 1000 Zyklen = 8000 Hz wundert es mich nicht daß der Effekt so stark sichtbar ist. Ich mache das multiplexen gerne in einer Timer-Interrupt-Routine. Dann ist auch sichergestellt daß alle Digits gleichmäßig hell leuchten. Den 1 Sekunden-Timer kann man dann als vielfaches des Multiplex-Taktes erreichen. z.B. alle 4 Digits * 100 Durchläufe. BC548 sind Transistoren für Verstärker-Anwendungen und nicht für Schalter-Betrieb spezifiziert. Eine Sperrverzögerungszeit ist daher in den Datenblättern nicht angegeben. Was nicht heißt daß man den BC548 nicht für Schalteranwendungen verwenden darf. Das Verhalten ist halt nur nicht spezifiziert. Bei Schalteranwendungen verwende ich gerne den 2N2222A der hat dann so 0,25 us Sperrverzögerungszeit bei 150mA Kollektorstrom.
Anjas Bemerkungen sind sehr richtig. Die Multiplexfrequenz sollte nicht höher sein als für eine flimmerfreie Darstellung nötig. 400 Hz sind ein guter Wert. Den Ziffernwechsel würde ich nach folgendem Schema bewerkstelligen: - Alle Segmente ausschalten - Den "alten" Transistor sperren - Evtl. kurz warten (µs-Bereich) - Den "neuen" Transistor durchschalten - Das neue Segmentmuster anlegen Dann sind die Segmente während des Umschaltens stromlos und es sollten keine Geisterbilder auftreten.
Geisterlicht schrieb: > @Gast > >>Merke: Nur Anfänger halten es für möglich, dass es auch an einer "Macke >>im Proz" liegen könnte. > > Ja, das merke ich mir. Wollte einen kleinen Scherz machen mit dem > Pollin-Proz :-) Achso, ja als Pollinkunde is das was anderes. Da darf man dann auch an der gelieferten Hardware zweifeln ;)
> Den Ziffernwechsel würde ich nach folgendem Schema bewerkstelligen: > - Alle Segmente ausschalten > - Den "alten" Transistor sperren > - Evtl. kurz warten (µs-Bereich) > - Den "neuen" Transistor durchschalten > - Das neue Segmentmuster anlegen Ich mach's immer anders herum: - Alle Digits ausschalten (irgendeines wird wohl ein gewesen sein) - Digit-Index hochzählen und begrenzen - Neues Segment-Bitmuster holen und ausgeben (kostet aufgrund der Positionierung im Array etwas Zeit, die hier parasitär als Verzögerung wirkt) - Neues Digit einschalten Da ich oftmals aufgrund der Vereinfachung der Platine Segment-Leitungen und Digit-Leitungen wild durcheinander auf die Portpins zweier Ports verteile, sieht es meist noch etwas anders aus (ASM): - Ausgabe einer 16-Bit-Konstante an beide Ports, die alle Digits und Segmente deaktiviert (2 mal LDI/OUT), während der folgenden Schritte dürfen sich die Transistoren "erholen" - Digit-Index hochzählen und begrenzen (Ring, Anzahl der Digits) - Pointer auf SRAM-Array (und Index) positionieren (2 x LDI, ADD, ADC) - Segment-Bitmuster aus Array holen (2 mal LDD) - Digit-Bitmuster aus Array holen (2 mal LDD) - Beide Bitmuster miteinander verknüpfen (2 x OR, AND, je nach Schaltung) - Bitmuster ausgeben (2 x OUT) Das Bereitstellen der Segment-Bitmuster für jedes Digit erledigt die Mainloop bereits beim Ermitteln des anzuzeigenden Wertes. Die Digit-Bitmuster wurden beim Initialisieren aus dem Flash ins SRAM-Array kopiert. Die Multiplex-Routine läuft selbstverständlich im Timer-Interrupt. Durch Nutzen eines Index' für mehrere SRAM-Lese-Zugriffe (2 Bytes Segment-Bitmuster, 2 Bytes Digit-Bitmuster) über LDD bleibt der Code schlank und schnell. Meist sind auch noch 4 Exklusiv-Register für ISRs drin (2 obere, 2 untere), das spart dann etliche PUSH und POP. ...
Hallo Gast. > Merke: Nur Anfänger halten es für möglich, dass es auch an einer "Macke > im Proz" liegen könnte. Alle erfahrenen Entwickler wissen, dass der > Grund immer im eigenen Programm zu finden ist. Die Frage lautet nie "der > Proz oder der Code?" sondern stets "im Code - nur wo?". Nö. Als ich so Anno 97 das erste mal mit Microcontrollern rumgemacht habe, habe ich mir prompt durch irgendeinen Schluss zwischen Leiterbahnen die Ausgangsports abgeschossen (Ein Zweig des Totempfahls). Allerdings konnte ich am Oszilloskop die Pins noch um 0,2V wackeln sehen, in dem Rythmus, in dem es geplant war.....insofern war alles klar. Ich hab auch erstmal mit dem kaputten IC weitergemacht, bis ich soweit war, das ich von der SW alles so hatte wie ich wollte......warum ein zweites IC riskieren? Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
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.