Forum: Mikrocontroller und Digitale Elektronik Stoppuhr Anzeigeproblem


von Tom (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe vor zur Übung eine Stoppuhr zu programmieren. Sie soll 
zweistellig die Minuten, zweistellig die Sekunden und einstellig die 
Zehntel anzeigen.
Das hat (mit etwas Recherche im Internet) auch bis her recht gut 
funktioniert. Jetzt möchte ich die Zeit durch 7 Segment Anzeigen 
ausgeben lassen und Letztere per Multiplexverfahren ansteuern.
Das Verfahren habe ich verstanden und es funktioniert auch soweit, mein 
Problem besteht darin, dass ich einen Tipp benötige, wie ich meine Zeit 
an den binär am PortC ausgeben kann.
Muss ich dazu 2 7Segment Decoder benutzen, oder kann ich die 5 Segmente 
auch mit einem Decoder ansteuern?
und wie bringe ich dem Decoder bei, dass die Segmente nur Zahlen von 1-9 
verstehen und nicht von 1-15?

Ich nutze den Atmega8 und programmiere per Bascom.
Der 7Segment Decoder ist ein CD4511BE
und mein Bisheriges Programm befindet sich im Anhang
(im Moment werden nur 2 stellen angesteuert und diesen sind noch keine 
Zahlenwerte zugeordnet. Sie zeigen also nur 0 an)

Ich hoffe ich konnte mein Problem einigermaßen verständlich beschreiben, 
falls nicht, einfach fragen =)

Vielen Dank schonmal für eure Hilfe,
Grüße,
Tom

von Thomas E. (thomase)


Lesenswert?

> und wie bringe ich dem Decoder bei, dass die Segmente nur Zahlen von 1-9
> verstehen und nicht von 1-15?
Dem kannst du gar nichts beibringen. Du musst dafür sorgen, daß er nur 
0..9 zu sehen bekommt.

> Der 7Segment Decoder ist ein CD4511BE

Klick doch einfach auf den Link. Da findest du nicht nur das Datenblatt, 
sondern auch die Threads, die sich mit dem Thema befassen.

mfg.

von Matthias Li. (Gast)


Lesenswert?

Also Angesteuert werden diese Treiber MEISTENS durch BCD (Binär Codierte 
Dezimalzahl)

also mit 4 BIT Daten

D C B A - PIN
0 0 0 0 = 0
0 0 0 1 = 1
0 0 1 0 = 2
0 0 1 1 = 3
0 1 0 0 = 4
0 1 0 1 = 5
0 1 1 0 = 6
0 1 1 1 = 7
1 0 0 0 = 8
1 0 0 1 = 8
(ICH HOFFE DIE TABELLE IS RICHTIG)


Ich kann mich nicht mehr genau entsinnen aber gabs bei bascom nicht mal 
die Lookup Table? damit kannst du den Zahlen 0..9 einen entsprechenden 
Wert zuweisen, um mit deinem PORT die Daten auszugeben. Multiplexen kann 
man damit Eben je nach CC oder CA mit PNP oder NPN Transistor die 
7-Segment ein ausschalten

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Matthias Li. schrieb:
> 1 0 0 1 = 8

eher 9 ;)

von Tom (Gast)


Lesenswert?

Hallo,
vielen Dank schonmal für eure Antworten.
Mit dem Multiplexen komme ich wunderbar zurecht. Mein Problem ist, in 
meinem Fall den PortC jeweils so einzustellen, dass er die Einzelnen 
Stellen genau so wie in dieser Tabelle anzeigt:

Matthias Li. schrieb:
> also mit 4 BIT Daten
>
> D C B A - PIN
> 0 0 0 0 = 0
> 0 0 0 1 = 1
> 0 0 1 0 = 2
> 0 0 1 1 = 3
> 0 1 0 0 = 4
> 0 1 0 1 = 5
> 0 1 1 0 = 6
> 0 1 1 1 = 7
> 1 0 0 0 = 8
> 1 0 0 1 = 9                            ( <- habe diese Stelle korrigiert )


