Hallo liebe Gemeinschaft,
ich bin ein blutiger Anfänger was C und Mikrocontroller angeht, und
versuche aus schulungszwecken eine 4 stellige 7 Segment Anzeige mit
Atmel Studio in Sprache C zu programmieren.
Ausgangssituation.
Ich habe einen Board (fertigboard) mit 4 stellige 7 Segment Anzeige,
einen Controller Atmega32A, 8 Pins (PORTB) für die 7 Segment Anzeige und
4 Pins (PORTA) für jede der 4 Stellen.
Ich kann problemlos eine Stelle ansteuern und von 0 bis 9 automatisch
durchlaufen lassen, und zwar so:
char ledmuster [10]= // Array
{
0b11000000, // muster 0
0b11111001, // muster 1
0b10100100, // muster 2
0b10110000, // muster 3
0b10011001, // muster 4
0b10010010, // muster 5
0b10000010, // muster 6
0b11111000, // muster 7
0b10000000, // muster 8
0b10010000, // muster 9
};
#define F_CPU 16000000U
#include <util/delay.h>
#include <avr/io.h>
uint8_t Zaehler=0;
int main(void)
{
DDRA=0b11111111;
DDRB=0b11111111;
DDRD=0b00000000;
while (1)
{
PORTD=0b11111111;
if(PORTD)
for (Zaehler=0; Zaehler<=9; Zaehler++)
{
PORTB=ledmuster[Zaehler];
_delay_ms(1000);
}
}
}
Das funktioniert eiwandfrei.
Sobald aber 2te Stelle zukommen soll, reicht mein wissen nicht aus wie
ich das hinbekommen soll.
Ich weiß, dass ich die 4 Pins für jede Stelle einzeln und zeitversetzt
ansteuern muss (also im Multiplex), aber ich weiß nicht wie!
Ich brauche wenigstens ein Tipp.
Ich habe keine Vorstellung wie ich 2 Schleifen gleichzeitig laufen
lassen soll, die eine Schleife zählt hoch, die andere zeigt die 4
Stellen an.
Bitte um Hilfe, Danke im voraus!
Gruß
DimiX schrieb:> und> versuche aus schulungszwecken eine 4 stellige 7 Segment Anzeige mit> Atmel Studio in Sprache C zu programmieren.
Das Ziel ist doch ein Mikrocontroller, nicht wahr?
Warum schreibst du hier dann im Bereich "PC-Programmierung"?
Du programmierst doch nicht deinen PC um die 7 Segment Anzeige
anzusteuern, oder doch?
DimiX schrieb:> Ich weiß, dass ich die 4 Pins für jede Stelle einzeln und zeitversetzt> ansteuern muss (also im Multiplex), aber ich weiß nicht wie!
Dann tue das doch, also
1. Pin für Stelle aktivieren
2. zugehörige Ziffer auf Stelle ausgeben
3. Warten (z.B. 5 ms)
4. zurück zu 1.
Alle paar Durchläufe, also z.B. jeden 200ten kannst du dann die
anzuzeigenden Ziffern ändern.
Dein delay(1000) ist Gift für den Programmablauf, weil es den
Prozessor für eine ganze Sekunde lahmlegt. Die 5ms bei 3. genauso -
eigentlich macht man das mit einem Timer-Interrupt.
Wolfgang schrieb:> Dann tue das doch, also> 1. Pin für Stelle aktivieren> 2. zugehörige Ziffer auf Stelle ausgeben> 3. Warten (z.B. 5 ms)> 4. zurück zu 1.
Das hab ich versucht,
Nur dann durchläuft das Programm von 0 bis 9999 und wenigen Sekunden
durch.
Und auf der Anzeigen ist kaum was zu erkennen wie es so schnell
durchläuft, dass man nur 4 x 8 sieht.
Der soll ja irgendwo zwischen den Zahlen eine Sekunde warte, wie eine
Uhr.
7segmentdepp schrieb:> Das Ziel ist doch ein Mikrocontroller, nicht wahr?
Es tut mir leid, wenn ich falschen Bereich gewählt habe, ich habe keinen
sinnvolleren gefunden.
Welchen hätte ich nehmen müssen?
Gruß
Wolfgang schrieb:> Dann tue das doch, also> 1. Pin für Stelle aktivieren> 2. zugehörige Ziffer auf Stelle ausgeben> 3. Warten (z.B. 5 ms)> 4. zurück zu 1.>> Alle paar Durchläufe, also z.B. jeden 200ten kannst du dann die> anzuzeigenden Ziffern ändern.
Habe das versucht in das Programm einzugeben, so:
char ledmuster [10]= // Array
{
0b11000000, // muster 0
0b11111001, // muster 1
0b10100100, // muster 2
0b10110000, // muster 3
0b10011001, // muster 4
0b10010010, // muster 5
0b10000010, // muster 6
0b11111000, // muster 7
0b10000000, // muster 8
0b10010000, // muster 9
};
#define F_CPU 16000000U
#include <util/delay.h>
#include <avr/io.h>
uint8_t Zahl1=0b11111110;
uint8_t Zahl2=0b11111101;
uint8_t Zahl3=0b11111011;
uint8_t Zahl4=0b11110111;
uint8_t Zaehler=0;
uint8_t Zaehler2=0;
int main(void)
{
DDRA=0b11111111; // Port A sind alle Ausgänge
DDRB=0b11111111; // Port B sind alle Ausgänge
/* Replace with your application code */
while (1)
{
for (Zaehler=0; Zaehler<=200; Zaehler++)
{
PORTA=Zahl1;
_delay_ms(5);
PORTA=Zahl2;
_delay_ms(5);
PORTA=Zahl3;
_delay_ms(5);
PORTA=Zahl4;
_delay_ms(5);
Zaehler++;
if (Zaehler=200)
{
Zaehler2++;
PORTB=ledmuster[Zaehler2];
}
}
}
Funktioniert leider nicht!
Gruß
DimiX schrieb:> Nur dann durchläuft das Programm von 0 bis 9999 und wenigen Sekunden> durch.
Dann hast du die anzuzeigende Zahl bei jedem Durchlauf geändert und
nicht nur bei z.B. jedem zweihundertsten. Damit muss das 1/4 Stunde
dauern.
Immer wenn du über PORTA eine Stelle von deinem Display auswählst,
musst du auf PORTB das LED-Muster für diese Stelle ausgeben (Schritt 2).
Wenn du PORTB nur bei jedem zweihundertsten Durchlauf änderst, siehst du
auf allen Stellen das gleiche Muster.
DimiX schrieb:> PORTB=ledmuster[Zaehler2];
Überlege dir mal, was passiert, wenn beispielsweise Zaehler2=12 ist.
Was soll dann auf deinem Display dargestellt werden und was musst du
dafür wann auf PORTB raus geben?
In der akt. Fassung des Programms ändert sich deine Anzeige mit dem Takt
der CPU - so schnell, wie diese die Schleife halt durchlaufen kann.
Logisch, dass du nicht viel erkennen kannst.
Für das Ansteuern einer Segementanzeige benötigt man allg. zwei
Taktgeber: einen für Änderungen des Displayspeicher (= was angezeigt
werden soll) und einen weiteren für die Abbildungsfrequenz des
Displayspeichers auf den Segementen.
Letzteren kannst du dir sparen; wir sagen einfach so schnell, wie
möglich oder er wird über den Controller gesteuert.
Bleibt also die Aufgabe den Code für die Anzeige so zu verändern, dass
diese nur alle 1/x Sekunden neu beschrieben wird.
Viel Erfolg!
Noname schrieb:> Bleibt also die Aufgabe den Code für die Anzeige so zu verändern, dass> diese nur alle 1/x Sekunden neu beschrieben wird.
Nein.
Das eine ist die Anzeige, das andere ist die Uhr, der Zähler oder was
auch immer. Das hat beides gar nichts direkt miteinander zu tun.
Für die Anzeige werden nacheinander die 4 Digits angesteuert. Und zwar
mindestens so schnell, daß die Anzeige nicht flimmert. Damit alle 4
Ziffern gleich hell leuchten, muß die jeweilige Anzeigezeit immer gleich
lang sein.
Das steuert man praktischerweise mit einem Timer. Z.B. mit einem
1ms-Takt. In diesem Fall käme jede Ziffer 250 Mal pro Sekunde dran. Das
kann man gleich in der ISR des Timer Interrupts erledigen.
1
#define DIGIT0 PORTB |= (1 << 0)
2
#define DIGIT1 PORTB |= (1 << 1)
3
#define DIGIT2 PORTB |= (1 << 2)
4
#define DIGIT3 PORTB |= (1 << 3)
5
#define DIGITOFF PORTB &= 0xF0
6
#define BLANK PORTA = 0
7
8
volatileunsigendcharZiffer0=0;
9
volatileunsigendcharZiffer1=0;
10
volatileunsigendcharZiffer2=0;
11
volatileunsigendcharZiffer3=0;
12
13
ISR(Timer-Int)
14
{
15
staticunsignedchardigit=0;
16
17
DIGITOFF;//Das ist wichtig,...
18
BLANK;//...sonst leuchten einzelne Segmente nach
19
20
switch(digit)
21
{
22
case0:
23
PORTA=Ziffer0;
24
DIGIT0;
25
break;
26
27
case1:
28
PORTA=Ziffer1;
29
DIGIT1;
30
break;
31
32
case2:
33
PORTA=Ziffer2;
34
DIGIT2;
35
break;
36
37
case3:
38
PORTA=Ziffer3;
39
DIGIT3;
40
break;
41
}
42
digit++;
43
digit%=4;
44
}
In der main werden in Ziffer0..Ziffer3 in aller Ruhe entsprechend des
Anzeigewertes die Bitmuster eingetragen. Das kann man zyklisch machen
oder auch nur wenn sich ein Wert geändert hat.
Thomas E. schrieb:> #define DIGIT0 PORTB |= (1 << 0)
Nein.
Seine Schaltung hat PNP Transistoren mit Emitter an Vcc
Damit braucht er eine aktive 0 um die Stelle anzusteuern.
7segmentdepp schrieb:> Nein.>> Seine Schaltung hat PNP Transistoren mit Emitter an Vcc> Damit braucht er eine aktive 0 um die Stelle anzusteuern.
Tatsächlich.
Ja, dann ändere das doch, anstatt nur rumzumäkeln. Dazu sind defines
doch da.
Thomas E. schrieb:> dann ändere das doch, anstatt nur rumzumäkeln.
Ich halte es auch nicht für didaktisch sinnvoll einem
Anfänger eine ISR vorzusetzen wo er noch nicht mal weiss
wie sie funktioniert und er sich an der Konfiguration des
erforderlichen Timers die Zähne ausbeissen wird (werden
dürfte ...).
DimiX schrieb:> Ich habe keine Vorstellung wie ich 2 Schleifen gleichzeitig laufen> lassen soll, die eine Schleife zählt hoch, die andere zeigt die 4> Stellen an.
Man muß beide Schleifen entkoppeln, d.h. die eine (Multiplex) muß im
Timerinterrupt laufen. Alles andere führt in eine Sackgasse.
Um den Timerinterrupt zu lernen, mach erstmal nur ne Blink-LED damit.