Forum: Mikrocontroller und Digitale Elektronik 7-Segment Anzeige ansteuern


von edgar 3. (edgar339)


Lesenswert?

Hallo Community!

Mir geht es darum ein 7-Segment Display mit einem ATmega8 anzusteuern. 
Das alles möchte ich in C (GCC) machen. Einzelne Zahlen darzustellen ist 
ja nicht das Problem, jedoch scheitere ich daran Variablen auszugeben. 
Also dem Controller beizubringen, welche Ziffer zu welchem Binärwert für 
PortD (da hängt das Display dran) gehört. Macht man sowas über einen 
#define-Block, mit einem Array oder noch anders? Wenn möglich sollte es 
nicht den SRAM belasten.

Weiter habe ich Probleme beim Multiplexen (Segmentweise). Wie bekomme 
ich es hin, dass er bei z.B. einer 21 die 2 "isoliert" und an erster 
Stelle anzeigt und das gleiche mit der 1 an zweiter Stelle macht?

Ich danke schon mal!

Edgar

von hp-freund (Gast)


Lesenswert?


von M. N. (Gast)


Lesenswert?


von hp-freund (Gast)


Lesenswert?

zeiten link für C vergessen:

http://www.mikrocontroller.net/articles/Uhr

von hp-freund (Gast)


Lesenswert?

bitte w einfügen ;)

von edgar 3. (edgar339)


Lesenswert?

Ich danke euch schon mal!
Laut
>http://www.mikrocontroller.net/articles/Uhr
macht man das also mit nem Array. Und das liegt dann nicht auf dem Ram?

von Michael L. (eagle87)


Lesenswert?

Das Array ist ja "const", kann also nicht mehr verändert werden. Wiso 
sollte es der GCC dann ins RAM stecken?

von edgar 3. (edgar339)


Lesenswert?

Gut, hatte ich mir schon gedacht, wollte nur sicher gehen.

Dann wäre nur noch die Frage des Multiplexens. Das einzige was mir 
einfällt wären jetzt lustige Konstruktionen mit Strings, aber ich glaube 
irgendwie nicht, das das so richtig ist...

Edgar

von Anja (Gast)


Lesenswert?

Michael L. schrieb:
> Das Array ist ja "const", kann also nicht mehr verändert werden. Wiso
> sollte es der GCC dann ins RAM stecken?

Weil das für den Compiler einfacher ist.

Wenn man wirklich haben will daß er das Array im Flash läßt braucht man 
entsprechende "progmem" attribute.
1
    GLOBAL const uint8 PROGMEM SegTab[16] = {/* Umwandlungstabelle Wert -> Segmente  */
2
  /*  + a +    */
3
  /*  f   b    */
4
  /*  + g +    */
5
  /*  e   c    */
6
  /*  + d + p  */
7
8
  /* 0 */ SEG_a | SEG_b | SEG_c | SEG_d | SEG_e | SEG_f         ,
9
  /* 1 */         SEG_b | SEG_c                                 ,
10
  /* 2 */ SEG_a | SEG_b |         SEG_d | SEG_e |         SEG_g ,
11
  /* 3 */ SEG_a | SEG_b | SEG_c | SEG_d |                 SEG_g ,
12
  /* 4 */         SEG_b | SEG_c |                 SEG_f | SEG_g ,
13
  /* 5 */ SEG_a |         SEG_c | SEG_d |         SEG_f | SEG_g ,
14
  /* 6 */ SEG_a |         SEG_c | SEG_d | SEG_e | SEG_f | SEG_g ,
15
  /* 7 */ SEG_a | SEG_b | SEG_c                                 ,
16
  /* 8 */ SEG_a | SEG_b | SEG_c | SEG_d | SEG_e | SEG_f | SEG_g ,
17
  /* 9 */ SEG_a | SEG_b | SEG_c | SEG_d |         SEG_f | SEG_g ,
18
  /* A */ SEG_a | SEG_b | SEG_c |         SEG_e | SEG_f | SEG_g , 
19
  /* b */                 SEG_c | SEG_d | SEG_e | SEG_f | SEG_g ,
20
  /* c */                 SEG_d | SEG_e |                 SEG_g ,
21
  /* d */         SEG_b | SEG_c | SEG_d | SEG_e |         SEG_g ,
22
  /* E */ SEG_a |                 SEG_d | SEG_e | SEG_f | SEG_g ,
23
  /* F */ SEG_a |                         SEG_e | SEG_f | SEG_g ,
24
  };
