对许多人来说,“如何运作”这个问题似乎很愚蠢。答案几乎是显而易见的:可寻址的LED灯带由多个串联的“智能LED”组成。只需查看磁带设备即可看到。您可以看到将单个微电路焊接到柔性回路中,还可以看到连接:微电路仅用三根电线串联连接,其中两根是电源和地线。仅一根导线传输像素颜色数据。那个怎么样?什么是智能LED?接下来,我将讨论基于WS2812B的LED灯条中使用的数据传输协议,此外,我将几乎在FPGA芯片中创建自己的“ LED灯条芯片”。因此,磁带通过单个数据信号使用串行传输。零位作为短的正脉冲和大约比脉冲宽两倍的暂停发送。位单元以宽正脉冲和短暂停时间的形式
传输:如果传输时间不超过50微秒,则磁带将返回其原始状态,准备从第一个像素开始接受像素。序列中的每24位是3个字节,用于三种RGB颜色。实际上,序列将是GRB。 G7的最高位在前。前24位的顺序是一个像素,它将接收录像带中的第一个LED。在第一个LED饱和之前,它不会将数据进一步传输到下一个LED。在第一个LED接收到其24x RGB位的部分后,它将打开与下一个LED的传输。最初,人们可以将一系列的LED想象成一连串的水罐,这些水罐先后装满水:第
一个,第二个,然后第三个,依次类推。因此,我认为传输协议已被整理。是否可以尝试自己设计这样的“智能LED”?当然,这没有什么实际意义,但是对于自我教育和开阔视野,这是一项有趣的任务。让我们尝试用编程语言Verilog HDL描述芯片的逻辑。当然,这不会是真正的芯片设计,会有局限性。最重要的限制之一-我的微电路将需要一个外部时钟。在真正的智能LED中,也存在这种发生器,但是它已经集成到芯片中。让我们像这样启动Verilog模块:module WS2812B(
input wire clk,
input wire in,
output wire out,
output reg r,
output reg g,
output reg b
);
我认为这里很清楚:时钟频率clk,“智能LED”的输入和输出信号进出,当然还有输出信号r,g,b,通过它们我将控制真正的外部LED红色,绿色和蓝色。我将在一个两位移位寄存器中捕获输入信号,然后从这些捕获的位中的当前状态,可以确定信号上升沿的开始位置: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);
此外,重要的是确定控制控制器在开始新的传输之前暂停时磁带重置的状态: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);
此外,从in_pos_edge的上升沿开始,必须承受一些停顿,以便有时间修复新的位: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);
芯片中已接收的位数如下: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;
这里引入一个重要的通过信号,该信号仅确定输入流到输出的重定向。接受24x像素位后,通过信号设置为1: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;
当pass_final信号为1时,输入输出多路复用到输出输出。好吧,当然,我们需要一个移位寄存器,其中接收到的24位像素被累加: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] };
收到所有24位后,它们也将被复制到最后的24位寄存器中。现在问题仍然很小。必须实现PWM(横向脉冲调制)信号,以根据接收到的RGB字节将亮度传输到实际的外部LED: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
好像就这些了。一个小细节仍然存在-如何体验这一切?我使用了FPGA MAX II的一些简单主板(这些是Mars rover系列卡),并使用此Verilog代码在项目中将它们全部刷新了。板上已经有8个LED,但是它们都是黄色的。在每个板上,我用R,G,B替换了3个LED。我将板串联起来,然后将它们连接到真实的LED灯带上。因此,我用自制的LED延长了真实的磁带。这种连接的结果是这样的:
实际上,它看起来像这样:
现在,将某些图像应用于磁带,我看到我的“智能LED”的行为与磁带上的真实LED完全相同:事实证明,我在FPGA中实现的逻辑是完全可用的!作为第一近似,我能够执行类似于真正的智能LED芯片的操作。实际上,我喜欢LED灯条。在他们的基础上,每个人都可以发明自己的东西:智能照明,屏幕,反光效果。有一次我什至在运行FPGA的LED磁带上实现了彩色音乐。但这是另一个故事。