Vereinfachung / Codereduzierung

Antworten
Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Vereinfachung / Codereduzierung

Beitrag von Maik81SE »

Moin @ll,

Heute mal eine Frage an jene, welche sich eher in ASM auskennen.

Ich habe folgende Tatsache.
Geschrieben habe ich mir mit Hilfe von einigen Mitgliedern folgende Funktion.

Code: Alles auswählen

procedure RgbShiftOut_L03;
var
  p                          : ^byte;
  byteCount                  : uint16;
  bitCount, RGBdata          : uint8;
begin
  p                          := @Rgb_Line[3];
  asm CLI end;
  for byteCount := 0 to sizeof(Rgb_Line[3]) - 1 do begin
    RGBdata                  := p^;
    inc(p);
    for bitCount := 0 to 7 do
      if RGBdata and $80 > 0 then begin
        asm
        sbi 08, LED_03
        nop
        nop
        nop
        cbi 08, LED_03
        end;
        RGBdata              := RGBdata shl 1;
      end else begin
        asm
        sbi 08, LED_03
        nop
        cbi 08, LED_03
        end;
        RGBdata              := RGBdata shl 1;
      end;
    end;
    asm SEI end;
  end;
läuft auch Astrein, jedoch komme ich hier an einen Punkt, der mein Wissen zugegeben übersteigt.
Wie kann ich in einer Zeitkritischen Situation die Befehle sbi/cbi umgehen, und dennoch og Funktion bis zu 12 mal und ggf mehr zu nutzen, ohne diese entsprechend Kopieren zu müßen.
Unter Normalen Umständen würde ich eine Case verwenden, aber diese würde mein Timing bei 8MHz total in die Tonne drücken.
Da ich diese Funktion an einem ATMega328P betreibe, wo auch noch eine UART (rs232/485 - Protkoll) mitlaufen muß komme ich schon mal sehr schnell an die 4K im Flash und ich brauche noch ca 200-600 Byte für die Auswertung der empfangenen Daten.

Zusätzlich brauch ich aber noch 100 Byte für meine Terminal Debug-Ausgabe, welche ich zur Fehlersuche nutze.

Grüße und angenehmen Montag

Maik

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Vereinfachung / Codereduzierung

Beitrag von kupferstecher »

Maik81SE hat geschrieben:
Mi 9. Mär 2022, 05:56
Wie kann ich in einer Zeitkritischen Situation die Befehle sbi/cbi umgehen, und dennoch og Funktion bis zu 12 mal und ggf mehr zu nutzen, ohne diese entsprechend Kopieren zu müßen.
Wieso 12 Mal? Hast du 12 LED-Kanäle? "SBI" setzt ja immer nur ein Bit. Mit dem Befehl "OUT" kann man ein ganzes Register schreiben, allerdings werden dann auch Bits geschrieben, die man vielleicht nicht ändern wollte. In dem Fall müsste man erst das Register mittels "IN" lesen , die entsprechenden Bit setzen und dann wieder ausgeben.

Am Besten erstmal in Pascal programmieren und nur auf Assembler gehen, wenn es zeitlich wirklich nicht reicht, bzw. an der Stelle, wo man ein genaues Timing braucht.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Vereinfachung / Codereduzierung

Beitrag von Socke »

Der FPC kann doch mittlerweile (im trunk?) auch Konstanten in Generics verwenden. Lässt sich damit dein Problem lösen?
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Vereinfachung / Codereduzierung

Beitrag von Maik81SE »

kupferstecher hat geschrieben:
Mi 9. Mär 2022, 11:32
Maik81SE hat geschrieben:
Mi 9. Mär 2022, 05:56
Wie kann ich in einer Zeitkritischen Situation die Befehle sbi/cbi umgehen, und dennoch og Funktion bis zu 12 mal und ggf mehr zu nutzen, ohne diese entsprechend Kopieren zu müßen.
Wieso 12 Mal? Hast du 12 LED-Kanäle? "SBI" setzt ja immer nur ein Bit. Mit dem Befehl "OUT" kann man ein ganzes Register schreiben, allerdings werden dann auch Bits geschrieben, die man vielleicht nicht ändern wollte. In dem Fall müsste man erst das Register mittels "IN" lesen , die entsprechenden Bit setzen und dann wieder ausgeben.