Ich habe die einzelnen Ziffern schon in den Variablen
St1, St2 , M1,M2,  Sek1,Sek2    gespeichert.     (Format SS:MM:SS)

Meine Überlegung war, es irgendwie so zu lösen:
"Multiplex" ist die Funktion, die 100 mal pro Sekunde ausgelöst wird.

Multiplex:
       Timer2 = Timer2_start
       portb = &BSt1
       Portb = &B00000001
       waitms 2
       portb = &BSt2
       Portb = &B00000010
       waitms 2
       portb = &BM1
       Portb = &B00000100
       waitms 2
       portb = &BM2
       Portb = &B00001000
       waitms 2
       portb = &BSek1
       Portb = &B00010000
       waitms 2
       portb = &BSek2
       Portb = &B00100000
       waitms 2
Return


aber das funktioniert nicht :-/

von Route_66 H. (route_66)


Lesenswert?

Tom schrieb:
> Meine Überlegung war, es irgendwie so zu lösen:
> "Multiplex" ist die Funktion, die 100 mal pro Sekunde ausgelöst wird.
>
> Multiplex:
>        Timer2 = Timer2_start
>        portb = &BSt1
>        Portb = &B00000001
>        waitms 2
>        portb = &BSt2
usw.

1. Du hast Multiplex nicht verstanden! Die Funktion, die mehrmals pro 
Sekunde aufgerufen wird, stellt immer nur eine Stelle dar. z.B. den 
Minuten-Zehner. Beim nächsten Mal den Minuten-Einer dann den 
Sekunden-Zehner usw. Bei 100 mal pro Sekunde flimmert es aber zu sehr, 
bei Dir wären 400-500 mal besser.

Solche Sachen:
>        portb = &BSt1
>        Portb = &B00000001
>        waitms 2
führen dazu, daß am Portb nur die 1 für 2 ms ansteht, St1 wird ja gleich 
überschrieben! Die Konstruktion "&BSt1" sieht sowieso eigenartig aus. 
Gibts das überhaupt unter BASCOM?

von oldmax (Gast)


Angehängte Dateien:

Lesenswert?

Hi
Was willst du mit Decodern? Wie viel freie Portpins für die Anzeige 
stehen denn zur Verfügung? 7 für die Segmente und 5 für die Stellen 
wären 12 und dann kann der Controller den Code liefern. Dazu gehst du 
wie folgt vor:
Du erzeugst ein Array of Byte, wo du den Portpins für die Segmente alias 
a-g die Signallage zuordnest. Bei gemeinsamer Kathode "1"er und bei 
gemeinsamer Anode "0"er.
Das Array fängt bei Index 0 an, also ist dein Segmentcode für 0 im 
Arrayfeld mit dem Index 0 Hast du eine Zahl 4, nimmst du die 
Basisadresse vom Array (das ist die mit dem Index0) und addierst darauf 
deine Zahl. Die zeigt dann auf die Adresse mit dem Index 4 und damit auf 
den Code der Anzeige 4. Somit hast du die Ziffer konvertiert. Um eine 
mehrstellige Zahl zu konvertieren baust du dir einen Zahlenpuffer, 
bspw.für deine Zeit im Format <m><m><s><s><z>. Dann setzt du einen Index 
auf <z>, wobei auch diesmal <z> im Puffer mit der niedrigsten Adresse 
steht.Das Konstrukt für den programmcode kann ich dir in BASCOM nicht 
geben, da das nicht meine Welt ist, aber etwas allgemein gültiges schon.
Zeitpuffer -> Array of Byte mit fünf Feldern
Matrix -> Array of Byte mit zehn Feldern
Anzeigepuffer: Array of Byte mit fünf Feldern

For j = 0 to 4
   Lade Zeitpuffer(i)
   Addiere auf Basisadresse Matrix Wert aus Zeitpuffer
   Adressiere damit Matrixpuffer
   kopiere den dort abgelegten Wert
   nach Anzeigepuffer
