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_tarray[6][3];
2
uint8_tg_byte[6];
3
.
4
.
5
.
6
voidPWM(void){
7
uint8_tstatici=0;
8
9
if(i<tabelle[array[0][0]]){
10
g_byte[0]|=(1<<0);
11
}elseg_byte[0]&=~(1<<0);
12
13
if(i<tabelle[array[0][1]]){
14
g_byte[0]|=(1<<1);
15
}elseg_byte[0]&=~(1<<1);
16
.
17
.
18
.
19
if(i<tabelle[array[5][2]]){
20
g_byte[5]|=(1<<2);
21
}elseg_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
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?
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.
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.
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.
W.S. schrieb:> Bei Pascal könnte man das so machen, aber C kennt keine mehrdimensinalen> Felder.
mein C schon.
int array[3][4];
und dort kommen keine zeiger vor.
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.
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?
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.
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?
du hast das SPI vergessen, kannst du denn überhaupt so schnell die daten
senden?
du muss ja 120 mal Pro sekunde alle 18bits raussenden? Ich weiss ja
nicht was du da noch an overhead hast.
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.
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.
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_tarray[6][3];
2
uint8_tg_byte[3];
3
4
voidPWM(void){
5
uint8_tstatici;
6
uint8_tbyte;
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.