25
26
        /* Zugriff auf Tabelle dann mit */
27
  j = pgm_read_byte_near(SegTab + (Akku & 0xF));

von Ulrich (Gast)


Lesenswert?

So groß sind die Arrays nicht, dass man da unbedingt vermeiden muss die 
ins RAM zu packen. Das sind nur 10 Bytes  für die Umrechnung von 0-9 in 
die Segmente. Den Speicher für die aktuelle Anzeige muss man ja ohnehin 
ins RAM ablegen, und das sind auch nur 1 Byte je Stelle.

Wenn das Array im RAM steht, wird der Zugriff in der Regel schneller und 
einfacher. Damit die Daten nicht ins Flash kommen müsste man schon 
PGM_space nutzen und dann einen umständlicheren Zugriff, weil das Flash 
einen anderen Adressbereich nutzt. Entsprechend landen bei GCC-AVR auch 
konstante Arrays normalerweise im RAM.

p.s. für ungenutztes RAM gibt es kein Geld zurück.

von hp-freund (Gast)


Lesenswert?


von edgar 3. (edgar339)


Lesenswert?

Gut, dann ins RAM, das bisschen interessiert nun auch nicht...
Ich als Anfäger bin nur etwas vorsichtiger, man liest ja auch überall, 
das man ressourcensparend programmieren soll.

Die Frage mit dem Multiplexen wäre da noch, oder habe ich dort einen 
vollkommen falschen Ansatz?

von hp-freund (Gast)


Lesenswert?

Was willst Du für eine Zahl darstellen? Wie viele Stellen?

Also z.B. integer von 0000 bis 9999 bei 4 Stellen

von edgar 3. (edgar339)


Lesenswert?

Erstmal sollen es nur zweistellige sein.

von hp-freund (Gast)


Lesenswert?

Dann brauchst Du nur die Funktionen Berechnung und Anzeige aus dem Uhr 
Beispiel anzupassen.

Das wichtigste ist dabei die modulo Operation.

von edgar 3. (edgar339)


Lesenswert?

Mein Wert kommt vom ADC und wird ein bisschen umgerechnet, am Ende 
bleibt eine zweistellige Zahl.

Meine Idee gerade: Bei einem Integer wird bei einer Division immer 
abgerundet(richtig?). Also einfach den Wert durch 10 und gut ist. Also 
21 / 10 = 2,1 -> 2 wird ausgegeben und dann angezeigt.

Für die 1 dann 21 % 10 müsste doch 1 ergeben

Könnte das funktionieren?

von hp-freund (Gast)


Lesenswert?

Ja.

von edgar 3. (edgar339)


Lesenswert?

Bin am programmieren ^^

von Michael H. (michael_h45)


Lesenswert?

Michael L. schrieb:
> Das Array ist ja "const", kann also nicht mehr verändert werden. Wiso
> sollte es der GCC dann ins RAM stecken?

weil der prozessor das flash in der avr-architektur nicht einfach so 
adressieren kann. siehe datenblatt, suchfunktion, compiler-doku, etc pp.

von edgar 3. (edgar339)


Lesenswert?

Ok, es gab ein paar Schwierigkeiten (Fehler meinerseits), aber jetzt 
geht es!
Ich danke euch!

Edgar

von c-hater (Gast)


Lesenswert?

Michael H. schrieb:
>
> weil der prozessor das flash in der avr-architektur nicht einfach so
> adressieren kann.

Klar kann er das, kostet (jedenfalls bis 64k Flash) nur genau einen Takt 
mehr pro Zugriff.

von Michael H. (michael_h45)


Lesenswert?

c-hater schrieb:
> Klar kann er das, kostet (jedenfalls bis 64k Flash) nur genau einen Takt
> mehr pro Zugriff.
nein, kann er nicht.
direkte adressierung ist nicht möglich. und ein compiler wird den teufel 
tun und die adressierung einfach so einbauen.
das ist bei harvard-architekturen prinzipbedingt so.

aber wenn dus nicht glauben willst, kann mir das egal sein. ein drittes 
mal schreiben werd ichs nicht.
lies es nach oder stirb dumm - deine sache.

von M. N. (Gast)


Lesenswert?