Am Besten erstmal in Pascal programmieren und nur auf Assembler gehen, wenn es zeitlich wirklich nicht reicht, bzw. an der Stelle, wo man ein genaues Timing braucht.
Diese Funktion/Procedere ist genau auf diesem Weg entstanden...
Erst komblett in Pascal und dann dank Tips aus dem forum in diese Mischform geändert.

Und ja...
Ich habe den ATMega328P mit 12 NeoPixel LED- stipes bestückt, um später wenn das Teilprojekt seine endgültige Form annimmt auf 1000 mm mit ca 12 x 150/200 lines schöne Werke zu zaubern...

Leoder hab ich schon erkennen müssen, das ich mind 20 bis 40 Sektionen brauchen werde.
Somit bekommt das spätere Protokoll und der Rechner welcher die Daten sendet richtig zu tun, weshalb ich den Gedanken mit einem MASTER-MC (rs232/RS485) zu arbeiten wieder über den Haufen werfe und gleich auf einen USB-RS485 umsetzen gehe.
Socke hat geschrieben:
Mi 9. Mär 2022, 14:22
Der FPC kann doch mittlerweile (im trunk?) auch Konstanten in Generics verwenden. Lässt sich damit dein Problem lösen?
Das hab ich noch gar nicht auf dem Schirm.
Ein Versuch ist es wert.
Da richte ich doch gleich mal meinen 2ten Laptop entsprechend ein...

zZ verwende ich FPC 3.2.0? Wenn ich das recht im Filter habe.

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Vereinfachung / Codereduzierung

Beitrag von kupferstecher »

Maik81SE hat geschrieben:
Mi 9. Mär 2022, 17:08
Ich habe den ATMega328P mit 12 NeoPixel LED- stipes bestückt, um später wenn das Teilprojekt seine endgültige Form annimmt auf 1000 mm mit ca 12 x 150/200 lines schöne Werke zu zaubern...

Leoder hab ich schon erkennen müssen, das ich mind 20 bis 40 Sektionen brauchen werde.
Was für Sektionen?
Wenn du frei in der Wahl des Datenformats bist, würde ich es so ändern, dass im Array immer ein Byte für einen Port steht. Die 12 Leitungen hängen hoffentlich an nur 2 Ports, also hast du 2 Byte pro Seriellem Bit. Je Port müssen 8 Byte rausgetaktet werden. Allerdings ist mir dein Timing ehrlich gesagt nicht ganz klar. Kommt es auch auf die Zeit zwischen jedem Schleifendurchlauf an?

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Vereinfachung / Codereduzierung

Beitrag von Maik81SE »

kupferstecher hat geschrieben:
Mi 9. Mär 2022, 18:10
Was für Sektionen?
Je nachdem, wir groß der Code in der Summe wird, muß einige Module (12 x 8 LEDs) in ein Datennetz verpacken.
Ein Modul = eine Sektion im RS485 Netzwerk
kupferstecher hat geschrieben:
Mi 9. Mär 2022, 18:10
Wenn du frei in der Wahl des Datenformats bist, würde ich es so ändern, dass im Array immer ein Byte für einen Port steht. Die 12 Leitungen hängen hoffentlich an nur 2 Ports, also hast du 2 Byte pro Seriellem Bit. Je Port müssen 8 Byte rausgetaktet werden. Allerdings ist mir dein Timing ehrlich gesagt nicht ganz klar. Kommt es auch auf die Zeit zwischen jedem Schleifendurchlauf an?
Das Timing bei der WS2812B ist besonders in der Logischen 1 sehr sehr Kritisch zu beachten.
Im Logischen 0-Byte kann man wohl etwas Strecken, aber auch da würde ich gerne so bleiben, das ich NICHT über 1,2µs komme.

Im Zweifelsfall hilft nur der Sprung von 8 auf 16 MHz.
Da weiß ich, das ich das Timing nochmal komplett anfassen muß.
Auch muß ich dann eine Übertragungsrate suchen welche bei 8 und 16 MHz Astrein läuft.

