Forum: Mikrocontroller und Digitale Elektronik Programmierung einer LED-Anzeige zu langsam


von Jano (Gast)


Lesenswert?

Ich habe 10 LEDs, der Wert, der angezeigt werden soll, ist zwischen 0 
und 10. Dieser sollen folgendermaßen angezeigt werden:

1 von 10:
X---------

5 von 10:
XXXXX-----

8 von 10:
XXXXXXXX--

(X = an, - = aus)

ich habe in C fogenden Code geschrieben:
(bit 1-8 der LEDs an portC, bit 9-10 an portD 1-2)
1
//Bereich anpassen, dass der Wert zwischen 0-10 ist
2
Wert /= 35;
3
4
5
show = pow(2, Wert) - 1;
6
/*2 hoch Wert ergibt immer ein vielfaches von 2, also ergibt sich folgendes:
7
Wert = 2 -> 0010000000   minus 1 ergibt -> 1100000000
8
Wert = 5 -> 0000010000   minus 1 ergibt -> 1111100000
9
*/
10
11
12
//Variable "Show" an Ports geben
13
14
PORTC = show;
15
16
if (show >= 511)
17
PORTD |= (1<<PD1);
18
else
19
PORTD &= ~(1<<PD1);
20
21
if (show >= 1023)
22
PORTD |= (1<<PD2);
23
else
24
PORTD &= ~(1<<PD2);

Es funktioniert zwar, ist aber nicht sehr schnell.
Würde mich über Vervesserungsvorschläge freuen, ist mein erstes Programm 
auf einem Mikrocontroller in C.

PS: Verwende einen ATmega32

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jano schrieb:
> Es funktioniert zwar, ist aber nicht sehr schnell.
definiere "schnell"

von Jano (Gast)


Lesenswert?

das ganze wird auf 10 Reihen gemultiplext, also brauch ich
10 x ca.30 pro Sekunde, damit es für das Auge schön anzusehen ist.

Das ist natürlich nicht das einzigste, was in der Schleife hängt. Habe 
aber bei Begin der Stelle ein Bit gesetzt und am Ende zurückgesetzt. Das 
Oszi hat mir dann verraten, dass das Ganze im Vergleich zum Rest viel 
Zeit in anspruch nimmt.

von Karl H. (kbuchegg)


Lesenswert?

Jano schrieb:

> Es funktioniert zwar, ist aber nicht sehr schnell.
> Würde mich über Vervesserungsvorschläge freuen,

Der ganze Teil, wie du aus einer Zahl von 0 bis 9 eine entsprechende 
Anzahl an LED einschaltest ist viiiiieeeeel zu kompliziert. Insbesondere 
pow() ist da so richtig die brachiale Holzhammermethode.

Du hast 10 mögliche Eingangswerte als Ergebnis deiner Division durch 35.
Diese 10 Werte liegen dann auch noch im Bereich 0 bis 9. Da kann man 
ruhig ein Array mit 10 Werten anlegen. Mit dem erhaltenen Rechenergebnis 
geht man als Index in das Array und kriegt ein Bitmuster raus, welches 
so gestaltet ist, dass man es nur noch auf den Port geben muss und eine 
entsprechende Anzahl von LED leuchtet.
Kein Mensch muss dazu irgendetwas kompliziert über Logarithmen 
ausrechnen (was pow() tun muss)

> (bit 1-8 der LEDs an portC, bit 9-10 an portD 1-2)

Und gewöhn dir an, dass wir bei 0 zu zählen anfangen.
Ein Byte hat 8 Bit, die von 0 bis 7 durchnummeriert werden.

von Daniel F. (df311)


Lesenswert?

1
/*2 hoch Wert ergibt immer ein vielfaches von 2, also ergibt sich folgendes:
2
Wert = 2 -> 0010000000   minus 1 ergibt -> 1100000000
3
Wert = 5 -> 0000010000   minus 1 ergibt -> 1111100000
4
*/
normalerweise werden zahlen mit dem MSB link geschrieben (du schreibst 
doch auch nicht 01^2=4201, oder?)

