Wie funktioniert ein Adress-LED-Streifen?


Wahrscheinlich erscheint diese Frage "Wie funktioniert es?" Vielen albern. Die Antwort liegt auf der Hand: Ein adressierbarer LED-Streifen besteht aus mehreren in Reihe geschalteten „Smart LEDs“. Dies kann einfach durch Betrachten des Bandgeräts gesehen werden. Sie können einzelne Mikroschaltungen sehen, die an ein flexibles Kabel gelötet sind, Sie können die Verbindungen sehen: Mikroschaltungen sind mit nur drei Drähten in Reihe geschaltet, und zwei davon sind Strom und Masse. Nur ein Draht überträgt Pixelfarbdaten. Wie ist das? Was ist eine intelligente LED?

Als nächstes werde ich über das Datenübertragungsprotokoll sprechen, das in dem auf WS2812B basierenden LED-Streifen verwendet wird, und außerdem werde ich fast meinen eigenen „LED-Streifen-Chip“ im FPGA-Chip erstellen.

Das Band verwendet also eine serielle Übertragung über ein einzelnes Datensignal.

Bit Null wird als kurzer positiver Impuls und als Pause übertragen, die ungefähr zweimal breiter als der Impuls ist. Die Biteinheit wird als breiter positiver Impuls und kurze Pause übertragen:



Wenn keine Übertragung von mehr als 50 Mikrosekunden erfolgt, kehrt das Band in seinen ursprünglichen Zustand zurück und ist bereit, Pixel ab dem ersten zu akzeptieren.

Alle 24 Bits in einer Sequenz sind 3 Bytes für drei RGB-Farben. Und tatsächlich wird die Sequenz GRB sein. Das hohe Bit von G7 steht an erster Stelle.

Die Sequenz der ersten 24 Bits ist ein Pixel, das die allererste LED im Band empfängt. Bis die erste LED gesättigt ist, werden keine Daten weiter zur nächsten LED übertragen. Nachdem die erste LED ihren Teil von 24x RGB-Bits empfangen hat, öffnet sie die Übertragung zur nächsten. Primitiv kann man sich eine Folge von LEDs als eine Kaskade von Krügen vorstellen, die nacheinander mit Wasser gefüllt werden: Die



erste, dann die zweite, dann die dritte und so weiter füllen sich.

Daher glaube ich, dass das Übertragungsprotokoll aussortiert wurde.

Ist es möglich, eine solche „intelligente LED“ selbst zu entwerfen? Das macht natürlich wenig Sinn, aber für die Selbstbildung und die Erweiterung des eigenen Horizonts ist dies eine interessante Aufgabe. Versuchen wir, die Logik des Chips in der Programmiersprache Verilog HDL zu beschreiben. Natürlich wird dies kein echtes Chip-Design sein, es wird Einschränkungen geben. Eine der wichtigsten Einschränkungen - Ich benötige eine externe Uhr für meine Mikroschaltung. In einer echten intelligenten LED existiert ein solcher Generator ebenfalls, der jedoch bereits in den Chip integriert ist.

Starten wir das Verilog-Modul folgendermaßen:

module WS2812B(
	input wire clk,
	input wire in,
	output wire out,
	output reg r,
	output reg g,
	output reg b
);

Ich denke, hier ist alles klar: die Taktfrequenz von clk, die Eingangs- und Ausgangssignale der „Smart LED“ ein und aus und natürlich die Ausgangssignale r, g, b, über die ich die realen externen LEDs in rot, grün und blau steuern werde.

Ich werde das Eingangssignal in einem Zwei-Bit-Schieberegister erfassen und aus dem aktuellen Zustand in diesen erfassten Bits kann ich den Beginn einer positiven Flanke des Signals bestimmen in:

reg [1:0]r_in = 0;
always @( posedge clk )
	r_in <= { r_in[0],in };