HIer komme ich Zwangsläufig an einen Punkt, so ich mehrere Faktoren zur gleichen Zeit im Auge haben muß.

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Vereinfachung / Codereduzierung

Beitrag von kupferstecher »

Maik81SE hat geschrieben:
Do 10. Mär 2022, 20:49
WS2812B
Hallo Maik,

ich hab mir jetzt mal das Datenblatt angeschaut. Das Timing ist wohl schon ziemlich kritisch mit einer Periodenzeit von 1,25µs. Bei 8MHz sind das ja nur 10 Takte. Für größere Rechnungen bleibt da keine Zeit. Ich denke die einzige Möglichkeit ist über den "OUT" Befehl einen ganzen Port zu schreiben. Die Daten müssen dann schon im passenden Format vorliegen. Die logischen Nullen und Einsen werden ja wie unten dargestellt übertragen, es gibt also 4 relevante Zeitpunkte. Zeitpunkt A = 0. Dort muss der Ausgang aktiviert werden, egal ob es sich um eine 0 oder 1 handelt. Also muss der Port mit dem Bytewert %11111111 (=$FF) beschrieben werden (alle Kanäle ein). Im Zeitpunkt B = 0,4µs unterscheidet sich dann das 0- und 1-Bit. Dort wird dann das Byte aus deinem Array auf den Port gegeben, Die Bits, die auf 1 gesetzt sind, belassen die Ausgänge auf eins, die die auf 0 gesetzt sind, ziehen nun die Kanäle auf Low. Im Zeitpunkt C= 0,8µs müssen dann alle Kanäle auf Low gezogen werden, also ein Nullbyte ausgegeben werden. Im Zeitpunkt A'=12,5µs fängt der neue Zyklus an. Auch dort muss das Timing stimmen, und zwar über 24 bit, eben für einen Pixel.

Code: Alles auswählen

     A    B  C   A'
      ____
'0'  |    |______|
      ______  
'1'  |      |____|
Zeitpunkte
A= 0
B= 0,4s = 3 Takte @ 8MHz = 6 Takte @16MHz
C= 0,8s = 6 Takte @ 8MHz = 13 Takte @16MHz
A'=1,25s = 10 Takte @8MHz = 20 Takte @16MHz

Also sieht der Asm Code in etwa so aus (ungetestet). Das wäre jetzt nur für einen Port, also max 8 Kanäle. Bei 16MHz sollte sich ein zweiter Port noch jeweils dazwischen einflechten lassen.
Die Äußere Schleife über alle Pixel fehlt auch noch, die ist aber ja weniger zeitkritisch.

Code: Alles auswählen

Procedure PIXELOUT;
label
  PixelLoop;
begin

asm
  ////X-Register mit der Array-Adresse laden
  ldi r26, lo(RGBArray);
  ldi r27, hi(RGBArray);

  ldi r20, 0;  //Register mit Nullbyte initialisieren
  ldi r21,$FF; //Register mit Einsen initialisieren

  ldi r22, 24; //Schleife für 24 Bit initialisieren

  PixelLoop:
    OUT PortA, r21; // 1 Takt  //A 0
    LD r25,X+;      // 2 Takte //  2
    OUT PortA, r25  // 1 Takt  //B 3
    NOP;            // 1 Takt  //  4
    NOP;            // 1 Takt  //  5
    OUT PortA, r20  // 1 Takt  //C 6		    
  dec r22;          // 1 Takt  //  7
  brne PixelLoop;   // 2 Takte //  9
end['r20','r21','r22','r23','r24','r25','r26','r27'];

end;

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Vereinfachung / Codereduzierung

Beitrag von Maik81SE »

kupferstecher hat geschrieben:
Fr 11. Mär 2022, 19:09
Maik81SE hat geschrieben:
Do 10. Mär 2022, 20:49
WS2812B
Hallo Maik,

