Atmega 2560 ausgelastet

Gast #5794105
Lesenswert?

Hallo!

ich habe folgendes Problemchen. Ich möchte mit meinem Atmega (in c++) ca 
300 LEDs ansteuern. Das ganze über 30 Pins die mit Vorwiderstand an den 
Anoden von je 10 LEDs hängen und dann eben noch 10 Pins die im Wechsel 
einen FET schalten um einen Abschnitt der LEDs zu aktivieren.

Das ganze klappt auch soweit. Ich erzeuge Effekte in der Main Funktion 
und eine ISR kümmert sich darum dass immer einer meiner FETs angesteuert 
wird.

Nun kommen im Programm so einige rand()%x Aufrufe vor und ich habe 
beobachtet, dass meine Anzeige ab einer kritischen Menge von ca 10 
Zufallsberechnungen im Pfad stehen bleibt. Es sind aber nach dem 
compilieren gerade mal 1% Speicher belegt. Frisst die rand() zur 
Laufzeit derartig viele Ressourcen dass der uC so schnell an seine 
Grenzen kommt? Wie überprüft man sowas am besten?
#5794154
Lesenswert?

Ralf Eden schrieb:
> Hallo!
>
> ich habe folgendes Problemchen. Ich möchte mit meinem Atmega (in c++) ca
> 300 LEDs ansteuern. Das ganze über 30 Pins die mit Vorwiderstand an den
> Anoden von je 10 LEDs hängen und dann eben noch 10 Pins die im Wechsel
> einen FET schalten um einen Abschnitt der LEDs zu aktivieren.

Das nennt man eine LED-Matrix.

> Das ganze klappt auch soweit. Ich erzeuge Effekte in der Main Funktion
> und eine ISR kümmert sich darum dass immer einer meiner FETs angesteuert
> wird.

Klingt gut.

> Nun kommen im Programm so einige rand()%x Aufrufe vor und ich habe
> beobachtet, dass meine Anzeige ab einer kritischen Menge von ca 10
> Zufallsberechnungen im Pfad stehen bleibt.

Programmierfehler.

> Es sind aber nach dem
> compilieren gerade mal 1% Speicher belegt. Frisst die rand() zur
> Laufzeit derartig viele Ressourcen dass der uC so schnell an seine
> Grenzen kommt?

Nö.

> Wie überprüft man sowas am besten?

Mit einer systematischen Fehlersuche.
Moderator (Firma: Titel) Persönliche Seite #5794156
Lesenswert?

Ralf Eden schrieb:
> eine ISR kümmert sich darum
Wie sieht denn diese ISR aus? Sind da drin Schleifen? Oder sogar 
irgendwelche Warteschleifen?

> Wie überprüft man sowas am besten?
Such mal nach "float" und/oder nach "double" in deinem Programm. Wenn da 
sowas vorkommt, dann findest du Zeitfresser recht schnell.

Ein recht guter Ansatz ist auch die Suche nach delay() und/oder seine 
Derivate...

> Wie überprüft man sowas am besten?
Man misst/berechnet die Zykluszeit der Hauptschleife. Messen geht z.B. 
über eine LED, die pro Durchlauf getoggelt wird. Mit einem Oszi kann man 
dann recht leicht feststellen, wie lang die dann braucht. Das selbe kann 
man mit einer LED in der ISR machen: am Anfang setzen, am Ende 
zurücksetzen, Oszi dran und ein paar Minuten laufen lassen, um auch 
Ausreißer zu erfassen.
Gast #5794372
Lesenswert?

Aber was bringt es mir, die Zykluszeit der Hauptschleife zu messen? Die 
sollte ja unkritisch sein, nachdem die Ansteuerung der LEDs von der ISR 
übernommen wird.

Die ISR ist unterdessen tatsächlich nicht gerade die feine englische 
Art, da in einer Schleife die Bits entsprechend der Pinbelegung 
angeordnet werden müssen. Aber sie läuft immer gleich lang ab und bis 
zum nächsten Interrupt vergehen sicher 50*256 Taktzyklen.
#5794446
Lesenswert?

Ralf Eden schrieb:
> Die ISR ist unterdessen tatsächlich nicht gerade die feine englische
> Art, da in einer Schleife die Bits entsprechend der Pinbelegung
> angeordnet werden müssen.

Da die Multiplexrate deutlich höher sein wird, als die Änderungsrate, 
könnte man die Bitreihenfolge besser vor Speicherung in den Bild-RAM 
umändern. Das spart deutlich CPU-Last.
Man könnte natürlich auch gleich die LED-Matrix in der optimalen 
Reihenfolge an den AVR anschließen.
Gast #5794464
Lesenswert?