wire in_pos_edge; assign in_pos_edge = (r_in==2'b01);

Darüber hinaus ist es wichtig, den Status des Bandresets zu ermitteln, wenn der Steuercontroller vor dem Starten einer neuen Übertragung angehalten wird:

localparam reset_level = 3000;
reg [15:0]reset_counter = 0;
always @( posedge clk )
	if( r_in[0] )
		reset_counter <= 0;
	else
	if( reset_counter<reset_level )
		reset_counter <= reset_counter+1;

wire reset; assign reset = (reset_counter==reset_level);

Ab einer positiven Flanke in_pos_edge ist es außerdem erforderlich, eine Pause zu überstehen, um den Moment des Fixierens eines neuen Bits zu erhalten:

localparam fix_level   = 50;
reg [7:0]bit_length_cnt;
always @( posedge clk )
	if( in_pos_edge )
		bit_length_cnt <= 0;
	else
	if( bit_length_cnt<(fix_level+1) && !pass )
		bit_length_cnt <= bit_length_cnt + 1;

wire bit_fix; assign bit_fix = (bit_length_cnt==fix_level);

Die Anzahl der bereits empfangenen Bits im Chip wird wie folgt betrachtet:

reg pass = 0;
reg [5:0]bits_captured = 0;

always @( posedge clk )
	if( reset )
		bits_captured <= 1'b0;
	else
	if( ~pass && bit_fix )
		bits_captured <= bits_captured+1'b1;

Hier wird ein wichtiges Durchgangssignal eingeführt, das lediglich die Umleitung des Eingangsstroms zum Ausgang bestimmt. Nach dem Akzeptieren des 24x-Pixelbits wird das Durchlasssignal auf eins gesetzt:

always @( posedge clk )
	if( reset )
		pass <= 1'b0;
	else
	if( bits_captured==23 && bit_fix )
		pass <= 1'b1;
		
reg pass_final;
always @( posedge clk )
	if( reset )
		pass_final <= 1'b0;
	else
	if( r_in!=2'b11 )
		pass_final <= pass;
		
assign out = pass_final ? in : 1'b0;

Der Eingangsausgang wird mit dem Ausgangsausgang gemultiplext, wenn das Signal pass_final eins ist.

Nun, natürlich brauchen wir ein Schieberegister, in dem die empfangenen 24 Bits des Pixels akkumuliert werden:

reg [23:0]shift_rgb;
always @( posedge clk )
	if( bit_fix )
		shift_rgb <= { in, shift_rgb[23:1] };

reg [23:0]fix_rgb;
always @( posedge clk )
	if( bits_captured==23 && bit_fix )
		fix_rgb <= { in, shift_rgb[23:1] };

Nach dem Empfang aller 24 Bits werden diese ebenfalls in das endgültige 24-Bit-Register kopiert.

Jetzt bleibt die Sache klein. Es ist erforderlich, ein PWM-Signal (Latitudinal Pulse Modulation) zu implementieren, um die Helligkeit gemäß den empfangenen RGB-Bytes an echte externe LEDs zu übertragen:

wire [7:0]wgreen; assign wgreen = { fix_rgb[0 ], fix_rgb[1 ], fix_rgb[2 ], fix_rgb[3 ], fix_rgb[4 ], fix_rgb[5 ], fix_rgb[6 ], fix_rgb[7 ] };
wire [7:0]wred;   assign wred   = { fix_rgb[8 ], fix_rgb[9 ], fix_rgb[10], fix_rgb[11], fix_rgb[12], fix_rgb[13], fix_rgb[14], fix_rgb[15] };
wire [7:0]wblue;  assign wblue  = { fix_rgb[16], fix_rgb[17], fix_rgb[18], fix_rgb[19], fix_rgb[20], fix_rgb[21], fix_rgb[22], fix_rgb[23] };

reg [7:0]pwm_cnt;

always @( posedge clk )
begin
	pwm_cnt <= pwm_cnt+1;
	r <= pwm_cnt<wred;
	g <= pwm_cnt<wgreen;
	b <= pwm_cnt<wblue;
end

Das scheint alles zu sein.

Ein kleines Detail bleibt - wie kann man das alles erleben?

Ich habe ein paar einfache Motherboards mit dem FPGA MAX II (dies sind die Karten der Mars Rover-Serie) genommen und sie alle mit einem Projekt mit diesem Verilog-Code aktualisiert. Es gab bereits 8 LEDs auf den Platinen, aber sie waren alle gelb. Auf jeder der Karten habe ich 3 LEDs durch R, G, B ersetzt. Ich habe die Karten in Reihe geschaltet und sie außerdem mit einem echten LED-Streifen verbunden. So habe ich das echte Band mit meinen selbstgemachten LEDs verlängert.

Diese Verbindung stellte sich folgendermaßen heraus:



In Wirklichkeit sieht es so aus:



Wenn ich nun ein bestimmtes Bild auf das Band anwende, sehe ich, dass sich meine „intelligenten LEDs“ genauso verhalten wie die echten vom Band:


Es stellt sich heraus, dass die Logik, die ich im FPGA implementiert habe, voll funktionsfähig ist! In erster Näherung konnte ich etwas Ähnliches wie einen echten intelligenten LED-Chip tun.

Eigentlich mag ich LED-Streifen. Jeder kann auf seiner Basis etwas Eigenes erfinden: intelligente Beleuchtung, Bildschirme, Ambilight-Effekte. Einmal habe ich sogar Farbmusik auf einem LED-Band mit FPGA implementiert. Aber das ist eine andere Geschichte .

All Articles