edgar 339 schrieb:
> Ich als Anfäger bin nur etwas vorsichtiger, man liest ja auch überall,
> das man ressourcensparend programmieren soll.

Höre nicht auf die Angstmacher! Das sind vielfach Ratschläge (heute sind 
es Vorurteile), die im letzten Jahrhundert entstanden sind und nicht 
mehr aus den Köpfen wollen.

Als Anfänger bietet Dir ein ATmega8 unendlich viel Platz. Kümmere Dich 
zunächst um die Lösung Deines Problems. Mit zunehmender Erfahrung 
programiert man automatisch effizienter.

Zur Orientierung noch ein Beispiel, wie man ADC-Werte auf ein Display 
bekommen kann. Beitrag "7-Segm. LCD-Ansteuerung, 4-stelliges Einbaumodul, Attiny45"
AVR-GCC erzeugt 2122 Byte Code und es werden 75 Byte Ram gebraucht - 
ohne irgendwelche platzsparenden Tricksereien.

Für eine 2-stellige Anzeige würde ich kein Multiplex veranstalten. Zwei 
Schieberegister CD4094 (die CMOS-Version) direkt an die Segmente 
angeschlossen reichen aus. Statischer Betrieb: keine Treiber, keine 
Widerstände und kein Ausfall der Anzeige, falls der µC hängen bleibt, 
keine 'Geistersegmente', kein Ripple in der Versorgungsspannung, keine 
Störstrahlung (um mal selber Angst zu schüren :-), ...
Aber mache, wie Du willst.

von edgar 3. (edgar339)


Lesenswert?

Danke für deine Vorschläge!
Das mit dem Angst machen ist mir auch schon aufgefallen. Bisher aber nur 
in den ganzen Schaltungen. Mindestbeschaltung der Controller 
beispielsweise. Abblockkondensatoren kann ich ja noch verstehen, aber 
Start-Up-Kondensator, oder nen externen Brown-Out-Detektor?

Ich wollte das Multiplexen, damit ich erstens lerne, wie das geht, und 
da dort noch weitere Digits dazukommen(derzeit läuft es mit 4, sollen 6 
werden).
Ein Problem fällt jetzt schon auf: die Berechnungen für die Ziffern 
dauern unterschiedlich lange, deshalb sind auch die Digits 
unterschiedlich hell. Ich werde das jetzt mit Timerinterrups machen, 
dann sollte das ja gehen.

Edgar

von M. N. (Gast)


Lesenswert?

edgar 339 schrieb:
> Ich wollte das Multiplexen, damit ich erstens lerne, wie das geht, und

Das ist gut, mach es!

Selber hatte ich im Laufe der Zeit einige Großanzeigen (25cm 
Ziffernhöhe) gebaut, wo Multiplexen nicht mehr sinnvoll war; aktuell 
habe ich eine Schaltung vor mir, die noch mit <3V arbeitet. Auch hier 
ist die statische Ansteuerung einfacher. Deshalb mein Rat zum statischen 
Betrieb.


> unterschiedlich hell. Ich werde das jetzt mit Timerinterrups machen,
> dann sollte das ja gehen.

Muliplexen sollte man grundsätzlich per Timerinterrupt machen. Achte auf 
ein bißchen Totzeit zwischen dem Abschalten des letzten Digits und dem 
Einschalten des nächsten. Die Zeit dazwischen kann man für weitere 
Befehle nutzen, die auch im Interrupt ausgeführt werden müssen. 
Andernfalls kommt es zu den 'Geistersegmenten'. Aber das wirst Du selber 
sehen.

von edgar 3. (edgar339)


Lesenswert?

...und ich dachte, die Leds scheinen durch die Wände zwischen den 
Segmenten :D

Ich bin fast fertig, benutze aber diesmal einen mega48 und bin erst mal 
nicht da...

Edgar

von edgar 3. (edgar339)


Lesenswert?