Next
Dieser Block wird im Hauptprogramm entweder bei Änderung der Werte im 
Zahlenpuffer oder in jedem Zyklus bearbeitet.
Im Interrupt setzt du im mSek. Bereich einfach einen Zeiger auf den 
Ausgabepuffer, wo deine Matrix der anzuzeigenden Zahlen steht und mit 
Ziffer 0 adressierst du Anzeigepuffer(0). Zur Selektion der Ziffer 
eignet sich ein Byte, in dem du einfach ein Bit von der ersten Ziffer 
bis zur letzten schiebst und dann von vorn beginnt, Zählt dein 
Adresszähler vom Ausgabepuffer mit, hast du die Adresse des 
Anzeigepuffers. In fedem Interrupt erst Anzeige dunkel schalten, nächste 
Adresse für Ausgabepuffer. Stellenbit schieben. Grenzwert überschritten 
dann Adresse Ausgabepuffer 0 und Schiebebit auf Anfang
Schließlich den Segmentcode aus Anzeigepuffer auf den Port schreiben und 
die Ziffern wieder zuschalten.
Damit löst du Zahlenwerte von deiner Anzeige. Es ist egal, aus welcher 
Zahlenaufbereitung der Code im Ausgabepuffer kommt. Die ISR greift nur 
auf den Puffer zu, was da drin steht, ist ihr völlig egal. Auch wenn du 
da "HILFE" rein schreibst, wird sie es abbilden. Die Skizze soll dir die 
Trennung ISR und Main mit der gemeinsamen Schnittstelle "Anzeigepuffer" 
zeigen

von Matthias L. (lindner8712)


Lesenswert?

Es geht auch mit lookup.. falls du Speicherprobleme hast

$regfile = "m32def.dat"
$crystal = 16e6
$baud = 100000
$hwstack = 100
$swstack = 500
$framesize = 500
$sim

' Sieben Segment Funktion
Config Timer0 = Timer , Prescale = 1
Config Portc = Output
Enable Timer0
Enable Interrupts

On Timer0 Tmr0isr
Dim Zahl As Byte

Main:
NOP
NOP
Goto Main

Siebenseg:
Data &B00111111 ,                                           '0
Data &B00000110 ,                                           '1
Data &B01011011 ,                                           '2
Data &B01001111 ,                                           '3
Data &B01100110 ,                                           '4
Data &B01101101 ,                                           '5
Data &B01111101 ,                                           '6
Data &B00000111 ,                                           '7
Data &B01111111 ,                                           '8
Data &B01101111 ,                                           '9
Data &B01110111 ,                                           'A
Data &B01111111 ,                                           'B
Data &B00111001 ,                                           'C
Data &B00111111 ,                                           'D
Data &B01111001 ,                                           'E
Data &B01101001 ,                                           'F

Tmr0isr:
' Mache irgendwas damit... z.b. Laufvariable und Select & case
' Segemt 1..2..3..4..5..6..1..2..3..4..5..6 usw...
' z.B.
Portb = 0

' Das ist die Funktion die deine Zahl in eine für deine
' 7 Segment Anzeige "lesbare Zahl" umwandelt
Portc = Lookup(zahl , Siebenseg)

Select Case Stelle
 Case 1 : Portb = 1                                   'Stellen ansteuern
 Case 2 : Portb = 2                                   'Stellen ansteuern
 Case 3 : Portb = 4                                   'Stellen ansteuern
 Case 4 : Portb = 8                                   'Stellen ansteuern
 Case 5 : Portb = 16                                  'Stellen ansteuern
 Case 6 : Portb = 32                                  'Stellen ansteuern
End Select
Incr Stelle
If Stelle = 7 Then
Stelle = 1
End If


Return

So sollte es klappen und erfüllt alle Anforderungen des Multiplexing...

Hoffe ich konnte helfen!

EDIT: Falsche Reihenfolge :-)

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

