Forum: Mikrocontroller und Digitale Elektronik Atmega8 langsam, viel versucht = ratlos


von Niklas F. (n8fever)


Angehängte Dateien:

Lesenswert?

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
Frequenzen der LEDs von etwa
entstehen.

-*TOOLCHAIN UND FLASHEN:*
avr-g++ leuchte.cpp -O3 -mmcu=atmega8 -o delme.elf;
avr-objcopy -O ihex delme.elf delme.hex;
avrdude -i10 -p m8 -c avr910 -P /dev/ttyUSB0 -v -U flash:w:delme.hex:a

-*FUSES (nach avrdude)*
lfuse reads as E1
hfuse reads as D9 =>
Frequenz von 1MHz nach
http://www.engbedded.com/fusecalc/

Ausführliche Ausgabe von avrdude hier:
http://students.informatik.uni-luebeck.de/~finck/output.txt

-*FEHLERBEHEBUNGSVERSUCHE:*
-atmega ausgetauscht
-programmcode mit einem Freund gemeinsam angesehen
(bis auf langsames Verhalten konform)
-externe Spannungsquelle benutzt
-Fuses anders gesetzt

-*'UMGEBUNG'*
-MYSMARTUSB MK2
-ATMEGA8
-LINUX
-AVR-GCC
-AVRDUDE

-*WAS ES WERDEN SOLL:*
Eine Stimmungslampe zu Weihnachten

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


Lesenswert?

>> 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.

von Vlad T. (vlad_tepesch)


Lesenswert?

ließ dir bitte nochmal den Artikel zu Software-PWM durch

von Andreas Kr (Gast)


Lesenswert?

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

von Peter (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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!

von Niklas F. (n8fever)


Lesenswert?

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-Symbole

Laufzeit
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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Niklas F. (n8fever)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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
inline void output()
2
{
3
  uint8_t led, t;
4
  uint8_t mask;
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_t led = 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.

von Peter (Gast)


Lesenswert?

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

von Εrnst B. (ernst)


Lesenswert?

Niklas Fink schrieb:
> [...] O-Notation [...]

Dabei nicht beachtet:
1
uint8_t mask = (1 << shift);
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.

von Karl H. (kbuchegg)


Lesenswert?

Darüber
1
inline void modify()
2
{
3
  for ( uint8_t i=0; i<LED_A_DIM ; i++ )
4
  {
5
    srand(get_seed());
reden wir noch.
Aber erstmal musst du deine PWM's richtig hinkriegen.

von Niklas F. (n8fever)


Lesenswert?

Jau, das mit dem Shift ist gut.
1
for ( t=0; t<254 ; t++ ) //255 bleibt immer an
2
        {
3
                uint8_t mask= 1;
4
                //alle Ports nacheinander
5
                //pruefe fuer alle LEDs ob diese noch an sein sollen
6
                PORTB = 0xFF;
7
                for ( led=0; led < 8 ; led++ )
8
                {
9
                        //schalte aus, wenn helligkeit erreicht
10
                        if ( t == led_array[led] )
11
                        {
12
                                //schalte spezielle LED aus
13
                                PORTB &= ( mask ^ 0xFF );
14
                        }
15
                        mask <<= 1;
16
                }
17
                mask = 1;
18
                PORTC = 0xFF;
19
                for ( ; led < 16  ; led++ )
20
                {
21
                        //schalte aus, wenn helligkeit erreicht
22
                        if ( t == led_array[led] )
23
                        {
24
                                //schalte spezielle LED aus
25
                                PORTC &= ( mask ^ 0xFF );
26
                        }
27
                        mask <<= 1;
28
                }
29
                mask = 1;
30
                PORTD = 0xFF;
31
                for ( ; led < 24 ; led++ )
32
                {
33
                        //schalte aus, wenn helligkeit erreicht
34
                        if ( t == led_array[led] )
35
                        {
36
                                //schalte spezielle LED aus
37
                                PORTD &= ( mask ^ 0xFF );
38
                        }
39
                        mask <<= 1;
40
                }
41
        }


@Εrnst B✶:
Ja stimmt, es gibt ja nur LSL um eine Stelle, ganz vergessen.
Komme erst so langsam wieder rein :)

von Karl H. (kbuchegg)


Lesenswert?

1
               PORTB = 0xFF;
2
                for ( led=0; led < 8 ; led++ )
3
                {
4
                        //schalte aus, wenn helligkeit erreicht
5
                        if ( t == led_array[led] )
6
                        {
7
                                //schalte spezielle LED aus
8
                                PORTB &= ( mask ^ 0xFF );

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.

von Niklas F. (n8fever)


Lesenswert?

Die PWM funktioniert sehr gut mit den Ports
PortB 2-7
PORTC 0-7
PORTD 0-2
1
uint8_t led, 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_t mask= 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?

von Εrnst B. (ernst)


Lesenswert?

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...

von Karl H. (kbuchegg)


Lesenswert?

Ε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!

von Falk B. (falk)


Lesenswert?

Man könnte ja einfach mal den Artikel Soft-PWM lesen, da sind 90% 
aller Probleme erklärt und gelöst . . .

von Niklas F. (n8fever)


Lesenswert?

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!

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.