> ist aber nicht sehr schnell.
ist definitionssache. "nicht optimal" triffts eher, du könntest beide if 
in eine schiebeoperation und bitmaske zusammenfassen, dann reduzierst 
den code

von Peter II (Gast)


Lesenswert?

wir wissen ja nicht mal was in welcher "schleife" abgearbeitet wird. Ich 
vermute mal ganz stark das

Wert /= 35;
show = pow(2, Wert) - 1;

jeweil in der Ausgabe-Schleife der Matrix drin sind. Das hat dort aber 
überhaupt nichts zu suchen. Man berechnet die Matrix vorher und die 
ausgabe sorgt nur dafür das sie angzeigt wird.

von Jano (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Du hast 10 mögliche Eingangswerte als Ergebnis deiner Division durch 35.
> Diese 10 Werte liegen dann auch noch im Bereich 0 bis 9. Da kann man
> ruhig ein Array mit 10 Werten anlegen. Mit dem erhaltenen Wert geht man
> als Index in das Array und kriegt ein Bitmuster raus, welches so
> gestaltet ist, dass man es nur noch auf den Port geben muss und eine
> entsprechende Anzahl von LED leuchtet.

also beim Initialisieren:
1
uint16_t Vorlage[10];
2
uint16_t Vorlage2[10];
3
4
5
Vorlage[0] = 0;
6
Vorlage[1] = 1;
7
Vorlage[2] = 3;
8
...
9
10
11
12
Vorlage2[0] = 0;
13
...
14
Vorlage2[9] = 1;
15
Vorlage2[10] = 3;


und dann

PORTC = Vorlage[Wert];
PORTD = Vorlage2[Wert];

von Jano (Gast)


Lesenswert?

edit: Das Array geht natürlich nur von 0-9, sorry für den schreibfehler 
;)

von Karl H. (kbuchegg)


Lesenswert?

Jano schrieb:

> also beim Initialisieren:
>
> uint16_t Vorlage[10];
> uint16_t Vorlage2[10];
>
>
> Vorlage[0] = 0;
> Vorlage[1] = 1;
> Vorlage[2] = 3;
> ...

* du kannst das alles in einem Aufwasch machen.

* Zahlensysteme!

  Da du es hier mit Bits zu tun hast (1 Bit regelt ja, ob eine LED
  leuchtet oder nicht), ist Dezimal hier keine große Hilfe.
  Schreibs halt einfach Binär hin, dann sieht auch ein Blinder
  wie das Leuchtmuster ist

* Warum verwendest du uint16_t für etwas, was nicht größer als 8 Bit
  sein wird? Machst du gerne Beschäftigungstherapie für deinen µC?
1
uint8_t MusterC[] = {
2
      0b00000000,          // 0
3
      0b00000001,          // 1
4
      0b00000011,          // 2
5
      0b00000111,          // 3
6
      0b00001111,          // 4
7
      0b00011111,          // 5
8
      0b00111111,          // 6
9
      0b01111111,          // 7
10
      0b11111111,          // 8
11
      0b11111111,          // 9
12
      0b11111111           // 10
13
};
14
15
uint8_t MusterD[] = {
16
      0b00000000,          // 0
17
      0b00000000,          // 1
18
      0b00000000,          // 2
19
      0b00000000,          // 3
20
      0b00000000,          // 4
21
      0b00000000,          // 5
22
      0b00000000,          // 6
23
      0b00000000,          // 7
24
      0b00000000,          // 8
25
      0b00000001,          // 9
26
      0b00000011           // 10
27
};

Alternativ hätte man natürlich auch ein einziges uint16_t Array nehmen 
können, welches man bei der Ausgabe bytemässig zerlegt.

von Karl H. (kbuchegg)


Lesenswert?

Jano schrieb:
> edit: Das Array geht natürlich nur von 0-9, sorry für den schreibfehler
> ;)

Es muss aber von 0 bis 10 gehen, dh. 11 Werte umfassen. Denn 'alle LED 
aus' ist ja bei dir offensichtlich auch ein Zustand.

von Karl H. (kbuchegg)


Lesenswert?

Jano schrieb:

> PORTC = Vorlage[Wert];
> PORTD = Vorlage2[Wert];