Wow, das sind mal Antworten =D
DANKE!
Ich werde mich in den nächsten Tagen mal dran machen und eure Tipps 
durcharbeiten; beim ersten Durchlesen habe ich noch nicht alles 
verstanden, aber ich schau mal was so draus wird.

Matthias Lindner schrieb:
> Main:
> NOP
> NOP
> Goto Main

Ehm, NOP ist laut Wikipedia ein Befehl der nichts macht, richtig?



Route 66 schrieb:
> Solche Sachen:
>>        portb = &BSt1
>>        Portb = &B00000001
>>        waitms 2
> führen dazu, daß am Portb nur die 1 für 2 ms ansteht, St1 wird ja gleich
> überschrieben! Die Konstruktion "&BSt1" sieht sowieso eigenartig aus.
> Gibts das überhaupt unter BASCOM?

Ich glaube nicht, dass es diesen Befehl in Bascom gibt, er funktioniert 
auch nicht ;). Ich wollte damit nur deutlich machen, was ich vorhabe.
Übrigens funktioniert die Anzeige wunderbar, wenn ich dem Port bestimmte 
Zahlen vorgebe, z.B.:

Multiplex:
       Timer2 = Timer2_start
       portB = &B00000101            '5 wird an erster Stelle ausgegeben
       PortC = &B00000001
       waitms 2

       portB = &B00000011            '3 wird an zweiter Stelle 
ausgegeben
       PortC = &B00000010
       waitms 2

       portB = &B00001000            '8 wird an dritter Stelle 
ausgegeben
       PortC = &B00000100
       waitms 2

       portB = &B00000011             '3 wird an vierter Stelle 
ausgegeben
       PortC = &B00001000
       waitms 2
      .....usw
Return

------------------------------------------------------------------------ 
---
Wichtiger Nachtrag:
Mit dem PortC werden übrigens die einzelnen Stellen nacheinander 
angezeigt. Dh. PortC wird zum Multiplexen verwendet. Ich hatte in meinem 
letzten Beitrag einen Fehler gemacht. Dort hatte ich erneut PortB 
anstelle von PortC geschrieben. 
------------------------------------------------------------------------ 
---

Ich müsste also nur dem PortB beibringen, wie er die Ziffern von 1-9, 
die ich in den Variablen St1, St2 , M1,M2,  Sek1 und Sek2 gespeichert 
habe binär an den 7Segment-Decoder weitergibt. Weil der versteht nur 
Bit-förmige Informationen.

Ich melde mich wieder, falls ich irgendwo nicht alleine weiterkomme. 
Falls jemand noch einen Tipp für mich hat, ich bin dankbar für alles! ;)

Vielen Dank nochmal!
Grüße,
Tom

von Tom (Gast)


Lesenswert?

Matthias Lindner schrieb:
> Es geht auch mit lookup..

Aaaaaahhhh ich habe gerade mal gegoogelt, was denn der Lookup Befehl 
ist.
Ich glaube damit werde ich es mal versuchen.

Grüße, Tom

von Tom (Gast)


Lesenswert?

Juhuuhh, hat schon funktioniert!
Vielen Dank nochmal für eure Hilfe. Ich werde mir die anderen Tipps 
trotzdem nochmal durchlesen und evtl. noch etwas dazulernen. ;)

Grüße,
Tom



Stoppuhr (allerdings bisher noch ohne Start- oder Stoppknopf):

$regfile "m8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 2500


Ddrd = &B111110011                                          'Pins an 
PortD 0,1,4,5,6,7 sind Outputs;   Pins an PortD.2,3 sind Inputs
Portd = &B00001100                                          'interne 
Pullups von Pd2 und Pd3 einschalten
Ddrb = &B11111111                                           'Alle Pins 
an PortB sind Outputs
Portb = &B00000001 
'Anfangsstatus für PortB
Ddrc = &B000001111
Dim Timer1_start As Word
Dim Timer2_start As Word

Dim Sekunde As Word
Dim Minute As Word                                          'Varriablen 
deffinieren
Dim Stunde As Word