ich hab mir jetzt mal das Datenblatt angeschaut. Das Timing ist wohl schon ziemlich kritisch mit einer Periodenzeit von 1,25µs. Bei 8MHz sind das ja nur 10 Takte. Für größere Rechnungen bleibt da keine Zeit. Ich denke die einzige Möglichkeit ist über den "OUT" Befehl einen ganzen Port zu schreiben. Die Daten müssen dann schon im passenden Format vorliegen. Die logischen Nullen und Einsen werden ja wie unten dargestellt übertragen, es gibt also 4 relevante Zeitpunkte. Zeitpunkt A = 0. Dort muss der Ausgang aktiviert werden, egal ob es sich um eine 0 oder 1 handelt. Also muss der Port mit dem Bytewert %11111111 (=$FF) beschrieben werden (alle Kanäle ein). Im Zeitpunkt B = 0,4µs unterscheidet sich dann das 0- und 1-Bit. Dort wird dann das Byte aus deinem Array auf den Port gegeben, Die Bits, die auf 1 gesetzt sind, belassen die Ausgänge auf eins, die die auf 0 gesetzt sind, ziehen nun die Kanäle auf Low. Im Zeitpunkt C= 0,8µs müssen dann alle Kanäle auf Low gezogen werden, also ein Nullbyte ausgegeben werden. Im Zeitpunkt A'=12,5µs fängt der neue Zyklus an. Auch dort muss das Timing stimmen, und zwar über 24 bit, eben für einen Pixel.

Code: Alles auswählen

     A    B  C   A'
      ____
'0'  |    |______|
      ______  
'1'  |      |____|
Zeitpunkte
A= 0
B= 0,4s = 3 Takte @ 8MHz = 6 Takte @16MHz
C= 0,8s = 6 Takte @ 8MHz = 13 Takte @16MHz
A'=1,25s = 10 Takte @8MHz = 20 Takte @16MHz

Also sieht der Asm Code in etwa so aus (ungetestet). Das wäre jetzt nur für einen Port, also max 8 Kanäle. Bei 16MHz sollte sich ein zweiter Port noch jeweils dazwischen einflechten lassen.
Die Äußere Schleife über alle Pixel fehlt auch noch, die ist aber ja weniger zeitkritisch.

Code: Alles auswählen

Procedure PIXELOUT;
label
  PixelLoop;
begin

asm
  ////X-Register mit der Array-Adresse laden
  ldi r26, lo(RGBArray);
  ldi r27, hi(RGBArray);

  ldi r20, 0;  //Register mit Nullbyte initialisieren
  ldi r21,$FF; //Register mit Einsen initialisieren

  ldi r22, 24; //Schleife für 24 Bit initialisieren

  PixelLoop:
    OUT PortA, r21; // 1 Takt  //A 0
    LD r25,X+;      // 2 Takte //  2
    OUT PortA, r25  // 1 Takt  //B 3
    NOP;            // 1 Takt  //  4
    NOP;            // 1 Takt  //  5
    OUT PortA, r20  // 1 Takt  //C 6		    
  dec r22;          // 1 Takt  //  7
  brne PixelLoop;   // 2 Takte //  9
end['r20','r21','r22','r23','r24','r25','r26','r27'];

end;
Muß ich im nächsten Step nachmal zu Brust nehmen.
Habe in Verbindung mit der UART nun ein anderes Problem auf den Zettel bekommen.
Erst mal dies von der Kante schieben, bevor ich an die Reduzierung gege.
Da ich zZ mit 12 RGBShiftOut_Lxy Proceduren arbeite, kann ich durch Ausklammern viel speicher gewinnen.

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Vereinfachung / Codereduzierung

Beitrag von Maik81SE »

kupferstecher hat geschrieben:
Fr 11. Mär 2022, 19:09

Code: Alles auswählen

Procedure PIXELOUT;
label
  PixelLoop;
begin

asm
  ////X-Register mit der Array-Adresse laden
  ldi r26, lo(RGBArray);
  ldi r27, hi(RGBArray);

  ldi r20, 0;  //Register mit Nullbyte initialisieren
  ldi r21,$FF; //Register mit Einsen initialisieren

  ldi r22, 24; //Schleife für 24 Bit initialisieren

  PixelLoop:
    OUT PortA, r21; // 1 Takt  //A 0
    LD r25,X+;      // 2 Takte //  2
    OUT PortA, r25  // 1 Takt  //B 3
    NOP;            // 1 Takt  //  4
    NOP;            // 1 Takt  //  5
    OUT PortA, r20  // 1 Takt  //C 6		    
  dec r22;          // 1 Takt  //  7
  brne PixelLoop;   // 2 Takte //  9
end['r20','r21','r22','r23','r24','r25','r26','r27'];

end;
Moin kupferstecher,

bin gerade mal wieder dabei, die Funktion zu überarbeiten...

Komme nur an dem Punkt in's Schleudern, das er iwie mein Array nicht futtern will...

Das von dir benannte RGBArray schaut bei mir wie folgt aus und nennt sich RGB_Line...

Code: Alles auswählen

type
  TRGB                       = record
    Color                    : array[0..2] of uint8;
  end;

const
  Max_Line                   = 6;								// Anzahl der Lines pro Port (Umschaltung via D-Latch) auf 12 Lines
  LED_Count                  = 8;

var
  Rgb_Line                   : array [1..Max_Line, 0..LED_Count] of TRGB;
die Pixelerweiterung entsprechend Analog wie folgt.

Code: Alles auswählen

procedure PixelOut_P_C;
label
  PixelLoop;

var
  p                          : ^byte;
  Row                        : array [1..6] of byte;
  Pixel, RGBLine             : uint8;
begin
  for Pixel := 0 to LED_Count do begin     // Pixel innerhalb der Line
    for RGBLine := 1 to 6 do begin         // NeoPixel_Line
      p                      := @RGB_Line[RGBLine, Pixel];
      Row[RGBLine]           := p^;
      end;
    asm
     // X-Register mit der Array Adresse laden
    ldi r26, lo(Row)
    ldi r27, hi(Row)

    ldi r20,   0           // Register mit Nullbyte initialisieren
    ldi r21,  63           // Register mit EINSEN initialisieren

    ldi r22,  24           // Schleife für 24 Bit initialisieren

    PixelLoop:
      OUT PORTC, r21       // 1 Takt  --> A 0
      LD  r25,   X+        // 2 Takte -->   2
      OUT PORTC, r25       // 1 Takt  --> B 3
      NOP                  // 1 Takt  -->   4
      NOP                  // 1 Takt  -->   5
      OUT PORTC, r20       // 1 Takt  --> C 6
    dec r22                // 1 Takt  -->   7
    brne PixelLoop;
    end['r20','r21','r22','r23','r24','r25','r26','r27'];
  end;
end;
Als Beispiel hab ich mal ein TextArray aus meinem Main-Programm kopiert, welches ich noch gefunden habe,

Code: Alles auswählen

{ Beispiel für ein Array welches auf 6 x 6 Pixel definiert sein kann.
  Alle Daten gem: des Types "TRGB"!!! wobei die Werte der reihen nach für Grün, Blau, Rot
  stehen...
  1: [  4, 0, 0]; [0,   4, 0]; [0, 0,   4]; [  4,   4, 0]; [  4, 0,   4]; [  4,   4,   4];
  2: [  8, 0, 0]; [0,   8, 0]; [0, 0,   8]; [  8,   8, 0]; [  8, 0,   8]; [  8,   8,   8];
  3: [ 16, 0, 0]; [0,  16, 0]; [0, 0,  16]; [ 16,  16, 0]; [ 16, 0,  16]; [ 16,  16,  16];
  4: [ 32, 0, 0]; [0,  32, 0]; [0, 0,  32]; [ 32,  32, 0]; [ 32, 0,  32]; [ 32,  32,  32];
  5: [ 64, 0, 0]; [0,  64, 0]; [0, 0,  64]; [ 64,  64, 0]; [ 64, 0,  64]; [ 64,  64,  64];
  6: [128, 0, 0]; [0, 128, 0]; [0, 0, 128]; [128, 128, 0]; [128, 0, 128]; [128, 128, 128];
}

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

Antworten