So, fertig. Das Programm zählt mit Timer1 hoch und zeigt mit Timer0 an.
1
#define F_CPU 1000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
const unsigned char Ziffer[10] = {0b01000000, 0b01111001 , 0b00100100,
7
0b00110000, 0b00011001, 0b00010010, 0b00000010, 0b01111000, 0b00000000, 0b00010000};
8
int y,i;
9
short int n;
10
11
int main(void)
12
{            
13
  sei();
14
  
15
  DDRD = 0b11111111;
16
  DDRB = 0b11111111;
17
  DDRC = 0b11111111;
18
  
19
  PORTD = 0b00000000;
20
  PORTB = 0b11111111;
21
  _delay_ms(1000);
22
  PORTB = 0b00000000;
23
                  
24
  TIMSK0 |= 1<<TOIE0;          
25
  TCCR0B |= (1<<CS01);  
26
  
27
  TIMSK1 |= 1<<TOIE1;  
28
  TCCR1B = (1<<CS10);
29
    
30
    while(1)
31
    {
32
                    //Trödeln
33
    }
34
}
35
36
ISR (TIMER0_OVF_vect)
37
{
38
  PORTC=0b00100000;
39
  i++;
40
  if (i>3)
41
  {
42
    i=0;
43
  }
44
  
45
  switch (i) 
46
  {
47
    case 0:    PORTB = 0b00000000;
48
          y = n / 1000;
49
          PORTB = 0b10000000;
50
          PORTD = Ziffer[y];
51
      break;
52
    case 1:    PORTB = 0b0000000;
53
          y = n % 1000;
54
          y = y / 100;
55
          PORTB = 0b01000000;
56
          PORTD = Ziffer[y];
57
      break;
58
    case 2:    PORTB = 0b00000000;
59
          y = n % 100;
60
          y = y / 10;
61
          PORTB = 0b00000001;
62
          PORTD = Ziffer[y];
63
      break;
64
    case 3:    PORTB = 0b00000000;
65
          y = n % 10;
66
          PORTB = 0b00000010;
67
          PORTD = Ziffer[y];
68
      break;
69
  }
70
    PORTC=0b00000000;          
71
}  
72
73
ISR (TIMER1_OVF_vect)
74
{
75
  n++;
76
}

Edgar

von M. N. (Gast)


Lesenswert?

Da hast Du ja schon mal ein Erfolgserlebnis. Das braucht man unbedingt.

Wenn Du kurze Kommentare in den Code einfügst, kann man ihn später 
einmal besser verstehen, insbesondere wenn es nicht mehr 50 sondern 5000 
Zeilen sind.
Eine Sache ist aber ganz wichtig: deklariere die Variablen i und y, die 
nur in der ISR von T0 gebraucht werden auch dort.

ISR (TIMER0_OVF_vect)
{
static uint8_t i;     // Schleifenzähler
uint16_t y;           // Hilfsvariable und Index
....

Ferner sollte erst PORTD geschrieben werden und dann PORTB, auch wenn es 
aktuell nichts auszumachen scheint.
Mit den Berechnungen in der T0-ISR hast Du ja allen Optimierungsgurus 
zunächst die kalte Schulter gezeigt :-)
Irgendwann wirst Du ein 'ausgabe_register[MAX_DIGITS]' verwenden und im 
Interrupt nur noch die betreffeden Digits indiziert ausgeben. Spätestens 
dann, wenn noch ein '-' Vorzeichen angezeigt oder die Vornullen 
unterdrückt werden sollen.

von edgar 3. (edgar339)


Lesenswert?

Die Kommentare habe ich nur weg gelassen, damit es schnell geht :D
Das die immer dazu gehören habe ich schon am eigenen Leibe erfahren 
müssen...

Globale Variablen sind böse, ich weiß...
i muss aber global sein, es soll ja von Interrupt zu Interrupt erhöht 
werden, ansonsten wird es jedes mal auf 0 gesetzt, oder?

Zur Leistungsoptimierung hatte ich mir gedacht: Für jede Ziffer eigene 
Variable, und die Berechnung nur durchführen, wenn sich die anzuzeigende 
Zahl auch wirklich geändert hat.
Außerdem wollte ich noch die Berechnung außerhalb der ISR durchführen.

Danke noch für das mit den Ports, da muss man nicht lange nachdenken, um 
zu sehen das das sinnvoller ist ...aber man muss selbst erstmal drauf 
kommen...

>ausgabe_register[MAX_DIGITS]
Ich verstehe zwar noch nicht, was das wird oder soll, aber wenn ich 
soweit bin, kann ich ja wieder hier vorbeischauen :)


Ich danke dir(und natürlich auch den anderen hier) das ihr mir helfen 
wollt und hoffe, das ich das irgendwann wieder gut machen kann(wie war 
das mit Maus und Löwe?...)

Edgar

von Patrick (Gast)