Dim Sek1 As Byte
Dim Sek2_zwischenergebnis As Byte
Dim Sek2 As Byte
Dim M1 As Byte
Dim M2_zwischenergebnis As Byte
Dim M2 As Byte
Dim St1 As Byte
Dim St2_zwischenergebnis As Byte
Dim St2 As Byte

Config Timer1 = Timer , Prescale = 1024                     'Timer1 als 
Timer definieren, mit einem Vorteilungsdivisor von 1024
On Timer1 Sekundenzaehlen                                   'wenn Timer1 
überläuft, dann gehe zur Funktion "Sekundenzaehlen"
Enable Timer1                                               'Timer1 
einschalten
Config Timer2 = Timer , Prescale = 128                      'Systemtakt 
wird durch 100 geteilt und dann an Timer2 übergeben. Timer2 zählt
On Timer2 Multiplex                                         'diesen dann 
und lößt bei jedem Überlaufen einen Interrupt aus.
Enable Timer2                                               'Timer2 
einschalten

Enable Interrupts                                           'Interrupts 
global einschalten
Sekunde = 0
Minute = 0
Stunde = 0
Timer1_start = 64559                                        'Startwert 
des Timers, sodass die Sekundenzählfrequenz passt. Er zählt von 64559 
bis 65535. (= 976 Zählschritte)
Timer2_start = 177
                                         'Startwert des Timers. Er zählt 
von 177 bis 255. (= 78 Zählschritte)
Timer1 = Timer1_start                                       'Timer2 soll 
den Startwert 64559 bekommen
Timer2 = Timer2_start
Dim Stunden As Byte


Do


           If Sekunde > 59 Then                             'erklärt 
sich von selbst                            .
           Minute = Minute + 1
           Sekunde = 0
           End If

           If Minute > 59 Then                              'erklärt 
sich von selbst
           Stunde = Stunde + 1
           Minute = 0
           End If

'Zerlegen der einzelnen Teilzeiten in ihre jeweiligen Stellen auf der 
Anzeige (Bsp:  46 Sekunden;   Sek1 = 4, Sek2 = 6 )
          Sek2 = Sekunde Mod 10                             'Sek2 ist 
die zweite stelle der Sekundenanzeige. S2 = Rest von sekunden/10
          Sek2_zwischenergebnis = Sekunde - Sek2 
'Hilfsergebnis ist aktuelle Sekunden - Einer
          Sek1 = Sek2_zwischenergebnis / 10                 'Sek1 ist 
die erste stelle der Sekundenanzeige. S1 ist Hilfsergebnis/10

          M2 = Minute Mod 10                                'M2 ist die 
zweite stelle der Minutenanzeige. M2 = Rest von Minuten/10
          M2_zwischenergebnis = Minute - M2 
'Hilfsergebnis ist aktuelle Minuten - Einer
          M1 = M2_zwischenergebnis / 10                     'M2 ist die 
erste stelle der Minutenanzeige. M1 ist Hilfsergebnis/10

          St2 = Stunde Mod 10                               'St2 ist die 
zweite Stelle der Hundertstelanzeige. H2 = Rest von Hundertstel/10
          St2_zwischenergebnis = Stunde - St2 
'Hilfsergebnis ist aktuelle Stunden - Einer
          St1 = St2_zwischenergebnis / 10                   'St1 ist die 
erste stelle der Stundenanzeige. St1 ist Hilfsergebnis/10

           Print St1 ; " " ; St2 ; " " ; M1 ; " " ; M2 ; " " ; Sek1 ; " 
" ; Sek2
Loop



End

   Sekundenzaehlen:                                         'wenn der 
Timer1 überläuft, soll die Varriable "Sekunde" um 1 erhöht werden, dann
       Incr Sekunde                                         'soll Timer1 
wieder am Wert "Timerstart" anfangen zu zählen. Außerdem soll Led2
       Timer1 = Timer1_start                                'dabei 