Peter D. schrieb:
> Ralf Eden schrieb:
> Die ISR ist unterdessen tatsächlich nicht gerade die feine englische
> Art, da in einer Schleife die Bits entsprechend der Pinbelegung
> angeordnet werden müssen.
>
> Da die Multiplexrate deutlich höher sein wird, als die Änderungsrate,
> könnte man die Bitreihenfolge besser vor Speicherung in den Bild-RAM
> umändern. Das spart deutlich CPU-Last.
> Man könnte natürlich auch gleich die LED-Matrix in der optimalen
> Reihenfolge an den AVR anschließen.


Ja stimmt, das steht auch auf der Todo-Liste. Aber die Ursache kann das 
ja eigentlich kaum sein denke ich.
#5794489
Lesenswert?

Ralf Eden schrieb:
> Die ISR ist unterdessen tatsächlich nicht gerade die feine englische
> Art, da in einer Schleife die Bits entsprechend der Pinbelegung
> angeordnet werden müssen.

jetzt nicht direkt zum Thema:

__builtin_avr_insert_bits ist bekannt?

https://gcc.gnu.org/onlinedocs/gcc/AVR-Built-in-Functions.html

damit gehen so Bit-Umsortierereien recht komfortabel und flott.
Natürlich nur, wenn die Pinbelegung zur Compilezeit schon bekannt ist.
Moderator (Firma: Titel) Persönliche Seite #5794503
Lesenswert?

Ralf Eden schrieb:
> Aber was bringt es mir, die Zykluszeit der Hauptschleife zu messen? Die
> sollte ja unkritisch sein, nachdem die Ansteuerung der LEDs von der ISR
> übernommen wird.
Wenn die Hauptschleife "normalerweise" 500 mal pro Sekunde durchlaufen 
wurde, nach einer "kleinen" Codeänderung an ganz anderer Stelle, z.B. in 
der Interruptroutine(!) aber auf einmal nur noch 200 mal, dann war die 
Änderung doch nicht so "klein" wie vermutet.

Ralf Eden schrieb:
> beobachtet, dass meine Anzeige ab einer kritischen Menge von ca 10
> Zufallsberechnungen im Pfad stehen bleibt.
Definiere "stehen bleiben", wie stellst du das fest?
#5794505
Lesenswert?

Was soll rand() als Random Generator ?
Nimm ein LFSR, siehe XApp210.pdf, XApp211.pdf, XApp217.pdf, XApp220.pdf 
von Xilinx.
Mit der Laenge kannst du dir die Repetierhaeufigkeit einstellen.
Ich denke oberhalb von 20 bit, entsprechend 1:1'000'000 bist du gut 
aufgestellt.
Ich wuerd's mit 32 bit probieren, mit Longint rechnen. Das waere dann 
eine wiederholung alle 4 Milliarden bit.
Gast #5794527
Lesenswert?

So hier ist mal der Code soweit. In der Main ist die Änderung 
auskommentiert, die zum Freeze führt. Wenn ich an anderer Stelle die 
Anzahl an rand()-Aufrufen reduziere, funktioniert die Änderung.
Angehängte Dateien:
Beitrag #5794535 wurde von einem Moderator gelöscht.
Beitrag #5794542 wurde von einem Moderator gelöscht.
Beitrag #5794543 wurde von einem Moderator gelöscht.
Beitrag #5794547 wurde von einem Moderator gelöscht.
Beitrag #5794551 wurde von einem Moderator gelöscht.
#5794572
Lesenswert?

Ralf Eden schrieb:
> So hier ist mal der Code soweit. In der Main ist die Änderung
> auskommentiert, die zum Freeze führt. Wenn ich an anderer Stelle die
> Anzahl an rand()-Aufrufen reduziere, funktioniert die Änderung.

Deine Formatierung ist ungünstig.

Auf den ersten Blick sehe ich keinen massiven Fehler. Aber es würde mich 
nicht wundern, wenn dein Funktion zum Erzeugen neuer Anzeigemuster 
spinnt. Das reine Multiplexen sieht erstmal OK aus.
#5794575
Lesenswert?

Ralf Eden schrieb:
> So hier ist mal der Code soweit.

Mit C++ kenne ich mich nicht so aus. Irgendwann habe ich den Faden 
verloren, wer wen aufruft. Vielleicht sollte man die Codehäppchen etwas 
größer machen und nicht alles bis zu Einzeilern zerteilen.

