Forum: Mikrocontroller und Digitale Elektronik Multiplex 7-Segment-Anzeige mit gemischten Ports


von Axel L. (ligonap)


Lesenswert?

Hallo,

ich möchte im Gegensatz zu einer direkten Ansteuerung der 
7-Segment-Anzeige (Beitrag "Re: 7-Segment-Anzeigen mit gemischten Ports") 
eine Multiplex-Ansteuerung ebenfalls mit gemischten Ports realisieren. 
Bisher konnte ich nur einen C-Code mit der Verwendung von "interrupt.h" 
finden 
(http://extremeelectronics.co.in/avr-tutorials/multiplexed-seven-segment-displays-part-ii/).
interrupt.h stört sich aber mit "struct" und "void 
OutputDigit(...)"-Programmierung, daher möchte ich sie nicht verwenden.

Weiterhin ist mir noch nicht ganz klar, wie ich die drei Ports der 
Anzeigensteuerung mit dem Timer2 verbinden kann.
1
unsigned char muxflag;
2
3
void counter_init()
4
{
5
  TCCR2B |= (1<<CS22) ; // Prescaler = FCPU/256 = 62500Hz -> 81.38Hz pro Segment
6
  TIMSK2 |= (1<<TOIE2) ; //  Overflow Interrupt Enable
7
  TCNT2=0 ;  // Init Counter - Zähler auf 0 setzen
8
}
9
10
int main()
11
{
12
counter_init();
13
...
14
}
15
16
void Multiplex(void)
17
{
18
  ?????? // Bezug zum Timer2??
19
  
20
  if(muxflag==0)
21
  {
22
  PORTC |= (1<<PC5);
23
  PORTC &= ~(1<<PC6);
24
  PORTC &= ~(1<<PC7),
25
  }
26
27
  if(muxflag==1)
28
  {
29
  PORTC &= ~(1<<PC5);
30
  PORTC |= (1<<PC6);
31
  PORTC &= ~(1<<PC7);
32
  }
33
34
  if(muxflag==2)
35
  {
36
  PORTC &= ~(1<<PC5);
37
  PORTC &= ~(1<<PC6);
38
  PORTC |= (1<<PC7);
39
  
40
  muxflag=0;
41
  }
42
   else
43
   muxflag = muxflag + 1;
44
}

Wie bekomme ich den Bezug zum Timer2 hin?

von Karl H. (kbuchegg)


Lesenswert?

Ob du die Anzeige komplett über einen Port abhandelst oder über mehrere 
hat nichts mit dem Timer zu tun.


> Wie bekomme ich den Bezug zum Timer2 hin?

Was willst du uns damit sagen?
Welchen Bezug?

Der Timer tickt vor sich hin und löst regelmässige Interrupts aus. Die 
Interrupts werden in der zugehörigen ISR behandelt. End of Story.

Vielleicht solltest du dich erst mal über Timer und deren Interrupts 
schlau machen, ehe du dann diesen Mechanismus benutzt um dir einen 
regelmässigen Funktionsaufruf-Takt zu erzeugen um damit die ANzeige zu 
multiplexen.

von Axel L. (ligonap)


Lesenswert?

Nun, da ich es ja gewohnt bin alles irgendwie selber zu machen, hier der 
aktuelle Code meiner Counter-Anzeige (300 -> 000), der recht gut läuft.
1
#include <avr/io.h>
2
#include <avr/iom644.h>
3
#include <avr/interrupt.h>
4
5
6
uint8_t muxflag;
7
uint8_t e, z, h;
8
9
10
//Segmente für die Zahlenwerte
11
uint8_t num[] =
12
{
13
  0b11111100 ,   // 0
14
  0b01100000 ,   // 1
15
  0b11011010 ,   // 2
16
  0b11110010 ,   // 3
17
  0b01100110 ,   // 4
18
  0b10110110 ,   // 5
19
  0b10111110 ,   // 6
20
  0b11100000 ,   // 7
21
  0b11111110 ,   // 8
22
  0b11110110     // 9
23
};
24
25
26
// Timer2 für Displayrefresh
27
void counter_init()
28
{
29
  TCCR2B |= (1<<CS22) ; // Prescaler = FCPU/256 = 62500Hz -> 81.38Hz pro Segment
30
  TIMSK2 |= (1<<TOIE2) ; //  Overflow Interrupt Enable
31
  TCNT2=0 ;  // Init Counter - Zähler auf 0 setzen
32
  sei();
33
}
34
35
36
// ISR für Timer2
37
ISR(TIMER2_OVF_vect)
38
{
39
  
40
  cli();    // Interrupts nicht zulassen
41
42
  // Alle Segmente aus
43
  PORTC &= ~(1<<PC5);
44
  PORTC &= ~(1<<PC6);
45
  PORTC &= ~(1<<PC7);
46
47
  if(muxflag==0)
48
  {
49
  PORTA = num[e-1];
50
  PORTC |= (1<<PC5);
51
  PORTC &= ~(1<<PC6);
52
  PORTC &= ~(1<<PC7);
53
  }
54
55
  if(muxflag==1)
56
  {
57
  PORTA = num[z-1];
58
  PORTC &= ~(1<<PC5);
59
  PORTC |= (1<<PC6);
60
  PORTC &= ~(1<<PC7);
61
  }
62
63
  if(muxflag==2)
64
  {
65
  PORTA = num[h-1];
66
  PORTC &= ~(1<<PC5);
67
  PORTC &= ~(1<<PC6);
68
  PORTC |= (1<<PC7);
69
  
70
  muxflag=0;
71
  }
72
   else
73
  {
74
  muxflag = muxflag + 1;
75
  }
76
  
77
  sei();    // Interrupts zulassen
78
79
}
80
81
82
83
// Timer für Wartezeit
84
void delay_ms(int ms) 
85
{
86
  TCCR0B |= (1<<CS02) | (1<<CS00) ; //prescale 1024 - Taktfrequenz/1024 = 15625Hz (Initialisierung)
87
  int i ;
88
  for(i=0; i<ms; i++) {  //Anzahl der ms-Schleifen
89
90
    while(TCNT0<8);  // 
91
    TCNT0=0; // 256er AufwärtsZähler auf 0 setzen
92
  }
93
  TCCR0B=0; // Timer auf 0 setzen
94
}
95
96
97
98
void luxeon()
99
{
100
  PORTD |= (1<<PD3) ; // Luxeon LED ein
101
  delay_ms(90);
102
  PORTD &= ~(1<<PD3) ; // Luxeon LED aus
103
}
104
105
106
int main()
107
{
108
109
// Als Ausgänge und Eingänge sind Definiert:
110
DDRA = 0b11111111;
111
DDRB = 0b11111111;
112
DDRC = 0b11111111;
113
DDRD = 0b00111111;
114
115
// Startwerte
116
muxflag = 0;
117
h = 4;
118
z = 1;
119
e = 1;
120
121
counter_init();
122
123
while ( PIND & (1<<PIND7) ) ;
124
125
  for( h = 3; h > 0; h=h-1) {
126
  for( z = 10; z > 0; z=z-1 ) {
127
      for( e = 10; e > 0; e=e-1 ) {
128
 
129
    
130
  luxeon();
131
    delay_ms(120);
132
    while ( PIND & (1<<PIND7) ) ;
133
134
      }
135
    }
136
  }
137
138
}

Wenn ich die Taste an PD7 nach der letzten Ziffer "000" drücke, bekomme 
ich eine "0" in der mittleren Anzeige. Wie lässt sich dies vermeiden?
Auch sind ganz ganz leichte Schatteneffekte innerhalb der Segmentanzeige 
zu sehen. Wird die Anzeige zu schnell angesteuert bzw. kommen meine 
Transistoren (BC548C) oder Segmente (Kingbright SC39-11PBWA) nicht mit?

von Frager (Gast)


Lesenswert?

Die "0" in der mittleren Anzeige läßt sich durch passende Steuerung der 
Treiberelektronik vermeiden.
Ich vermute jetzt mal, dass du LED Anzeigen verwendest. Schatteneffekte 
können an Störungen des Zeitablaufs beim Multiplex oder an Schwankungen 
der Segmentströme liegen. Ohne Schaltplan läßt sich das aber schlecht 
beurteilen.

von Karl H. (kbuchegg)


Lesenswert?

Axel L. schrieb:

> Wenn ich die Taste an PD7 nach der letzten Ziffer "000" drücke, bekomme
> ich eine "0" in der mittleren Anzeige. Wie lässt sich dies vermeiden?

Indem du deinem Hauptprogramm die übliche Hauptschleife verpasst, aus 
der das Programm nie herauskommt.
Dein Programm hat keine Hauptschleife sondern erreicht in diesem Fall 
dann den return in main(). D.h. dir wird die Kontrolle entzogen, der gcc 
Standardcode übernimmt und schaltet alle Interrupts ab. Und damit steht 
dann dein Multiplexing

> Auch sind ganz ganz leichte Schatteneffekte innerhalb der Segmentanzeige
> zu sehen. Wird die Anzeige zu schnell angesteuert bzw. kommen meine
> Transistoren (BC548C) oder Segmente (Kingbright SC39-11PBWA) nicht mit?

Höchst wahrscheinlich.
Du könntest zb das Weiterschalten vom muxflag komplett zwischen 
Abschalten der alten Belegung (und zwar komplett! auch den PORTA!) und 
Einschalten der neuen Belegung legen. Dann verbrauchst du dort ein 
bischen Zeit und das könnte dann schon reichen, dass dir die winzigen 
Kapazitäten nicht mehr in die Quere kommen.


Dass h z e immer um 1 größer sein sollen, alls die Zahl die sie anzeigen 
müssen, ist mehr als unschön.
Auch nimmt man beim Multiplexen gerne die eigentlichen Ziffern aus der 
Multiplexroutine raus. Die Multplexroutine zeigt einfach 3 Bytes an. Was 
diese darstellen interessiert sie nicht. Dann kann man zb auch ein '-' 
als Vorzeichen anzeigen, oder irgendwelche coole Einschaltanimationen, 
etc.
Dafür schreibt man sich eine Funktion, die eine Zahl übernimmt und diese 
3 Bytes bestimmt, die die Multiplexroutine anzeigen soll:

zb
1
void ito7Seg( int number )
2
{
3
  e = num[number % 10];
4
  number = number / 10;
5
6
  z = num[number % 10];
7
  number = number / 10;
8
9
  h = num[number % 10];
10
}

In der ISR dann
1
   ...
2
3
  if(muxflag==0)
4
  {
5
    PORTA = e;
6
    PORTC |= (1<<PC5);
7
    PORTC &= ~(1<<PC6);
8
    PORTC &= ~(1<<PC7);
9
  }
(und natürlich sinngemäss auch für die anderen anzuzeigenden Bytes

und dann kann man den Zähler so schreiben und ist das Denken in 
Hundertern, Zehnern und Einern los
1
....
2
  while( 1 ) {
3
4
    for( counter = 0; counter < 300; ++counter ) {
5
      ito7Seg( counter );
6
      _delay_ms( 150 );
7
    }
8
  }
9
}



NB: Nimm den cli und sei aus der ISR raus! Interrupts sind wärend einer 
ISR sowieso ausgeschaltet. Der sei am Ende der ISR kann dir aber 
Probleme machen, weil die Interrupts dann ein klein wenig zu früh 
aktiviert werden.

von oldmax (Gast)


Lesenswert?

Hi
Da ich in C Problemehabe, begrenzt sich meine Hilfe mal nur auf die 
Vorgehensweise. In Assembler trigger ich den Multiplexer auch aus der 
ISR heraus, jede mSek. Im Prinzip wie folg:
Timer_ISR:
   Push ...
   RCALL Multiplex
   ....
   POP ..
RETI
Im Multiplexer werden zuerst alle Ziffern durch Wegnahme des Gemeinsamen 
ausgeschaltet, das Ziffern-Select -Bit geschoben, der Code für die 7 - 
Segmentanzeige auf die Ausgänge geschaltet und anschließend das 
Ziffern-Select-Bit auf die Gemeinsame aufgeschaltet. Dadurch wird 
verhindert, das die Ziffer noch kurz mit den Segmenten der vorherigen 
Anzeige angesteuert wird. Auch wenn es nur ein paar µSek. sind, es 
bleibt doch ein leichtes Glimmen.
Gruß oldmax

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.