Blinken.
       Toggle Portd.5
       Return

Multiplex:
       Timer2 = Timer2_start                                'Timer2 auf 
Startwert zurücksetzen
       Portc = Lookup(sek1 , Tabelle) 
'Lookup-Befehl: (Für jeden Wert von Sek1 wird der dazugehörige wert in 
der Tabelle an PortC ausgegeben)
       Portb = &B00000001                                   'Erste 
Stelle der Sekundenanzeige einschalten
       Waitms 2
       Portc = Lookup(sek2 , Tabelle) 
'Lookup-Befehl: (Für jeden Wert von Sek2 wird der dazugehörige wert in 
der Tabelle an PortC ausgegeben)
       Portb = &B00000010                                   'Zweite 
Stelle der Sekundenanzeige einschalten
       Waitms 2
       Portc = Lookup(m1 , Tabelle) 
'Lookup-Befehl: (Für jeden Wert von M1 wird der dazugehörige wert in der 
Tabelle an PortC ausgegeben)
       Portb = &B00000100                                   'Erste 
Stelle der Minutenanzeige einschalten
       Waitms 2
       Portc = Lookup(m2 , Tabelle) 
'Lookup-Befehl: (Für jeden Wert von M2 wird der dazugehörige wert in der 
Tabelle an PortC ausgegeben)
       Portb = &B00001000                                   'Zweite 
Stelle der Minutenanzeige einschalten
       Waitms 2
Return


Tabelle:
      Data &B00000000,                                            '0
      Data &B00000001,                                            '1
      Data &B00000010,                                            '2
      Data &B00000011,                                            '3
      Data &B00000100,                                            '4
      Data &B00000101,                                            '5
      Data &B00000110,                                            '6
      Data &B00000111,                                            '7
      Data &B00001000,                                            '8
      Data &B00001001,                                            '9

von Matthias L. (lindner8712)


Lesenswert?

Tom schrieb:
> Ehm, NOP ist laut Wikipedia ein Befehl der nichts macht, richtig?

Jup, genau richtig... NOP = No Operation aus dem Assembler Befehlssatz.
Der verbrät einfach nur 1 Takt!

das ist der große Vorteil von Bascom das man zwischendruch mal schnell 
ein paar Assemblerzeilen schreiben kann. Geht in AVR GCC aber nicht ganz 
soo schön!

Zu deiner ISR:

Waitms hat im Interrupt mal garnix verloren! damit bremst du das ganze 
Programm aus! In der Zeit kann kein anderer Interrupt erfolgen, da das 
I-Flag im SREG während der ISR gelöscht wird!

Folgende Rechnung: Systemtakt: 1 MHZ / Prescale: 128 / Takte bis 
Überlauf:78

1/1MHZ = 1µS

Also: 1µS X 128 X 78 = 10ms von Interrupt zu Interrupt... Wenn du das 
mit waitms machst, bekommst du rechnerisch bei 6 Anzeigen schon 10ms 
Wartezeit!! Sowas kann man in der Do - Loop schleife machen aber NICHT 
in einer ISR!

Das was du machst ist kein Richtiges Multiplexing.

Multiplexing sieht so aus:


HAUPTPROGRAMM
INTERRUPT: Anzeige 1 aktualisieren
HAUPTPROGRAMM
INTERRUPT: Anzeige 2 aktualisieren
HAUPTPROGRAMM
INTERRUPT: Anzeige 3 aktualisieren
HAUPTPROGRAMM
INTERRUPT: Anzeige 4 aktualisieren
HAUPTPROGRAMM
INTERRUPT: Anzeige 5 aktualisieren
HAUPTPROGRAMM
INTERRUPT: Anzeige 6 aktualisieren
HAUPTPROGRAMM

und dann wieder von Vorne!

In SUMME: Bei jedem Interrupt von Timer2 1!!!!!!!!!!! Anzeige 
aktualisieren und dann auch bis zum nächsten Interrupt stehen lassen.

: Bearbeitet durch User
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.