Es sieht so aus, daß fast alles im Interrupt läuft, also ne richtig 
große Menge Holz. Daneben soll aber der Interrupt noch die Helligkeit 
ändern, d.h. ne PWM machen (je LED?).
Wenn der Interrupt dicht ist, dann läuft Main auf Sparflamme, d.h. 
zwischen den Interrupts wird nur noch ein Befehl des Main ausgeführt. 
Das kann so wirken, als ob das Main steht.
Ein Aufräumen wäre also dringeng angeraten.
Man sollte auch vermeiden, für jedes Bit ein Byte zu verbraten. 
Bitmasken (ein Byte je 8 Bit) machen das ganze nochmal schneller.
Gast #5794582
Lesenswert?

Falk B. schrieb:
>
> Deine Formatierung ist ungünstig.
>
> Auf den ersten Blick sehe ich keinen massiven Fehler. Aber es würde mich
> nicht wundern, wenn dein Funktion zum Erzeugen neuer Anzeigemuster
> spinnt. Das reine Multiplexen sieht erstmal OK aus.

Kannst du das präzisieren? Was würdest du genau anders machen?

 Das seltsame ist ja, dass die Probleme erst auftreten, sobald ich zu 
viele Parameter per Zufall erzeuge. Solange ich in diesem Fall die 
beiden Directions fest übergebe klappt alles. Ich kann auch die 
Directions per Zufall bestimmen und dafür die Axen fest übergeben. Dann 
läuft es auch. Mein Eindruck ist, dass einzig und allein die Anzahl der 
rand() Aufrufe den Unterschied macht.
Gast #5794594
Lesenswert?

Peter D. schrieb:
> Ralf Eden schrieb:
> So hier ist mal der Code soweit.
>
> Mit C++ kenne ich mich nicht so aus. Irgendwann habe ich den Faden
> verloren, wer wen aufruft. Vielleicht sollte man die Codehäppchen etwas
> größer machen und nicht alles bis zu Einzeilern zerteilen.
>
> Es sieht so aus, daß fast alles im Interrupt läuft, also ne richtig
> große Menge Holz. Daneben soll aber der Interrupt noch die Helligkeit
> ändern, d.h. ne PWM machen (je LED?).
> Wenn der Interrupt dicht ist, dann läuft Main auf..

Nein im Interrupt wird nur umsortiert u die Portzuweisung gemacht. 
Wobei die sortierung wirklich noch raus kann aber das kann es kaum sein. 
Da sind noch ca 50*256 Taktzyklen Zeit. Helligkeit wird nicht im 
Interrupt behandelt.
#5794619
Lesenswert?

Ralf Eden schrieb:

>> Auf den ersten Blick sehe ich keinen massiven Fehler. Aber es würde mich
>> nicht wundern, wenn dein Funktion zum Erzeugen neuer Anzeigemuster
>> spinnt. Das reine Multiplexen sieht erstmal OK aus.
>
> Kannst du das präzisieren? Was würdest du genau anders machen?

Einfach mal eine EINFACHE Funktion schreiben, die einfach ein paar 
Zahlen hochzählt und anzeigt.

>  Das seltsame ist ja, dass die Probleme erst auftreten, sobald ich zu
> viele Parameter per Zufall erzeuge.

Wo denn?

In deiner Hauptschleife wählst du zufällig eine von 5 Funktionen aus. In 
den einzelnen Funktion stecken auch wieder lange delays. Aber das ist 
nebensächlich. Denn das Multiplexing ist davon unberührt. Bestenfall 
wenn die CPU-Last im Multiplexing zu hoch wird (sehr nahe 100%), kommt 
das Hauptprogramm gar nicht mehr merklich vorwärts. Das kann man aber 
messen, auch ohne Oszi.

Schalte in JEDEM Interrut zu Beginn ein IO-Pin auf HIGH und am Ende 
wieder auf LOW. Damit kann man die CPU-Last der ISRs messen. Mit dem 
Oszi ist es komfortabel, es reicht aber auch ein Multimeter! Denn das 
mißt den Mittelwert des Signals. Wenn du dort >90% von VCC mißt, hast du 
ein Problem.

> Solange ich in diesem Fall die
> beiden Directions fest übergebe klappt alles.

Wo denn? Mit welcher Funktion?

 Ich kann auch die
> Directions per Zufall bestimmen und dafür die Axen fest übergeben. Dann
> läuft es auch. Mein Eindruck ist, dass einzig und allein die Anzahl der
> rand() Aufrufe den Unterschied macht.

Sicher nicht, denn die sind relativ kurz, ein paar Dutzend Takte.

