www.mikrocontroller.net

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


Autor: Niklas Fink (n8fever)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Vlad Tepesch (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ließ dir bitte nochmal den Artikel zu Software-PWM durch

Autor: Andreas Kr (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Niklas Fink (n8fever)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Niklas Fink (n8fever)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
inline void output()
{
  uint8_t led, t;
  uint8_t mask;

  //fuer alle Zeitschritte in einem Zyklus der PWM
  for ( t=0; t<255 ; t++ )
  {
    //pruefe fuer alle LEDs ob diese noch an sein sollen
    uint8_t led = 13;

    // Alle LED an Port B
    pb = 0;
    mask = 0x01;
    for ( led = 0; led < 8; led++ )
    {
      if ( t <= led_array[led] )
        pb |= mask;
      mask <<= 1;
    }
    PORTB = pb;

    // Alle LED an Port C
    pc = 0;
    mask = 0x01;
    for ( ; led < 16; led++ )
    {
      if ( t <= led_array[led] )
        pc |= mask;
      mask <<= 1;
    }
    PORTC = pc;

    // Alle LED an Port D
    pd = 0;
    mask = 0x01;
    for ( ; led < 24; led++ )
    {
      if ( t <= led_array[led] )
        pc |= mask;
      mask <<= 1;
    }
    PORTD = pd;
  }
}

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.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Εrnst B✶ (ernst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Niklas Fink schrieb:
> [...] O-Notation [...]

Dabei nicht beachtet:
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: Niklas Fink (n8fever)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jau, das mit dem Shift ist gut.
for ( t=0; t<254 ; t++ ) //255 bleibt immer an
        {
                uint8_t mask= 1;
                //alle Ports nacheinander
                //pruefe fuer alle LEDs ob diese noch an sein sollen
                PORTB = 0xFF;
                for ( led=0; led < 8 ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                PORTB &= ( mask ^ 0xFF );
                        }
                        mask <<= 1;
                }
                mask = 1;
                PORTC = 0xFF;
                for ( ; led < 16  ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                PORTC &= ( mask ^ 0xFF );
                        }
                        mask <<= 1;
                }
                mask = 1;
                PORTD = 0xFF;
                for ( ; led < 24 ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                PORTD &= ( mask ^ 0xFF );
                        }
                        mask <<= 1;
                }
        }


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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
               PORTB = 0xFF;
                for ( led=0; led < 8 ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                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
                                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!

                        if ( t == led_array[led] )
und ich hab auch absichtlich
                        if ( t <= led_array[led] )
gelassen.
Ob das jetzt < oder <= heissen soll, kann man diskutieren. Aber == ist 
völlig daneben.

Autor: Niklas Fink (n8fever)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die PWM funktioniert sehr gut mit den Ports
PortB 2-7
PORTC 0-7
PORTD 0-2
uint8_t led, t;
        pb = 0;
        pc = 0;
        pd = 0;
        //fuer alle Zeitschritte in einem Zyklus der PWM
        //fange mit t=255 an und schalte an ab erreichter helligkeit    
        for ( t=255; t>LOW ; t-- )
        {
                uint8_t mask= 1;
                //alle Ports nacheinander
                //pruefe fuer alle LEDs ob diese noch an sein sollen
                for ( led=2; led < 8 ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                pb |= mask;
                        }
                        mask <<= 1;
                }
                mask = 1;
                for ( ; led < 16  ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                pc |= mask;
                        }
                        mask <<= 1;
                }
                mask = 1;
                for ( ; led < 19 ; led++ )
                {
                        //schalte aus, wenn helligkeit erreicht
                        if ( t == led_array[led] )
                        {
                                //schalte spezielle LED aus
                                pd |= mask;
                        }
                        mask <<= 1;
                }
                PORTB = pb;
                PORTC = pc;
                PORTD = pd;
        }
@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?

Autor: Εrnst B✶ (ernst)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Falk Brunner (falk)
Datum:

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

Autor: Niklas Fink (n8fever)
Datum:

Bewertung
0 lesenswert
nicht 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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.