Hallo,
mein einfaches Programm (siehe Anhang, //Kommentiert) läuft auf einem
Atmega8 sehr sehr langsam, hat wer ne Idee was ich falsch mache?
Sitze schon sehr lange dran und bin für jede Idee dankbar!
-*WAS DAS PROGRAMM MACHEN SOLL*:
Mache eine einfache PWM auf allen Ports des Atmega8
-*WAS ES GERADE MACHT*:
Man kann den LEDs beim bliiinken zusehen,
sehr lahm!
-*WIE DAS PROGRAMM PWM REALISIERT*:
1)Speichere gewollte Helligkeit in ein Array (led_array[])
2)while(1)
{
Laufe über eine for-Schleife mit Variable t=1:255 und teste
for alle LEDs 'led' t<=helligkeit(led)?,
dann (led=an) : (led=aus sonst)
}
-*LAUFZEIT in O(TAKTE)*
O(for 't' * for 'led' * sehr_wenig ) =
O(255 * #PORTS) =
O(255 * 24) =
O(6120)
Daraus sollte bei Takt von
>> Daraus sollte bei Takt von 1MHz>> Frequenzen der LEDs von etwa 160Hz>> entstehen.
Wie kommst du darauf?
Nicht jeder C-Befehl braucht 1 Taktyklus. Genau genommen sogar die
allerwenigsten... :-o
Lass dein Programm doch mal im Simulator laufen und lies dort die Zeit
ab.
Wenn ich beim Überfliegen dein Programm richtig interpretiert habe,
machst du deine PWM-Genierung für das gesamte LED-Array nacheinander
anstatt parallel! Dadurch teilst du die PWM-Wiederholfrequenz durch die
Anzahl an LEDs, wahrscheinlich 8.
Schau dir mal den Artikel zu Software PWM Erzeugung an und versuch das
zum Laufen zu kriegen. Dann kannst du immer noch was ändern.
Gruss
Andreas
selbst diese einfach anweisung braucht vermutlich schon mindestens 3
Takte
pc |= mask;
dann der compiler wird nicht erkennen das hierbei nur 1 bit gesetzt
wird, damit bleibt ihm nichts anderes Übrig als
port einlesen
daten verodern
port ausgeben
Und diesen teil rufst du viel zu oft auf, ständig wird die LED
ausgeschaltet auch wenn sie schon lange aus ist.
Niklas Fink schrieb:> Hallo,>> mein einfaches Programm (siehe Anhang, //Kommentiert) läuft auf einem> Atmega8 sehr sehr langsam, hat wer ne Idee was ich falsch mache?
Du hast dem Prozessor mit deiner switch_led Funktion jede Menge
Mehrarbeit aufgehalst, die nicht notwendig wäre, wenn du selber von
vorne herein gleich die Aufteilung der LEDs auf die einzelnen Ports
berücksichtigen würdest. Dann müsstest du zb nicht die Maske so
kompliziert zusammenbauen, sondern könntest einfach ein Masken-1 in
einer Schleife für jede LED um 1 Stelle weiterschieben.
Auch die ganze Portbyteadressierung würde einfacher. Das sind alles
Takte im inneren von geschachtelten Schleifen, die sich aufsummieren.
Kurz und gut: die Funktion switch_led ist kontraproduktiv. Und zwar
enorm!
Danke für die Antwort.
Ich vermute gerade ein Problem mit einem Port.
Wie ziehe ich den Post zurück?
O-Notation
Die O-Notation ist nur eine grobe Schätzung.
Für Landau-Notation (O-Notation), siehe:
http://de.wikipedia.org/wiki/Landau-SymboleLaufzeit
Wäre interessant das abzuschätzen,
Ich schätze die Kosten mit
24 Takte ab.
Bei 8MHz also 160/3 = 53Hz = genug.
240 Takte wie bisher sind es wirklich nicht, oder?
Ich arbeite gerade an dem Port-Ding.
Simulation
-AVR-Studio läuft unter wine nicht
-AVR-Studio unter xen habe ich nicht vollends einrichten können (USB=
DomU vs. Dom0?)
-Ich versuche mich mal an simulavr, auch wenn ich gerade nicht den
Programmcode für Fehlerhaft halte
Niklas Fink schrieb:> *Laufzeit*> Wäre interessant das abzuschätzen,> Ich schätze die Kosten mit> 24 Takte ab.
24 Takte wofür?
> Bei 8MHz also 160/3 = 53Hz = genug.> 240 Takte wie bisher sind es wirklich nicht, oder?
Doch. Leicht.
Du halst deinem µC in switch_led jede Menge unnötige Arbeit auf!
Für jede LED muss er sich erneut den Port bestimmen sowie die Bitnummer
an diesem Port. Für jede LED muss er sich aus der Bitnummer erneut eine
Maske zusammenschieben. Für jede LED muss er erneut durch die Abfragen
durch, welcher Port betroffen ist.
Das alles summiert sich.
Für 1 LED!
Du hast 24 LED
und du hast 255 Zeitzyklen pro LED.
VIELEN DANK,
ich werde meinen Code effizienter gestalten!
Erstmal kümmere ich mich um PortB,PortC,
denn für die LEDs 5-18 Läuft der Code schnell!
Vielen Dank!
Niklas Fink schrieb:> VIELEN DANK,> ich werde meinen Code effizienter gestalten!>> Erstmal kümmere ich mich um PortB,PortC,
Erst mal solltest du output() umschreiben
1
inlinevoidoutput()
2
{
3
uint8_tled,t;
4
uint8_tmask;
5
6
//fuer alle Zeitschritte in einem Zyklus der PWM
7
for(t=0;t<255;t++)
8
{
9
//pruefe fuer alle LEDs ob diese noch an sein sollen
10
uint8_tled=13;
11
12
// Alle LED an Port B
13
pb=0;
14
mask=0x01;
15
for(led=0;led<8;led++)
16
{
17
if(t<=led_array[led])
18
pb|=mask;
19
mask<<=1;
20
}
21
PORTB=pb;
22
23
// Alle LED an Port C
24
pc=0;
25
mask=0x01;
26
for(;led<16;led++)
27
{
28
if(t<=led_array[led])
29
pc|=mask;
30
mask<<=1;
31
}
32
PORTC=pc;
33
34
// Alle LED an Port D
35
pd=0;
36
mask=0x01;
37
for(;led<24;led++)
38
{
39
if(t<=led_array[led])
40
pc|=mask;
41
mask<<=1;
42
}
43
PORTD=pd;
44
}
45
}
Das ist so ziemlich das Maximum, was du mit dieser Technik der Soft-PWM
rausholen kannst. Wenn das nicht reicht, dann musst du mit den 255
runter und weniger PWM-Stufen in Kauf nehmen.
Niklas Fink schrieb:> AVR-Studio unter xen habe ich nicht vollends einrichten können (USB=> DomU vs. Dom0?)
du braucht für den Simulator überhaupt kein USB
hat auf dem AVR O(n), das muss in deine Betrachtungen mit
reinmultipliziert werden!
Die Lösung vom Karl Heinz umgeht das geschickt, indem "mask" einfach
jeden Durchlauf eine Stelle geschoben wird.
Du stehst offenbar darauf, deinem µC mehr Arbeit als notwendig zu
verschaffen.
Ich hab absichtlich nicht direkt am Port gearbeitet und ich hab
absichtlich nur Bits in einer Variablen gesetzt, die mit einem Wert
von 0 gestartet ist.
Das hier
1
PORTB&=(mask^0xFF);
kann der COmpiler auf dem Mega8 schon wieder nicht als 1 Operation
implementieren sondern er muss schon wieder durch die komplette C
Operation durch!
1
if(t==led_array[led])
und ich hab auch absichtlich
1
if(t<=led_array[led])
gelassen.
Ob das jetzt < oder <= heissen soll, kann man diskutieren. Aber == ist
völlig daneben.
Die PWM funktioniert sehr gut mit den Ports
PortB 2-7
PORTC 0-7
PORTD 0-2
1
uint8_tled,t;
2
pb=0;
3
pc=0;
4
pd=0;
5
//fuer alle Zeitschritte in einem Zyklus der PWM
6
//fange mit t=255 an und schalte an ab erreichter helligkeit
7
for(t=255;t>LOW;t--)
8
{
9
uint8_tmask=1;
10
//alle Ports nacheinander
11
//pruefe fuer alle LEDs ob diese noch an sein sollen
12
for(led=2;led<8;led++)
13
{
14
//schalte aus, wenn helligkeit erreicht
15
if(t==led_array[led])
16
{
17
//schalte spezielle LED aus
18
pb|=mask;
19
}
20
mask<<=1;
21
}
22
mask=1;
23
for(;led<16;led++)
24
{
25
//schalte aus, wenn helligkeit erreicht
26
if(t==led_array[led])
27
{
28
//schalte spezielle LED aus
29
pc|=mask;
30
}
31
mask<<=1;
32
}
33
mask=1;
34
for(;led<19;led++)
35
{
36
//schalte aus, wenn helligkeit erreicht
37
if(t==led_array[led])
38
{
39
//schalte spezielle LED aus
40
pd|=mask;
41
}
42
mask<<=1;
43
}
44
PORTB=pb;
45
PORTC=pc;
46
PORTD=pd;
47
}
@Karl heinz Buchegger
Hast Recht, wollte nur mit == arbeiten um die Innere if-Anweisung
einzusparen. Die Schleife läuft jetzt abwärts.
Kostet schreiben auf I/O-(?)Register(?) mehr?
Niklas Fink schrieb:> Kostet schreiben auf I/O-(?)Register(?) mehr?
Was du machst ist ein Read-Modify-Write auf einem volatile-Register. Bei
einzel-Bits kann der AVR-GCC das auf Set-Bit-Operationen optimieren,
aber nicht mit ner variablen Maske...
Εrnst B✶ schrieb:> Niklas Fink schrieb:>> Kostet schreiben auf I/O-(?)Register(?) mehr?>> Was du machst ist ein Read-Modify-Write auf einem volatile-Register. Bei> einzel-Bits kann der AVR-GCC das auf Set-Bit-Operationen optimieren,> aber nicht mit ner variablen Maske...
Oder auf Deutsch (nicht das Ernst B* was falsches geschrieben hätte):
Wenn du direkt an die Ports rangehst, dann willst du entweder
* den Port als Ganzes setzen
* oder nur mit etwas konstantem an die Portbits ran.
* wenns gar nicht anders geht, dann willst du dir erst im Speicher die
Einzelteile zurecht legen und dann in einem Rutsch auf den Port
raus
Alles andere ist für den µC Aufwand!
Danke für die vielen Antworten, ich habe nun den schwerwiegensten Fehler
gefunden.
Compileroptimierung!
Wenn ich mit -O3 compiliere:
avr-g++ leuchte.cpp -O3 -mmcu=atmega8 -o delme.elf
Dann ist eine Soft-PWM nichtmehr erkennbar (vorher ca. 5Hz).
Ich werde nochmal Posten, wenn das Projekt fertig ist und alles
erklären.
Bis dahin:
DANKE!