Geschwindigkeit Array vs Mehrdimensionaler Array

Gast #2705867
Lesenswert?

Guten Abend,

Ich bin auf ein Problem gestoßen. Mein Atmega8 ist mir zu lahm. Ich 
lasse den auf 8 MHz laufen und scheinbar ist mein Programmierstil so 
unglaublich schlecht, dass das nicht ausreicht. ;-)

Das Problem:
Ich habe ein doppeltes Array[6][3], dass ich gegen einen Wert i prüfen 
möchte. Wenn ich irgendwann fertig bin, soll das eine 18 Kanal PWM mit 
Schieberegister werden. g_byte wird dann per SPI losgeschickt. Sieht 
ungefähr so aus:
1
uint8_t array[6][3];
2
uint8_t g_byte[6];
3
.
4
.
5
.
6
void PWM(void){
7
uint8_t static i= 0;
8

9
  if (i < tabelle[array[0][0]]) {
10
    g_byte[0] |= (1 << 0);
11
  } else   g_byte[0] &= ~(1 << 0);
12

13
  if (i < tabelle[array[0][1]]) {
14
    g_byte[0] |= (1 << 1);
15
  } else   g_byte[0] &= ~(1 << 1);
16
.
17
.
18
.
19
  if (i < tabelle[array[5][2]]) {
20
    g_byte[5] |= (1 << 2);
21
  } else   g_byte[5] &= ~(1 << 2);
22

23
i++;
24

25
}

Das sind eine ganze Menge if-Abfragen und das dauert. Wäre es schneller, 
wenn ich da ein eindimensionales Array mache?
Oder kann ich da mit Pointern irgendwas zaubern?
Eine komplette Lösung meines Problems brauch ich jetzt nicht, sondern 
nur einen kleinen Anstoß, in welche Richtung ich gucken sollte, denn 
scheinbar ist meine Lösung nicht sonderlich flott...

Grüße
Gast #2705884
Lesenswert?

ich glaube mit dieser variante kommst du nicht weiter.

du musst ein Array vorberechnen damit du in diesem Teil vom Programm nur 
noch ein ein array zukreifen musst.
1
g_byte[0] = daten[x];
2
g_byte[1] = daten[x];
3
g_byte[5] = daten[x];

beim berechnen von daten kommt es ja nicht auf die zeit an. Das braucht 
dann zwar mehr speicher ist aber schneller.

Aber PWM und schieberegister passt eh nicht zusammen, hast du überhaupt 
mal gerechnet welcher wiederholfreqenz möglich ist?
Gast #2705907
Lesenswert?

A. K. schrieb:
> Bei Indizierung mit Konstanten ist es irrelevant ob ein- oder
> mehrdimensional, da bereits der Compiler die Adresse berechnet.

aber nicht die zusätzliche Rechnerrei.

Die Frage ist auch vielmehr wozu er das array g_byte erst befüllt. Er 
muss die byte ja später sowiso seriell versenden, dann könnte er auch 
gleich an dieser stelle die aktuell pwm berechnen.
Gast #2705919
Lesenswert?