Damit beschreibst du aber den kompletten PORTD neu. Wenn da an anderen 
Ausgängen sonst noch was hängt, veränderst du damit ungewollt deren 
Zustand. PORTC kannst du komplett zuweisen, das ist kein Thema. Aber am 
Port D musst du ein wenig sorgfältiger sein.

Erst die beiden untersten Bits (welches sind es jetzt eigentlich: PD0 
und PD1 oder doch PD1 und PD2?) gezielt auf 0 setzen und dann den Wert 
aus dem Array darüberodern.

von Jano (Gast)


Lesenswert?

@Karl Heinz Buchegger

stimmt natürlich, mit Bits viel leichter zu lesen.
Das mit den 8 Bit ist mir auch gerade aufgefallen, da hab ich mal wieder 
etwas durcheinander gedacht ;)


ich habe ja oben noch geschrieben, dass ich es dann so setze:

PORTC = Vorlage[Wert];
PORTD = Vorlage2[Wert];

An Port D wird allerdings noch an anderen Pins etwas ausgegeben.
Ich bin mir nicht sicher ob ich da etwas übersehen habe, aber ich habe 
in dem AVR-GCC Tutorial nicht gefunden, wie ich das jetzt am besten 
setze.
(deswegen auch etwas umständlichs setzen des Port D im ersten Beitrag)

von Jano (Gast)


Lesenswert?

oh du warst mit der Antwort schneller, wie ich mit meiner Frage.
Ja ist ein schreibfehler, sollte P0 und P1 sein.

Was heißt darüberodern?

von Karl H. (kbuchegg)


Lesenswert?

Jano schrieb:
> oh du warst mit der Antwort schneller, wie ich mit meiner Frage.
> Ja ist ein schreibfehler, sollte P0 und P1 sein.
>
> Was heißt darüberodern?

Eine Oder-Operation.

Mittels

  PORTD |= ( 1 << PD0 );

würdest du PD0 auf 1 setzen.

Mittels

  PORTD |= ( 1 << PD0 ) | ( 1 << PD1 );

würdest du PD0 und PD1 auf 1 setzen.

Mittels

  PORTD |=  MaskeD[Wert];

würdest du die Bits 0 bzw 1 auf 1 setzen, je nachdem ob sie in der Maske 
gesetzt sind.

Aber!
Du kannst die so nicht mehr auf 0 bringen. Mit dem Oder kannst du sie 
nur auf 1 ziehen. Macht aber nichts. Ziehst du eben die beiden Bits auf 
jeden Fall erst mal auf 0 und dann je nach Bedarf (was eben das 
Maskenbyte sagt) die entsprechenden Bits auf 1

  PORTD = ( PORTD & ~0x03 ) | MaskeD[Wert];

> in dem AVR-GCC Tutorial nicht gefunden, wie ich das jetzt am
> besten setze.
Weil du hier Dinge kombinieren musst.
2 Grundtechniken (Bit setzen, Bit löschen) zu einer Einheit 
zusammenbauen.

von Karl H. (kbuchegg)


Lesenswert?

Jano schrieb:
> das ganze wird auf 10 Reihen gemultiplext, also brauch ich
> 10 x ca.30 pro Sekunde, damit es für das Auge schön anzusehen ist.
>
> Das ist natürlich nicht das einzigste, was in der Schleife hängt. Habe
> aber bei Begin der Stelle ein Bit gesetzt und am Ende zurückgesetzt. Das
> Oszi hat mir dann verraten, dass das Ganze im Vergleich zum Rest viel
> Zeit in anspruch nimmt.

Das lässt mich Schlimmeres zum Themenkreis 'Multiplexen' erahnen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das lässt mich Schlimmeres zum Themenkreis 'Multiplexen' erahnen.
Komm, Jano, trau dich und lass aus der Vermutung Gewissheit werden:
poste deinen Code als Anhang...

von Peter D. (peda)


Lesenswert?

Multiplexen hat in der Mainloop nichts zu suchen. Das gehört immer in 
den Timerinterrupt!
Die Mainloop muß die Ausgabedaten nur aufbereiten und im SRAM bereit 
stellen.


Peter

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.