Man kann den Spies auch mal rumdrehen. Lass die ISRs AUS! Kein sei() vor 
der Hauptschleife! Und dann lass deine Datengenerierungsfunktionen mal 
laufen und schalte vor jedem Aufruf/Durchlauf eine Status-LED an und 
nach dem Aufruf aus. Dann siehst du, ob da was langsamer wird oder 
stehen bleibt, wenn du deine Aufrufparameter veränderst.
Gast #5794628
Lesenswert?

Falk B. schrieb:
> Wo denn?
>

Siehst du in der Main. Dort sind zwei Funktionsaufrufe auskommentiert. 
So wie der Code JETZT ist, funktioniert er tadellos. Erst wenn durch 
diese beiden veränderten Funktionsaufrufe weitere rand Aufrufe statt 
finden fangen die Probleme an. Ich kann aber dann an anderer Stelle im 
Pfad rand Aufrufe weg nehmen und es klappt wieder.
Gast #5794639
Lesenswert?

Peter D. schrieb:
> Falk B. schrieb:
> In
> den einzelnen Funktion stecken auch wieder lange delays.
>
> Das kann die Ursache sein. _delay_ms(1); geht davon aus, daß es 16000
> Zyklen verbraten soll. Wenn es aber nur jede 1ms (1/960Hz) für einen
> Zyklus dran kommt, ist es 16000-fach langsamer (16s statt 1ms).

Guter Punkt, aber nochmal. So wie das Programm jetzt ist läuft es wie 
gewünscht. Dann würde es ja gar nicht laufen.
Beitrag #5794641 wurde von einem Moderator gelöscht.
#5794644
Lesenswert?

Ralf Eden schrieb:
> Falk B. schrieb:
>> Wo denn?
>>
>
> Siehst du in der Main. Dort sind zwei Funktionsaufrufe auskommentiert.
> So wie der Code JETZT ist, funktioniert er tadellos. Erst wenn durch
> diese beiden veränderten Funktionsaufrufe weitere rand Aufrufe statt
> finden fangen die Probleme an. Ich kann aber dann an anderer Stelle im
> Pfad rand Aufrufe weg nehmen und es klappt wieder.

OK. Hmmm, merkwürdig. Denn das sind einfache, aufeinanderfolgende 
Funktionsaufrufe.

Deine Funktion

void var_delay_ms(double ms)
{
  while(0 < ms)
  {
    _delay_ms(1);
    --ms;
  }
}

funktioniert zwar, der Parameter MS ist aber mit double nonsense. Die 
Funktion kann nur ganzzahlige Millisekunden warten, also reicht ein 
Paramter vom Typ integer oder Long. aber das ist nicht das Problem.
Moderator (Firma: Titel) Persönliche Seite #5794649
Lesenswert?

Ralf Eden schrieb:
> Erst wenn durch diese beiden veränderten Funktionsaufrufe weitere rand
> Aufrufe statt finden
Was passiert denn, wenn du die Funktion mal so oder mal so aufrufst:
1
        // mal so
2
        CFbelt(randAxis(), ((rand()%20)+25), DIR_ASC);  
3
        // un beim zweiten Test so
4
        CFbelt(randAxis(), ((rand()%20)+25), DIR_DESC);

Denn das hier scheint mir sehr eigenartig: 2 Möglichkeiten, aber modulo 
3...
1
Direction randDirection(void)
2
{
3
  switch(rand()%3)  // stimmt das wirklich?
4
  {
5
    case 0:
6
    default:
7
      return DIR_ASC;
8
    break;
9
    case 1:
10
      return DIR_DESC;
11
    break;
12
  }
13
}
Ich hätte das so gemacht:
1
Direction randDirection(void)
2
{
3
  if(rand()&1) return DIR_ASC;
4

5
  return DIR_DESC;
6
}
Gast #5794653
Lesenswert?

Falk B. schrieb:
> OK. Hmmm, merkwürdig. Denn das sind einfache, aufeinanderfolgende
> Funktionsaufrufe.
>
> Deine Funktion
>
> void var_delay_ms(double ms)
> {
>   while(0 < ms)
>   {
>     _delay_ms(1);
>     --ms;
>   }
> }
>
> funktioniert zwar, der Parameter MS ist aber mit double nonsense. Die
> Funktion kann nur ganzzahlige Millisekunden warten, also reicht ein
> Paramter vom Typ integer oder Long. aber das ist nicht das Problem.

Ich habe Double genommen, weil _delay_ms auch einen Double Wert 
erwartet, wenn ich nicht irre. Aber stimmt ein uint16 sollte ausreichen.