Oh Mann. Ich habs nicht kapiert. :-{

OK. Mit einer Schleife kriegst Du das Ganze nicht schneller.
Höchstens den Programmtext kürzer.

Falls nicht einer ohnehin raten kann, was der TO da macht, wäre es 
vielleicht hilfreich mal zu erklären was der Algorithmus ist und wie die 
Daten organisiert sind.
Möglicherweise kann man da was ändern und schneller machen.
Gast #2705932
Lesenswert?

A. K. schrieb:
> Bei Indizierung mit Konstanten ist es irrelevant ob ein- oder
> mehrdimensional, da bereits der Compiler die Adresse berechnet.

Das ist bei C leider dummes Zeug, was du da geschrieben hast.

Bei Pascal könnte man das so machen, aber C kennt keine mehrdimensinalen 
Felder. Stattdessen besteht ein scheinbar mehrdimensionales Feld aus 
einem Feld von Zeigern, die jeweils auf ein anderes Feld zeigen. Also 
ist bei jedem Zugriff ein mehrfaches Dereferenzieren angesagt.

Unser Freund "Weniger ist manchmal mehr" schrieb:
> Ich habe ein doppeltes Array[6][3]

Ja, eben. Schreib deine Quelle um - und zwar so, daß du nicht ständig 
den Berg kreißen und ne Maus gebären lassen mußt.
Als erstes schmeiß deine Variablen
  uint8_t array[6][3];
  uint8_t g_byte[6];
raus und ersetze sie durch simple Variablen.
Und dann arbeite besser mit temporären Variablen, die bei Atmels noch in 
Registern gehalten wrden können. Sowas kannst du 1x am Schleifenanfang 
auf 0 setzen und sparst dir dann sämtliche else-Zweige ein.

W.S.
#2706049
Lesenswert?

W.S. schrieb:

> Das ist bei C leider dummes Zeug, was du da geschrieben hast.
>
> Bei Pascal könnte man das so machen, aber C kennt keine mehrdimensinalen
> Felder. Stattdessen besteht ein scheinbar mehrdimensionales Feld aus
> einem Feld von Zeigern, die jeweils auf ein anderes Feld zeigen.

Was soll das denn nun? C ist schon schlimm genug, da muss man die Leute 
nicht noch mehr verwirren als nötig.

Arrays von Arrays sind keine Arrays von Zeigern auf Arrays. Auch wenn 
array[2] bei ihm auf einen Zeiger hinausläuft ist dieser Zeiger dennoch 
nirgends gespeichert, sondern berechnet.

Wenn man die Dinger vollständig durchindiziert besteht zwischen Arrays 
von Arrays in C und mehrdimensionalen Arrays in Pascal nur ein 
syntaktischer Unterschied, nicht aber im Code. In einem solchen 
Zusammenhang tauchen Zeiger nur auf, wenn man sich die Mühe macht, die 
Adressarithmetik des Compilers nachzuvollziehen. Ich habe folglich kein 
Problem damit, diese Dinger auch in C als mehrdimensionale Arrays zu 
bezeichnen.

Und egal wie man das nennt, seine Zugriffe wie array[5][2] werden vom 
Compiler auf konstante Adressen fertig berechnet (weil statisch). Zeiger 
tauchen da nirgends real auf und werden schon garnicht irgendwo 
gespeichert.
Gast #2706071
Lesenswert?

Guten Abend,

Ich hatte hier gerade doch noch einen ziemlich großen Denkfehler drin.

Die Funktion PWM ist zur Berechnung von g_byte da. g_byte wird nach 
jedem Durchlauf der Funktion per SPI an 3 Schieberegister gesendet. 
Somit ist g_byte die Darstellung meiner PWM auf dem Schieberegister.

Allerdings dauert die Berechnung ewig. Mein Gedankengang hinter der 
Funktion PWM ist:

Ich lasse eine Variable i von 0 bis 255 hochzählen. Wenn ich kleiner als 
ein Wert in einer PWM-Tabelle bin, dann wird ein Bit in g_byte auf 1 
gesetzt, andernfalls auf 0.

Da ich allerdings 18 PWM-Kanäle habe, muss ich jedes mal alle 18 Werte 
in der Tabelle nachgucken und dementsprechend meine Bits in g_byte 
setzen. Und das dauert ewig lange.

Vorher hab ich mich echt mistig ausgedrückt, ich hoffe, es ist jetzt 
klarer, was ich eigentlich versuche.

Gibts da also einen besseren Weg als der, den ich versuche zu gehen?
Gast #2706075
Lesenswert?

Mehr ist mehr schrieb:
> Da ich allerdings 18 PWM-Kanäle habe

lass uns doch erstmal Rechnen wieviel zeit du hast (oder auch nicht).

Welche PWM Frequenz braucht du?
Müssen immer 18 (oder sogar mehr) daten per SPI rausgeschoben werden und 
wie schnell
  -> wie lange dauert alleine das senden?
Welche Auflösung willst du haben (8bit?)

ich kann mir vorstellen das es sehr knapp wenn nicht sogar unmöglich 
ist.
Gast #2706099
Lesenswert?

Ok, überschlagsrechnung:

120 Hz, damits flimmerfrei ist. 8 Bit PWM, weils dadrunter keinen Sinn 
macht.

Also müsste ich 120 mal bis 256 zählen. Das heißt, die Funktion PWM 
müsste 30600 mal pro Sekunde durchlaufen, richtig? Bei einem Takt von 8 
MHz / 30600 = 261 Takte, die die Funktion dauern darf, wenn ich sonst 
nichts nebenher tue.

Klingt ziemlich wenig... Was braucht eine if-Abfrage denn so?
Gast #2706111
Lesenswert?

SPI sendet ja immer ein ganzes Byte und ich brauche alle 3. Ausserdem 
lass ich das schon auf schnellster Geschwindigkeit, also 4 Mhz laufen. 
Da ist nichts mehr zu holen.

Gerechnet habe ich 24 Bit * 120 = 2880 Bit, die ich bei 4 Mhz sende. Das 
sollte eigentlich kein Problem sein, oder?

Und, lang ists her, so ein alter Röhrenfernseher hat mit 50 Hz 
Bildwiederholung gearbeitet, oder nicht? Dann wäre ich nämlich bei 625 
Takten, die ich Zeit hätte. Und das ist wieder ein ganz anderer Schnack.
Gast #2706305
Lesenswert?

Mehr ist mehr schrieb:
> Gerechnet habe ich 24 Bit * 120 = 2880 Bit, die ich bei 4 Mhz sende. Das
> sollte eigentlich kein Problem sein, oder?

jetzt musst du aber noch * 256 rechnen weil du ja eine PWM machen 
willst.


also 24bit  120  256 = 737280bit/s  (müsste noch nachbar sein, wird 
aber eng) Immerhin muss du ja auch jedes byte in der ISR senden.

ich würde die berechnung der zu senden byte gleich an der stelle machen 
wo du sie senden willst. Also erst ein byte senden und dann das nächste 
byte berechnen.
Moderator #2706395
Lesenswert?

Wozu dient tabelle? Könnte der Wert, der von tabelle ausgelesen
wird, nicht direkt schon in array stehen? Dann würde man sich den
Array-Zugriff sparen. Da die Zugriffe auf array und g_byte aus den
Gründen, die A. K. oben genannt hat, de facto gar keine Array-Zugriffe
sind, werden damit sämtliche Array-Zugriffe eliminiert.

Warum hat g_byte 6 Elemente, von denen jeweils nur 3 Bits genutzt
werden? Ich dachte, du wolltest per SPI 24 Bits hinausschicken, von
denen 18 belegt sind. Da würde es sich doch eher anbieten, g_byte mit
3 Elementen zu dimensionieren und in jedem Element 6 Bits zu nutzen.

Da in jedem Aufruf von PWM sämtliche relevanten Bits der Elemente von
g_byte aktualisiert werden, kannst du erst alle Bits löschen und dann
diejenigen setzen, bei denen die Bedingung erfüllt ist. Dadurch ersparst
du dir die viele Else-Zweige.

Das Ganze würde dann etwas so aussehen:
1
uint8_t array[6][3];
2
uint8_t g_byte[3];
3

4
void PWM(void) {
5
  uint8_t static i;
6
  uint8_t byte;
7

8
  byte = 0;
9
  if (i < array[0][0])
10
    byte |= 1 << 0;
11
  if (i < array[1][0])
12
    byte |= 1 << 1;
13
  if (i < array[2][0])
14
    byte |= 1 << 2;
15
  if (i < array[3][0])
16
    byte |= 1 << 3;
17
  if (i < array[4][0])
18
    byte |= 1 << 4;
19
  if (i < array[5][0])
20
    byte |= 1 << 5;
21
  g_byte[0] = byte;
22

23
  byte = 0;
24
  if (i < array[0][1])
25
    byte |= 1 << 0;
26
  if (i < array[1][1])
27
    byte |= 1 << 1;
28
  if (i < array[2][1])
29
    byte |= 1 << 2;
30
  if (i < array[3][1])
31
    byte |= 1 << 3;
32
  if (i < array[4][1])
33
    byte |= 1 << 4;
34
  if (i < array[5][1])
35
    byte |= 1 << 5;
36
  g_byte[1] = byte;
37

38
  byte = 0;
39
  if (i < array[0][2])
40
    byte |= 1 << 0;
41
  if (i < array[1][2])
42
    byte |= 1 << 1;
43
  if (i < array[2][2])
44
    byte |= 1 << 2;
45
  if (i < array[3][2])
46
    byte |= 1 << 3;
47
  if (i < array[4][2])
48
    byte |= 1 << 4;
49
  if (i < array[5][2])
50
    byte |= 1 << 5;
51
  g_byte[2] = byte;
52

53
  i++;
54
}

Ohne den CALL und RET-Befehl braucht die Funktion 104 Taktzyklen.

Statt die 3 Bytes erst in g_byte zu schreiben, kannst du sie auch
gleich an das SPI schicken. Damit sparst du noch ein paar Zyklen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren