Bits werden falsch gesetzt.

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:

Bits werden falsch gesetzt.

Beitrag von Maik81SE »

Moin Ihr lieben.

nachdem ich den Blindfisch mit meinem Array hinbekommen habe, und die Daten auf meinem ATTiny85 PB3 ausgeben will, komme ich leider nicht drumrum, festzustellen, das das Array falsch übermittelt wird. :?

Vorweg.
habe meine Funktion so geschrieben, das ich diese auf einem ATMega mit 16 MHz testen und alle Fehler suchen kann, sowohl auch diese auf meinem kleinen Tiny laufen lasse.

Anzeige meiner UART

Code: Alles auswählen

/1\/1\/1\/1\/1\/1\/1\/0\/0\/1\/0\/0\/0\/0\/0\/0\/1\/0\/1\/0\/0\/0\/0\/0\111111100100000010100000#|-
die Funktion dazu schaut wie folgt aus...

Code: Alles auswählen

procedure Set_LED(g, r, b: byte);
var a, Rest                  : byte;
begin
  {$if defined (ATMega16)}UARTSendChar('-');{$endif}
  for a := 0 to 7 do begin
    Rest                     := g mod 2;
    {$if defined (ATTiny85)}
    PORTB                    := PORTB or (1 shl LED);
    {$endif}{$if defined (ATMega16)}
    PORTC                    := PORTC or (1 shl LED);
    UARTSendChar('/');
    {$endif}
    if Rest = 0 then begin
       Data[a] := '0';
       T0H;
       {$if defined (ATTiny85)}
       PORTB            := PORTB and not (1 shl LED);
       {$endif}{$if defined (ATMega16)}
       UARTSendChar('0');
       PORTC            := PORTC and not (1 shl LED);
       UARTSendChar('\');
       {$endif}
       T0L;
       end
    else begin
         Data[a]             := '1';
         T1H;
         {$if defined (ATTiny85)}
         PORTB            := PORTB and not (1 shl LED);
         {$endif}{$if defined (ATMega16)}
         UARTSendChar('1');
         PORTC            := PORTC and not (1 shl LED);
         UARTSendChar('\');
         {$endif}
         T1L;
         end;
      {$if defined (ATMega16)}UARTSendChar(Data[a]);{$endif}
    g                        := g div 2;
    end;

    {$if defined (ATMega16)}UARTSendChar('-');{$endif}
  for a := 8 to 15 do begin
    Rest                     := r mod 2;
    {$if defined (ATTiny85)}
    PORTB                    := PORTB or (1 shl LED);
    {$endif}{$if defined (ATMega16)}
    PORTC                    := PORTC or (1 shl LED);
    UARTSendChar('/');
    {$endif}
    if Rest = 0 then begin
       Data[a] := '0';
       T0H;
       {$if defined (ATTiny85)}
       PORTB            := PORTB and not (1 shl LED);
       {$endif}{$if defined (ATMega16)}
       UARTSendChar('0');
       PORTC            := PORTC and not (1 shl LED);
       UARTSendChar('\');
       {$endif}
       T0L;
       end
    else begin
         Data[a]             := '1';
         T1H;
         {$if defined (ATTiny85)}
         PORTB            := PORTB and not (1 shl LED);
         {$endif}{$if defined (ATMega16)}
         UARTSendChar('1');
         PORTC            := PORTC and not (1 shl LED);
         UARTSendChar('\');
         {$endif}
         T1L;
         end;
    {$if defined (ATMega16)}UARTSendChar(Data[a]);{$endif}
    r                        := r div 2;
    end;

  {$if defined (ATMega16)}UARTSendChar('-');{$endif}
  for a := 16 to 23 do begin
    Rest                     := b mod 2;
    {$if defined (ATTiny85)}
    PORTB                    := PORTB or (1 shl LED);
    {$endif}{$if defined (ATMega16)}
    PORTC                    := PORTC or (1 shl LED);
    UARTSendChar('/');
    {$endif}
    if Rest = 0 then begin
       Data[a] := '0';
       T0H;
       {$if defined (ATTiny85)}
       PORTB            := PORTB and not (1 shl LED);
       {$endif}{$if defined (ATMega16)}
       UARTSendChar('0');
       PORTC            := PORTC and not (1 shl LED);
       UARTSendChar('\');
       {$endif}
       T0L;
       end
    else begin
         Data[a]             := '1';
         T1H;
         {$if defined (ATTiny85)}
         PORTB            := PORTB and not (1 shl LED);
         {$endif}{$if defined (ATMega16)}
         UARTSendChar('1');
         PORTC            := PORTC and not (1 shl LED);
         UARTSendChar('\');
         {$endif}
         T1L;
         end;
    {$if defined (ATMega16)}UARTSendChar(Data[a]);{$endif}
    b                        := b div 2
    end;
    {$if defined (ATMega16)}for a:= 0 to 23 do UARTSendChar(Data[a]);{$endif}
end;
gecleant soll es am ende so aussehen.

Code: Alles auswählen

procedure Set_LED(g, r, b: byte);
var a, Rest                  : byte;
begin
  for a := 0 to 23 do Data[a]:= '0';
  for a := 0 to 7 do begin
    Rest                     := g mod 2;
    if Rest = 0 then Data[a] := '0'
    else Data[a]             := '1';
    g                        := g div 2;
    end;

  for a := 8 to 15 do begin
    Rest                     := r mod 2;
    if Rest = 0 then Data[a] := '0'
    else Data[a]             := '1';
    r                        := r div 2;
    end;

  for a := 16 to 23 do begin
    Rest                     := r mod 2;
    if Rest = 0 then Data[a] := '0'
    else Data[a]             := '1';
    r                        := r div 2;
    end;

  for a := 23 downto 0 do begin
    {$if defined (ATTiny85)}
    PORTB                    := PORTB
    {$endif}{$if defined (ATMega16)}
    PORTC                    := PORTC
    {$endif}                 or (1 shl LED);
    if Data[a] = '0' then begin
       T0H;
       {$if defined (ATTiny85)}
       PORTB                 := PORTB
       {$endif}{$if defined (ATMega16)}
       PORTC                 := PORTC
       {$endif}              and not (1 shl LED);
       T0L;
       end
    else begin
         T1H;
         {$if defined (ATTiny85)}
         PORTB               := PORTB
         {$endif}{$if defined (ATMega16)}
         PORTC               := PORTC
         {$endif}             and not (1 shl LED);
         T1L;
         end;
  end;

end;  
Data, Txy sind wie folgt deklariert:

Code: Alles auswählen

var Data                     : array [0..23] of char;
meine 4 Txy

Code: Alles auswählen

procedure T0H; //0,35µs +-150ns
label
  loop;
begin
  asm
  {$if defined (ATTiny85)}ldi r16, 21{$endif}
  {$if defined (ATMega16)}ldi r16, 42{$endif}
    loop:
      dec r16
      brne loop
    end['r16'];
  end;

procedure T0L; //0,9µS +-150ns
label
  loop;
begin
  asm
  {$if defined (ATTiny85)}ldi r16, 54{$endif}
  {$if defined (ATMega16)}ldi r16, 108{$endif}
    loop:
      dec r16
      brne loop
    end['r16'];
  end;

procedure T1H; //0,9µS +-150ns
label
  loop;
begin
  asm
  {$if defined (ATTiny85)}ldi r16, 54{$endif}
  {$if defined (ATMega16)}ldi r16, 108{$endif}
    loop:
      dec r16
      brne loop
    end['r16'];
  end;

procedure T1L; //0,35µs +-150ns
label
  loop;
begin
  asm
  {$if defined (ATTiny85)}ldi r16, 21{$endif}
  {$if defined (ATMega16)}ldi r16, 42{$endif}
    loop:
      dec r16
      brne loop
    end['r16'];
  end;
Wie ihr dem sicherlich entnehmen könnt, sollen WS2812 angesteuert werden und da die Adurino-Version bei mir nicht läuft, muß ich bei meinen MCUs die Funktionen selber aufbauen.

Sicher werdet Ihr fragen, warum ich die UART nicht auch auf dem Tiny laufen lassen.
Da geb ich gerne und offen zu, das ich diese da nicht nicht auf die Kette bekommen habe.
Will es wie bei dem ATMega angehen.
Erst die Basics dann erweitern.

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)

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Bits werden falsch gesetzt.

Beitrag von siro »

Hallo Maik,

da ich schon sehr viele verschiedene RGB Leds und Stripes mit PIC Controllern angesteuert habe, möchte ich hier einige Anregungen geben:

Ich habe mal meinen "leider in C" geschriebenen Code in Pascal umgesetzt und
die erforderlichen Assemblerteile ausgeklammert, jedoch schon versucht die nötigen AVR Befehle einzusetzen.

Vielleicht kann der ein oder andere auch etwas aus den Informationen nutzen.

Code: Alles auswählen

CONST LED_COUNT = 16;    // Anzahl der LEDs in der Kette

Type TRGB=record
   green,red,blue:Byte;  // Jede Farbe hat 1 Byte, also Helligkeitswerte von 0 bis 255
                         // Entscheidend ist hier die Reihenfolge der Bytes wegen dem Ausschieben
                         // Die meisten Stripes haben diese Anordnung, ich hatte aber auch schon
                         // welche mit vertauschten Datenbytes, dann werden die hier gedreht
end;

// Hier werden die Daten der gesamten RGB Kette aufbewahrt
var RgbLine:Array[0..LED_COUNT-1] of TRGB;

// hier kann man die gesamte Kette mit einer spezifischen Farbe füllen oder auch komplett löschen
procedure RgbFill(r,g,b:Byte);
var i:integer;
begin
  for i:=0 to LED_COUNT-1 do begin
    RGBline[i].green:=g;
    RGBline[i].red  :=r;
    RGBline[i].blue :=b;
  end;
end;

// Hier ist der eigentliche Knackpunkt für die ordnungsgemässe Funktion:
// Das Ausschieben selbst stellt kein Probelm dar, aber das Timing
// und das muss dem Controller bzw. der Taktfrequenz entsprechend angepasst werden
// Mit dieser Vorgehensweise konnte ich noch mit 4 MHz = 250ns Instruction Cycle
// mit einem PIC-Controller PIC12F1840 eine RGB Leiste problemlos ansteuern

procedure RgbShiftOut;
var p:^Byte;                 // Ein Zeiger auf das auszuschiebene Byte
    byteCount : Byte;        // Zähler für die LEDs 0..n
    bitCount  : Byte;        // Bitzähler 0..7
    dataByte  : Byte;        // das aktuell auszuschiebene Byte
begin
  p:=@RgbLine;      // den Zeiger wird auf das erste Byte des LedArray gesetzt
  // GANZ Wichitg: die Interrupts sperren, sonst bekommt man evtl. Flackersalat :-)
{  asm CLI  }                                // Global Interrupt Disable
  for byteCount:=0 toSizeOf(RgbLine)-1 do begin   // Alle Array Bytes durchlaufen
{    asm WDR  }                              // evtl. den Watchdog bedienen, falls dieser aktiviert ist
    dataByte:=p^;                            // das Datenbyte laden worauf der Zeiger grade zeigt
    inc(p);                                  // dann den Datenzeiger aufs nächste Byte setzen
    for bitCount:=0 to 7 do begin            // 8 Bits des aktuellen Datenbytes ausgeben, angefangen mit dem MSB höchsten Bit
      if dataByte AND $80 > 0 then begin     // ein High Bit ausschieben
{         asm SBI port,bit }   // Portbit auf 1 setzen,  lange High Phase einleiten
{         asm NOP          }   // kurz warten, z.B. mit NOP füllen oder loop
{         asm NOP          }
{         asm NOP          }
{         asm CBI port,bit }   // Portbit wieder auf Low setzen
      end else Begin         // ein Low Bit ausschieben
{        asm SBI port,bit  }   // Portbit auf 1 setzen,  kurze High Phase einleiten
{        asm NOP           }   // kurze warten
{        asm CBI port,bit  }   // Portbit wieder auf Low setzen
      end;
    end;
    dataByte := dataByte SHL 1;  // das Datenbyte einmal nach links schieben
    // Die Low Phasen ergeben sich bei meinem Controller automatisch durch den Schleifencode und sind unkritisch
    // hier müssen jedoch evtl. einige NOPs / Loop rein ???
  end;

  // wenn alles ausgeschoben wurde die Interrupts wieder freigeben
{  asm SEI  }      // Global Interrupt Enable Interrupts wieder freigeben

  // hier benötigt man noch ein Delay von 50us bei WS2812B bzw. 80 Mikrosekunden bei den SK68 Leds
  // Diese lange Pause löst das Übertragen der Daten ALLER LEDS gleichzeitig aus.
  // normalerweise ist die Pasue aber nicht nötig, da man ja noch etwas anderes macht nach dem Ausschieben.

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RgbFill($00,$00,$00);  // löscht die komplette RGB Kette

  // einige Beispiele: ganze Kette mit einer Fabe füllen
  RgbFill($FF,$00,$00);  // Füllt die RGB Kette komplett mit rot
  RgbFill($00,$FF,$00);  // Füllt die RGB Kette komplett mit grün
  RgbFill($00,$00,$FF);  // Füllt die RGB Kette komplett mit blau
  RgbFill($FF,$00,$FF);  // Füllt die RGB Kette komplett mit violett

  // Led Nummer 5 in der Kette soll gelb sein, aber nur halbe Helligkeit
  RgbLine[5].red  :=127;
  RgbLine[5].green:=127;
  RgbLine[5].blue :=0;

  // Led Nummer 13 in der Kette soll blau sein, volle Helligkeit
  RgbLine[5].red  :=0;
  RgbLine[5].green:=0;
  RgbLine[5].blue :=255;


  // wenn man alle seine Daten für die LEDs gesetzt hat, wird ausgeschoben
  // und das ist wirklich sehr zeitkritisch, jedoch nur für die High Phasen
  // Die Low Phasen dürfen generell wesentlich länger sein als im Datenblatt angegeben,
  // hier habe ich auch schon 7 Mikrosekunden probiert und das ging, ab 7,5us gabs Probleme

  RgbShiftOut;
end;
Zuletzt geändert von siro am Mo 3. Jan 2022, 23:49, insgesamt 1-mal geändert.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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: Bits werden falsch gesetzt.

Beitrag von Maik81SE »

siro hat geschrieben:
Mi 29. Dez 2021, 20:22

Code: Alles auswählen

CONST LED_COUNT = 16;    // Anzahl der LEDs in der Kette

Type TRGB=record
   green,red,blue:Byte;  // Jede Farbe hat 1 Byte, also Helligkeitswerte von 0 bis 255
                         // Entscheidend ist hier die Reihenfolge der Bytes wegen dem Ausschieben
                         // Die meisten Stripes haben diese Anordnung, ich hatte aber auch schon
                         // welche mit vertauschten Datenbytes, dann werden die hier gedreht
end;

// Hier werden die Daten der gesamten RGB Kette aufbewahrt
var RgbLine:Array[0..LED_COUNT-1] of TRGB;

// hier kann man die gesamte Kette mit einer spezifischen Farbe füllen oder auch komplett löschen
procedure RgbFill(r,g,b:Byte);
var i:integer;
begin
  for i:=0 to LED_COUNT-1 do begin
    RGBline[i].green:=g;
    RGBline[i].red  :=r;
    RGBline[i].blue :=b;
  end;
end;

// Hier ist der eigentliche Knackpunkt für die ordnungsgemässe Funktion:
// Das Ausschieben selbst stellt kein Probelm dar, aber das Timing
// und das muss dem Controller bzw. der Taktfrequenz entsprechend angepasst werden
// Mit dieser Vorgehensweise konnte ich noch mit 4 MHz = 250ns Instruction Cycle
// mit einem PIC-Controller PIC12F1840 eine RGB Leiste problemlos ansteuern

procedure RgbShiftOut;
var p:^Byte;                 // Ein Zeiger auf das auszuschiebene Byte
    byteCount : Byte;        // Zähler für die LEDs 0..n
    bitCount  : Byte;        // Bitzähler 0..7
    dataByte  : Byte;        // das aktuell auszuschiebene Byte
begin
  p:=@RgbLine;      // den Zeiger wird auf das erste Byte des LedArray gesetzt
  // GANZ Wichitg: die Interrupts sperren, sonst bekommt man evtl. Flackersalat :-)
{  asm CLI  }                                // Global Interrupt Disable
  for byteCount:=0 to LED_COUNT-1 do begin   // Alle RGB Leds durchlaufen
{    asm WDR  }                              // evtl. den Watchdog bedienen, falls dieser aktiviert ist
    dataByte:=p^;                            // das Datenbyte laden worauf der Zeiger grade zeigt
    inc(p);                                  // dann den Datenzeiger aufs nächste Byte setzen
    for bitCount:=0 to 7 do begin            // 8 Bits des aktuellen Datenbytes ausgeben, angefangen mit dem MSB höchsten Bit
      if dataByte AND $80 > 0 then begin     // ein High Bit ausschieben
{         asm SBI port,bit }   // Portbit auf 1 setzen,  lange High Phase einleiten
{         asm NOP          }   // kurz warten, z.B. mit NOP füllen oder loop
{         asm NOP          }
{         asm NOP          }
{         asm CBI port,bit }   // Portbit wieder auf Low setzen
      end else Begin         // ein Low Bit ausschieben
{        asm SBI port,bit  }   // Portbit auf 1 setzen,  kurze High Phase einleiten
{        asm NOP           }   // kurze warten
{        asm CBI port,bit  }   // Portbit wieder auf Low setzen
      end;
    end;
    dataByte := dataByte SHL 1;  // das Datenbyte einmal nach links schieben
    // Die Low Phasen ergeben sich bei meinem Controller automatisch durch den Schleifencode und sind unkritisch
    // hier müssen jedoch evtl. einige NOPs / Loop rein ???
  end;

  // wenn alles ausgeschoben wurde die Interrupts wieder freigeben
{  asm SEI  }      // Global Interrupt Enable Interrupts wieder freigeben

  // hier benötigt man noch ein Delay von 50us bei WS2812B bzw. 80 Mikrosekunden bei den SK68 Leds
  // Diese lange Pause löst das Übertragen der Daten ALLER LEDS gleichzeitig aus.
  // normalerweise ist die Pasue aber nicht nötig, da man ja noch etwas anderes macht nach dem Ausschieben.

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RgbFill($00,$00,$00);  // löscht die komplette RGB Kette

  // einige Beispiele: ganze Kette mit einer Fabe füllen
  RgbFill($FF,$00,$00);  // Füllt die RGB Kette komplett mit rot
  RgbFill($00,$FF,$00);  // Füllt die RGB Kette komplett mit grün
  RgbFill($00,$00,$FF);  // Füllt die RGB Kette komplett mit blau
  RgbFill($FF,$00,$FF);  // Füllt die RGB Kette komplett mit violett

  // Led Nummer 5 in der Kette soll gelb sein, aber nur halbe Helligkeit
  RgbLine[5].red  :=127;
  RgbLine[5].green:=127;
  RgbLine[5].blue :=0;

  // Led Nummer 13 in der Kette soll blau sein, volle Helligkeit
  RgbLine[5].red  :=0;
  RgbLine[5].green:=0;
  RgbLine[5].blue :=255;


  // wenn man alle seine Daten für die LEDs gesetzt hat, wird ausgeschoben
  // und das ist wirklich sehr zeitkritisch, jedoch nur für die High Phasen
  // Die Low Phasen dürfen generell wesentlich länger sein als im Datenblatt angegeben,
  // hier habe ich auch schon 7 Mikrosekunden probiert und das ging, ab 7,5us gabs Probleme

  RgbShiftOut;
end;
Moin Siro,

das ist schon mal ein ansatz, der sich vielversprechend liest, und ja ich war gerade dabei, dies alles mit SIMAvr selber zu versuchen.
Deine Variante schaut mir im ersten Moment danach aus, das diese eher für einen PI geschrieben wurde, aber kann mich auch sicher irren.

Danke erst mal und schönen Abend dir noch wünsche.

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