Lesenswert?

edgar 339 schrieb:
> Globale Variablen sind böse, ich weiß...

Naja, ganz sooo ist es dann auch wieder nicht - es ist nur einfach 
sinnvoll, die Variablen in der kleinstmöglichen Sichtbarkeitsebene zu 
deklarieren - d. h. in einer Funktion, wenn sie nur ein der Funktion 
gebraucht werden, oder in der Datei, wenn sie nur in der Datei gebraucht 
werden.

> i muss aber global sein, es soll ja von Interrupt zu Interrupt erhöht
> werden, ansonsten wird es jedes mal auf 0 gesetzt, oder?

Nicht, wenn Du es mit dem Schlüsselwort "static" versiehst.

>>ausgabe_register[MAX_DIGITS]
> Ich verstehe zwar noch nicht, was das wird oder soll, aber wenn ich
> soweit bin, kann ich ja wieder hier vorbeischauen :)

Der Hintergedanke ist, dass die Berechnung in der main()-Loop oder wo 
auch immer vorgenommen und in der Timer-ISR nur das Ergebnis ausgegeben 
wird. Als Variable zur Übergabe dient dann sinnvollerweise ein 
(globales) Array, das den anzuzeigenden Wert jedes Digits enthält.

von edgar 3. (edgar339)


Lesenswert?

>Berechnung in der main()-Loop oder wo
>auch immer vorgenommen und in der Timer-ISR nur das Ergebnis ausgegeben
>wird

Dann habe ich ja schon aus Versehen richtig gedacht :D
Ah, jetzt ist es klar, mich hatte "Register" nur verwirrt. Hatte 
überhaupt nicht an ein Array gedacht... Danke!

von Peter D. (peda)


Lesenswert?

M. N. schrieb:
> Andernfalls kommt es zu den 'Geistersegmenten'.

Aber auch nur, wenn man Treibertransistoren im Emitterschaltung benutzt. 
Die Kollektorschaltung ist schnell genug und spart obendrein den 
Bassiswiderstand.

von M. N. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Die Kollektorschaltung ist schnell genug und spart obendrein den
> Bassiswiderstand.

Das ist ja nicht verkehrt, aber wer macht es denn so? :-)
Allerdings ist der Anwendungsbereich eingeschränkt, da der sinnvolle 
Versorgungsspannungsbereich bei rund 4-5V liegt. Ein 3V-Design geht 
nicht mehr, da der Spannungsabfall (Segment ca. 1,8-2,2V + 2 x Vbe ca. 
1,4V) zu groß wird. Die obere Grenze wird durch VCC des Prozessors 
begrenzt. Größere Anzeigen mit mehreren LEDs in Reihenschaltung (>=2) 
funktionieren somit auch nicht.

Auf der anderen Seite sind ca. 5µs Totzeit bei einer Multiplexfrequenz 
von 0,4 - 1kHz (>= 1ms) doch kein Thema. Die Helligkeit wird um max. 
0,5% rduziert, was überhaupt nicht negativ auffällt.

Wenn wir schon bei der Helligkeit sind: bei statischer Ansteuerung kann 
man diese bequem über ein PWM-Signal an output-enable steuern.
Ein Pluspunkt, den ich oben noch vergessen hatte :-)

von edgar 3. (edgar339)


Angehängte Dateien:

Lesenswert?

Noch was verbesserbar? :D
Das einzige was mir einfallen würde, währe, nur die letzte(n) Ziffern 
neu zu berechnen, nachdem hochgezählt wurde, aber es soll ja später für 
Werte vom ADC sein, die sich willkürlich ändern.

Edgar

von Karl H. (kbuchegg)


Lesenswert?

edgar 339 schrieb:
> Noch was verbesserbar? :D

zb
1
    case 0:    PORTB = 0b00000000;                  //Ziffer4 anzeigen
2
          PORTD = Ziffer[Ausgabe_Ziffern[0]];
3
          PORTB = 0b10000000;
dir ist klar, dass du dir damit den Rest vom Port B unbenutzbar machst?
1
ISR (TIMER1_OVF_vect)
2
{
3
  n++;                                //Auszugebene Zahl erhöhen
4
  Ausgabe_Ziffern[0] = n / 1000;                    //Berechnungen
5
  Ausgabe_Ziffern[1] = (n % 1000) / 100;  
6
  Ausgabe_Ziffern[2] = (n % 100) / 10;
7
  Ausgabe_Ziffern[3] = n % 10;
8
}

