www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Geisterleuchten bei 7-Segment Anzeige


Autor: Geisterlicht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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();
  }
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier
void stellenanzeige(void)
{// Ausgabe der Binärwerte auf PORTB und Weiterschalten der Stelle durch PORTD
  PORTB =  bin_out[digit];
  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
  PORTB = 0x00;  // oder 0xFF je nachdem wie deine LED angeschlossen sind
  PORTD = (PORTD & 0xE0) | (1<<digit);
  PORTB = bin_out[digit];

Autor: Michael M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
schau dir doch mal das geisterleuchte an...
du wirst die leds der zuletzt gemultiplexten ziffer erkennen

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Anja (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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];

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?".

Autor: Anja (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Geisterlicht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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!

Autor: Geisterlicht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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 :-)

Autor: Anja (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Da Dieter (dieter)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 ;)

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

...

Autor: Bernd Wiebus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.