一、手册分析
WS2812B的0、1不是单纯的拉高拉低电平,而是在一个周期内高低电平占空比不同。1码—高电平占580ns~1us(先高后低)、低电平占220ns~380ns。0码—高电平占580ns~1us(先低后高)、低电平占580ns~1us。而1帧与1帧之间的间隔要低电平280us以上。(帧:显示一幅画面)
及联电路:这个过程包括将一个电路的输出连接到另一个电路的输入来形成连续的操作关系。这种级联是通过将一个电路中的输出端口与另一个电路中的输入端口相连接来完成的。简单来说就是挨个给电流,第一个信号(24bit)给第一个led后第二个信号(24bit)给第二个led。一帧后要刷新一个RESET后才开始下一帧。
此外、我们还知道WS2812B有64个LED灯珠,每个灯珠要24bit数据控制,00000000_00000000_00000000.通过这24bit来控制灯珠颜色与亮度。且这24bit是高位先发。
二、代码分析
module ws2812_ctrl(
input clk , //系统时钟
input rst_n , //复位鑫海
output din //ws2812b的数据输入
);
parameter TIME = 7'd64 ; //传输一个数据的最大时间 64*20=1280ns
parameter Zhen = 5'd24 ; //一帧有24个数据//一个灯的数据
parameter MAX = 7'd64 ; //一共要点64个灯。
parameter RGB = 24'b00000000_00001111_00000000; // 红色
parameter RGB1 = 24'b00001111_00000000_00000000; // 蓝色
parameter RGB2 = 24'b00000000_00000000_00001111; // 绿色
reg [6:0]cnt0 ;//计数一bit数据传输的最长周期,
wire add_cnt0 ;
wire end_cnt0 ;
reg [4:0] cnt_1z ; //一个灯珠要24bit数据
wire add_cnt_1z ;
wire end_cnt_1z ;
reg [6:0] cnt_64z ; //一共有64给灯珠。
wire add_cnt_64z ;
wire end_cnt_64z ;
reg [26:0] cnt_rst ; //计数复位信号,用于自动刷新计数
wire add_cnt_rst ;
wire end_cnt_rst ;
reg [8:0] cnt_state ; //灯珠闪烁的三种状态,一种颜色一帧,3种颜色循环。
wire add_cnt_state ;
wire end_cnt_state ;
reg [23:0] rgb_b ;
reg tx_done ;//1bit数据传输完的使能信号
reg rgb_r ; //寄存每次传输的RGB数据;
reg data_out1 ; //1码 和 0码 的转换
//计数一bit数据传输的最长周期
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt0 <= 0;
end
else if(add_cnt0) begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0+1 ;
end
end
assign add_cnt0 = tx_done ;
assign end_cnt0 = add_cnt0 && cnt0 == 64 - 1;
//一帧数据传输24bit
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_1z <= 24;
end
else if(add_cnt_1z) begin
if(end_cnt_1z)
cnt_1z <= 24;
else
cnt_1z <= cnt_1z-1 ;
end
end
assign add_cnt_1z = end_cnt0;
assign end_cnt_1z = add_cnt_1z && cnt_1z == 1; //减到0就停止计数
//一共发送64帧;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_64z <= 0;
end
else if(add_cnt_64z) begin
if(end_cnt_64z)
cnt_64z <= 0;
else
cnt_64z <= cnt_64z+1 ;
end
end
assign add_cnt_64z = end_cnt_1z;
assign end_cnt_64z = add_cnt_64z && cnt_64z == 64 - 1;
//灯珠闪烁三种颜色变换
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_state <= 0;
end
else if(add_cnt_state) begin
if(end_cnt_state)
cnt_state <= 0;
else
cnt_state <= cnt_state+1 ;
end
end
assign add_cnt_state = end_cnt_64z; //一帧结束后计数一次
assign end_cnt_state = add_cnt_state && cnt_state == 3 - 1;
//复位信号
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin //复位状态和发送数据使能为1时数据清零
cnt_rst <= 0;
end
else if(tx_done == 1)begin //当数据传输使能有效时复位计数器一直为低,
cnt_rst <= 0 ;
end
else if(add_cnt_rst) begin
if(end_cnt_rst)
cnt_rst <= cnt_rst;
else
cnt_rst <= cnt_rst+1 ;
end
end
assign add_cnt_rst = 1;
assign end_cnt_rst = add_cnt_rst && cnt_rst == 5000_0000 - 1;
//传输数据使能信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tx_done <= 0 ;
end
else if(end_cnt_64z)begin //当计数完64帧以后 发送数据使能拉低
tx_done <= 0;
end
else if(end_cnt_rst) begin //复位完成,发送数据使能拉低
tx_done <= 1;
end
else begin
tx_done <= tx_done;
end
end
//当复位后以及1帧(64个灯珠亮完)后,使能信号拉低,复位计数器开始计数,计数5000_0000,计数结束后
//使能信号拉高,数据开始传输。实现自动帧刷新。
//灯珠的三种颜色按顺序
always @(*)begin
if(rst_n==1'b0)begin
rgb_b <= 24'd0 ;
end
else begin
case(cnt_state)
0: rgb_b = RGB ; //这里是一帧一帧改,可以改变cnt_state计数方式来感觉显示
1: rgb_b = RGB1 ;
2: rgb_b = RGB2 ;
default : ;
endcase
end
end
//将颜色数据的每一个bit赋值给寄存器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rgb_r <= rgb_b[23];
end
else begin
rgb_r <= rgb_b[cnt_1z-1] ;
end
end
//0码和1码
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0 )begin
data_out1 <= 0 ;
end
else if(tx_done == 0)begin
data_out1 <= 0 ;
end
else if((rgb_r == 1'b0 )&&( tx_done == 1) ) begin //发送0码
if(cnt0 < 15)begin
data_out1 <= 1 ;
end
else begin
data_out1 <= 0 ;
end
end
else if((rgb_r == 1'b1) && (tx_done==1))begin //发送1码
if(cnt0 < 35)begin //小于35未高电平
data_out1 <= 1 ;
end
else begin
data_out1 <= 0 ;
end
end
else begin
data_out1 <= data_out1 ;
end
end
assign din = data_out1 ; //赋值给din信号
endmodule