Die Aufsplittung eines uint16_t in die einzelnen Ziffern (und das 
bestimmen des dafür anzuzeigenden Musters) hätte sich aber wirklich eine 
eigene Funktion verdient. Sowas wird man ja schliesslich öfter brauchen.


> Werte vom ADC sein, die sich willkürlich ändern.

Eben. Du willst Werte vom ADC ausgeben. Und dafür hättest du gerne eine 
Funktion

void disp_number( uint16_t value )
{
  ...
}

welche sich um das 'schmutzige' Geschäft kümmert, alles für die 
Multiplexroutine herzurichten, so dass diese dann auch tatsächlich diese 
Zahl anzeigt.

Lass diese Umkodierung von der numerischen Ziffer in das von der Anzeige 
anzuzeigende Muster
1
    PORTD = Ziffer[Ausgabe_Ziffern[0]];
auch gleich die Funktion disp_number machen.

Zum einen muss das nicht ständig in jedem Interrupt gemacht werden, zum 
anderen soll sich die Multiplex-Funktion gar nicht darum kümmern, was 
das was du anzeigst, eigentlich darstellen soll. Wenn du in deinem 
Programm entscheidest, dass die 7-Seg Anzeige in allen 4 Stellen lauter 
'-' anzeigen soll, dann sollte das der Multiplex-Funktion aber sowas von 
egal sein. Dein Programm schreibt die entsprechenden Musterbytes in das 
Array und der Multiplex-Code in der ISR zeigt sie an.

von edgar 3. (edgar339)


Lesenswert?

Ok, Danke!

PortB brauche ich nicht, PortB |= 1 << [0/1/was auch immer] wäre 
natürlich besser.

Eine Funktion ist bestimmt sinnvoller, als den Code in andere Projekte 
zu kopieren...

Edgar

von Karl H. (kbuchegg)


Lesenswert?

edgar 339 schrieb:
> Ok, Danke!
>
> PortB brauche ich nicht,

:-)
kennst du die Kategorie: Berühmte letzte Worte?

> PortB |= 1 << [0/1/was auch immer] wäre
> natürlich besser.

Nicht nur "wäre". Es ist in Summe auch einfacher und universeller. 
Solche Basis-Sachen richtet man sich einmalig ordentlich her, so dass 
man sie die nächsten 10 Jahre in x Projekten benutzen kann, ohne dauernd 
das Rad neu erfinden zu müssen.
1
#define SEGMENT_PORT PORTD
2
#define DIGIT_PORT   PORTB
3
4
#define DIGIT_0     (1<<PB7)
5
#define DIGIT_1     (1<<PB6) 
6
#define DIGIT_2     (1<<PB0) 
7
#define DIGIT_1     (1<<PB1) 
8
9
#define ALL_DIGITS  DIGIT_0 | DIGIT_1 | DIGIT_2 | DIGIT_3
10
11
12
const uint8_t Ziffer[] = {
13
    0b01000000, 0b01111001,    // 0  1
14
    0b00100100, 0b00110000,    // 2  3
15
    0b00011001, 0b00010010,    // 4  5
16
    0b00000010, 0b01111000,    // 6  7
17
    0b00000000, 0b00010000     // 8  9
18
};
19
20
21
uint8_t digits[4]       = {DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3 };
22
volatile uint8_t digitPattern[4];
23
24
25
ISR( ... )
26
{
27
  static uint8_t digitNr = 0;
28
29
  DIGIT_PORT &= ~(ALL_DIGITS);        // alles aus
30
31
  digitNr++;                     // welches ist die nächste Anzeige die
32
  if( digitNr == 4 )             // leichten soll?
33
    digitNr = 0;
34
35
  SEGMENT_PORT = digitPattern[digitNr]; // Muster raus
36
  DIGIT_PORT |= digits[digitNr];      // ... und einschalten
37
}
38
39
40
void disp_number( uint16_t value )
41
{
42
  uint8_t i;
43
44
  for( i = 0; i < 4; ++i ) {
45
    digitPattern[3-i] = Ziffer[ value % 10 ];
46
    value /= 10;
47
  }
48
}

von M. N. (Gast)


Lesenswert?