Gut also werde ich mir wenn ich Zuhause bin die ISR nochmal genauer 
anschauen. Wenn die Last hier tatsächlich das Problem sein sollte, 
müsste es ja mit einem größeren Prescaler laufen. Trotzdem seltsam aber 
schauen wir mal.
#5794688
Lesenswert?

Kleiner Tipp. Die Verdrehung der IO-Bits kann man einfach und DEUTLICH 
schneller mit einer Tabelle machen. Dann sieht das so aus.
1
uint8_t bit_mix[256] = {.....};
2

3
*_ptrList[colInd] =  bit_mix[localDataByte];

Da deine Pinzuordnung konstant ist, kann man die Tabelle auch als 
Konstante in den Flash legen, das spart 256 Bytes RAM. Dann muss man sie 
aber vorher per Script, Excel oder zu Fuß erstellen 8-0. Im RAM kann man 
sie per Funktion erstellen.
Gast #5794715
Lesenswert?

Lothar M. schrieb:
>         CFbelt(randAxis(), ((rand()%20)+25), DIR_ASC);
>         // un beim zweiten Test so
>         CFbelt(randAxis(), ((rand()%20)+25), DIR_DESC);
> ...
>   switch(rand()%3)  // stimmt das wirklich?

Oh je. Also rand()%3 spuckt mir Zahlen von 0 bis 2 aus. Das ist eins 
Zuviel, stimmt. Sollte der Default: zwar auffangen, aber falsch ist es 
allemal. Wird geändert. Und ich Idiot habe DIR_DESC gar nicht einzeln 
getestet. Und da kommen wir zu

Andreas M. schrieb:
> ist so was nicht eine Endlosschleife?
> for(uint8_t i=6; i>=0; i--)

Andreas M. schrieb:
> Es kann nicht stoppen, denn wegen unsigned ist die Bedingung immer true
> ;)

Das sieht doch stark nach dem Übeltäter aus.
Gast #5794719
Lesenswert?

Vlad T. schrieb:
> var_delay_ms sieht für mich äußerst verdächtig aus.
>
> stell das mal um, dass es ein Int als Argument nimmt und mit ints
> rechnet.

Jupp wird gemacht. Danke!



Falk B. schrieb:
> Kleiner Tipp. Die Verdrehung der IO-Bits kann man einfach und
> DEUTLICH
> schneller mit einer Tabelle machen. Dann sieht das so aus.
> uint8_t bit_mix[256] = {.....};
>
> *_ptrList[colInd] =  bit_mix[localDataByte];
>
> Da deine Pinzuordnung konstant ist, kann man die Tabelle auch als
> Konstante in den Flash legen, das spart 256 Bytes RAM. Dann muss man sie
> aber vorher per Script, Excel oder zu Fuß erstellen 8-0. Im RAM kann man
> sie per Funktion erstellen.

Ja stimmt wohl. Danke für den Hinweis!
#5794727
Lesenswert?

Lothar M. schrieb:
> Andreas M. schrieb:
>> ist so was nicht eine Endlosschleife?
>> for(uint8_t i=6; i>=0; i--)
> Mich würde auch die Meinung des Compilers dazu interessieren...

Keine Warnung, kein Fehler.. Ich benutze den AVR/GNU C++ in den 
Standardeinstellungen des Atmel Studio. Außer Unterschiede und Einflüsse 
unterschiedlicher Optimierungsflags hab ich mich damit auch noch nicht 
wirklich beschäftigt.
Moderator (Firma: Titel) Persönliche Seite #5794735
Lesenswert?

Hannes J. schrieb:
> Keine Warnung, kein Fehler..
Ok, die Warnung gibts erst ab -Wtype-limits, und das wäre dann drin z.B. 
in -Wextra (zusammen mit anderen zusätzlichen Warnings für C++):
https://gcc.gnu.org/onlinedocs/gcc-4.6.0/gcc/Warning-Options.html

Deshalb schlägt dieses Problem hier immer wieder auf...   ;-)
Beitrag #5794740 wurde von einem Moderator gelöscht.
Gast #5794748
Lesenswert?

Lothar M. schrieb:
> Hannes J. schrieb:
>> Keine Warnung, kein Fehler..
> Ok, die Warnung gibts erst ab -Wtype-limits, und das wäre dann drin z.B.
> in -Wextra (zusammen mit anderen zusätzlichen Warnings für C++):
> https://gcc.gnu.org/onlinedocs/gcc-4.6.0/gcc/Warning-Options.html
>
> Deshalb schlägt dieses Problem hier immer wieder auf...   ;-)

Dazu kommt, dass AVR Studio die Option nur im C Compiler anbietet, nicht 
im C++. Habe sie jetzt manuell eingefügt und jetzt meckert er auch.

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