Comment fonctionne une bande d'adresse LED?


Cette question "comment ça marche" semble probablement idiote à beaucoup. La réponse est presque évidente: une bande de LED adressable se compose d'une pluralité de «LED intelligentes» connectées en série. Cela peut être vu simplement en regardant le lecteur de bande. Vous pouvez voir des microcircuits individuels soudés à un câble flexible, vous pouvez voir les connexions: les microcircuits sont connectés en série avec seulement trois fils, et deux d'entre eux sont l'alimentation et la terre. Un seul fil transmet les données de couleur des pixels. Comment c'est? Qu'est-ce qu'une LED intelligente?

Ensuite, je vais parler du protocole de transfert de données utilisé dans la bande LED basée sur WS2812B, et, de plus, je vais presque créer ma propre «puce bande LED» dans la puce FPGA.

Ainsi, la bande utilise la transmission série via un seul signal de données.

Le bit zéro est transmis sous la forme d'une impulsion positive courte et d'une pause qui est environ deux fois plus large que l'impulsion. L'unité de bit est transmise sous forme d'une impulsion positive large et d'une courte pause:



s'il n'y a pas de transmission de plus de 50 microsecondes, la bande revient à son état d'origine, prête à accepter les pixels à partir de la première.

Tous les 24 bits d'une séquence correspondent à 3 octets pour trois couleurs RVB. Et en fait, la séquence sera GRB. Le bit élevé de G7 vient en premier.

La séquence des 24 premiers bits est d'un pixel, qui recevra la toute première LED de la bande. Tant que la première LED n'est pas saturée, elle ne transfère plus les données à la LED suivante. Une fois que la première LED a reçu sa portion de 24 bits RVB, elle ouvre la transmission à la suivante. Primitivement, on peut imaginer une séquence de LED comme une cascade de cruches remplies successivement d'eau: la



première, puis la seconde, puis la troisième, etc., se remplissent.

Je pense donc que le protocole de transfert a été réglé.

Est-il possible d'essayer de concevoir vous-même une telle «LED intelligente»? Bien sûr, cela n'a pas beaucoup de sens pratique, mais pour l'auto-éducation et l'élargissement de ses horizons, c'est une tâche intéressante. Essayons de décrire la logique de la puce dans le langage de programmation Verilog HDL. Bien sûr, ce ne sera pas une vraie conception de puce, il y aura des limitations. L'une des limitations les plus importantes - j'aurai besoin d'une horloge externe pour mon microcircuit. Dans une vraie LED intelligente, un tel générateur existe également, mais il est déjà intégré dans la puce.

Commençons le module Verilog comme ceci:

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

Je pense que tout est clair ici: la fréquence d'horloge de clk, les signaux d'entrée et de sortie de la "LED intelligente" dans et hors, et, bien sûr, les signaux de sortie r, g, b à travers lesquels je contrôlerai les vraies LED externes en rouge, vert et bleu.

Je vais capturer le signal d'entrée dans un registre à décalage à deux bits et à partir de l'état actuel dans ces bits capturés, je peux déterminer le début du front positif du signal dans:

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);

De plus, il est important de déterminer l'état de la réinitialisation de la bande lorsque le contrôleur de contrôle s'arrête avant de démarrer une nouvelle transmission:

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);

De plus, à partir d'un bord positif in_pos_edge, il est nécessaire de résister à une pause afin d'obtenir le moment de fixer un nouveau bit:

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);

Le nombre de bits déjà reçus dans la puce est considéré comme suit:

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;

Un signal de passage important est introduit ici, qui détermine simplement la redirection du flux d'entrée vers la sortie. Après avoir accepté le bit 24x pixels, le signal de passage est défini sur un:

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;

L'entrée de sortie est multiplexée sur la sortie de sortie lorsque le signal pass_final en est un.

Eh bien, bien sûr, nous avons besoin d'un registre à décalage, où les 24 bits reçus du pixel sont accumulés:

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] };

À la réception de tous les 24 bits, ils sont également copiés dans le registre 24 bits final.

Maintenant, la question reste petite. Il est nécessaire de mettre en œuvre un signal PWM (Latitudinal Pulse Modulation) pour transmettre la luminosité à de vraies LED externes en fonction des octets RVB reçus:

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

Cela semble être tout.

Un petit détail demeure - comment vivre tout cela?

J'ai pris quelques cartes mères simples avec le FPGA MAX II (ce sont les cartes de la série Mars rover) et je les ai toutes flashées avec le projet avec ce code Verilog. Il y avait déjà 8 LED sur les cartes, mais elles étaient toutes jaunes. Sur chacune des cartes, j'ai remplacé 3 LED par R, G, B. J'ai connecté les cartes en série et, en plus, je les ai connectées à une vraie bande LED. Ainsi, j'ai allongé la vraie bande avec mes LED maison.

Cette connexion s'est avérée comme ceci:



En réalité, cela ressemble à ceci:



Maintenant, en appliquant une certaine image à la bande, je vois que mes «LED intelligentes» se comportent exactement comme les vraies de la bande:


Il s'avère que la logique que j'ai implémentée dans le FPGA est entièrement fonctionnelle! En première approximation, j'ai pu faire quelque chose de similaire à une vraie puce LED intelligente.

En fait, j'aime les bandes LED. Sur leur base, chacun peut inventer quelque chose qui lui est propre: éclairage intelligent, écrans, effets d'ambilight. Une fois, j'ai même implémenté de la musique couleur sur une bande LED exécutant FPGA. Mais c'est une autre histoire .

All Articles