Zu Deiner T0-ISR:
Lösche bitte ganz zu Anfang alle Segmente und den zuletzt aktiven 
Digit-Treiber (hatte Karl-Heinz ja schon gezeigt). Danach mach mit
i++; .... weiter.
Dadurch erhälst Du automatisch eine Totzeit zwischen den Digits.
Als nächstes könntest Du probieren, die Vornullen zu unterdrücken, dass 
anstatt "0000" nun "   0", oder statt "0123" dann " 123" angezeigt wird.

Wenn Du ein bißchen mutiger bist, setze den CPU-Takt ruhig auf 8MHz :-)

von edgar 3. (edgar339)


Lesenswert?

Gut, dann habe ich ja was, worüber ich nachdenklen kann, ich muss mich 
aber jetzt erst mal um die Schule kümmern... Naja, hat ja alles seinen 
Sinn.

Edgar

von edgar 3. (edgar339)


Angehängte Dateien:

Lesenswert?

So, langes Wochenende :3

So, ich habe die Zeit sinnvoll genutzt und den Code umgeschrieben. Die 
Vornullen werden unterdrückt, indem erst abgefragt wird, ob ganz links 
eine 0 steht. Falls ja wird dieses Digit mit Ziffer[10] belegt, was 
nichts anzeigt. Nun wird abgefragt ob die zweite Stelle eine 0 ist. Wenn 
ja, dann wird nachgesehen, ob die vorherige Stelle unterdrückt wurde, um 
nicht Nullen in der Zahl zu unterdrücken(soll ja vorkommen). Das geht 
dann immer so weiter.
Ich habe auch schon überlegt, das in einer for-schleife zu machen, 
jedoch gab es da große Komplikationen und ich wollte, bevor ich weiter 
überlege erst einmal wissen, ob der Ansatz richtig ist.

Des weiteren wird "EEEE" ausgegeben, wenn die Zahl höher ist als 9999. 
Das wird am Anfang der Berechnungen durchgeführt.

Fragen die ich noch direkt habe:
1. Ist die Schleife notwendig, oder gibt es einen Weg, gleich mehreren 
Stellen eines Arrays den gleichen Wert zuzuweisen(Ziffer[0,1,2,3] mag 
der Compiler nicht)?

2. Wie ist die Null-Unterdrückung zu verbessern(Schleife?)?

Ich danke nochmals!

Edgar

von edgar 3. (edgar339)


Lesenswert?

Ähm, ich habe gerade bemerkt, dass nicht EEEE ausgegeben wird, sondern 
er wieder neu anfängt zu zählen. Zumindest beim erstem mal, bei den 
nächsten flackert er wieder, weil er über das Array hinausschießt.
Das kann ich mir irgendwie nicht erklären... Naja, ich werde noch mal 
nachsehen.

Edgar

von Karl H. (kbuchegg)


Lesenswert?

> uint8_t Ausgabe_Ziffern[4]  ;

>     for (i=0; i<5; i++)
                ***
>    {
>      Ausgabe_Ziffern[i] = Ziffer[11];
>    }


Ein Array der Länge 4 hat die Indizes
 0, 1, 2, 3
das sind 4 Stück ... zähl nach. Und der höchste gültige Index ist 3. 
Also 1 weniger als die Arraylänge.

eine Schleife
>     for (i=0; i<5; i++)
durchläuft den Schleifenkörper mit Werten in i von

   0, 1, 2, 3, 4

nach der nächsten Erhöhung von i wäre i dann 5 und für 5 gilt dann i < 5 
nicht mehr und die Schleife bricht ab.
Aber:
Innerhalb der Schleife greifst du daher auf
    Ausgabe_Ziffern[0], Ausgabe_Ziffern[1], Ausgabe_Ziffern[2],
    Ausgabe_Ziffern[3] und Ausgabe_Ziffern[4]
zu.
Nur: Ein Ausgabe_Ziffern[4] existiert nicht! Der höchste zulässige Index 
war 3!

von edgar 3. (edgar339)


Lesenswert?

Ich sollte auch zurück schreiben, wenn ich das Problem gelöst habe und 
nich erst Tage warten...

Das Problem liegt auf der Hand: er stellt es schön auf "EEEE" ein ... um 
danach wie üblich alles für die Ziffern zu berechnen und Mist 
auszugeben...
Deshalb habe den Rest nach der for-Schleife in Klammern eines else 
gesteckt.

Edgar

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.