這次設計一個RGB燈的控制器,該控制器具有如下特點:
- 每個燈的顏色可調,亮滅可控
- 可以設定引數來修改RGB的數目
WS2812B的資料時序如下圖所示:



(圖片來源自網路、侵權刪)
為了方便設計我把T1H和T0L的時間值設為0.8us,為了穩定將RES設定為60us,
設計的思路是,設定一個400ns的計時器,然后再設定一個計400ns次數的計時器(每計三次清零),然后1碼就是前兩個400us為高電平,第三個400us為低電平;0碼也是同理,
一、設計的代碼
1、單像素控制模塊
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: GDUT
// Engineer: Lclone
//
// Create Date: 2023/03/01 12:04:28
// Design Name:
// Module Name: WS2812B_controller
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module WS2812B_controller(
input Clk, //時鐘信號
input Rst_n, //復位信號
input [23:0] Disp_Data, //像素點的資料
input Data_valid, //資料有效信號
output reg Send_done, //單像素發送完成信號
output reg Data_out //資料輸出
);
parameter CNT_400NS = 20;
reg [4:0] cnt_400ns; //40us計數器
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
cnt_400ns <= 0;
else if(cnt_400ns == CNT_400NS - 1)
cnt_400ns <= 0;
else
cnt_400ns <= cnt_400ns + 1'b1;
end
reg [23:0] Disp_Data_reg; //資料寄存
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
Disp_Data_reg <= 0;
else if(Data_valid)
Disp_Data_reg <= Disp_Data;
else if(Send_done)
Disp_Data_reg <= 0;
else
Disp_Data_reg <= Disp_Data_reg;
end
parameter NUM_400NS = 3;
reg [1:0] num_400ns; //400ns段計數器,用來控制符號1和符號0的生成
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
num_400ns <= 0;
else if(num_400ns == NUM_400NS - 1 & cnt_400ns == CNT_400NS - 1)
num_400ns <= 0;
else if(cnt_400ns == CNT_400NS - 1)
num_400ns <= num_400ns + 1'b1;
else
num_400ns <= num_400ns;
end
parameter BIT_CNT = 24;
reg [4:0] bit_cnt; //位計數器
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
bit_cnt <= 0;
else if(bit_cnt == BIT_CNT - 1 & num_400ns == NUM_400NS - 1 & cnt_400ns == CNT_400NS - 1)
bit_cnt <= 0;
else if(num_400ns == NUM_400NS - 1 & cnt_400ns == CNT_400NS - 1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
Send_done <= 0;
else if(bit_cnt == BIT_CNT - 1 & num_400ns == NUM_400NS - 1 & cnt_400ns == CNT_400NS - 2)
Send_done <= 1;
else
Send_done <= 0;
end
reg symbol_1;
reg symbol_0;
always @(*) begin // 這里將case = 2的情況提前到case = 0 ,是為了讓模塊不在作業的時候使輸出為0;實際case情況串起來時也是滿足要求的
case (num_400ns)
0:begin
symbol_1 <= 0;
symbol_0 <= 0;
end
1:begin
symbol_1 <= 1;
symbol_0 <= 1;
end
2:begin
symbol_1 <= 1;
symbol_0 <= 0;
end
default:begin
symbol_1 <= 0;
symbol_0 <= 0;
end
endcase
end
always @(*) begin //資料傳輸,先傳輸高位
case(bit_cnt)
0: Data_out <= (Disp_Data_reg[23]) ? symbol_1 : symbol_0;
1: Data_out <= (Disp_Data_reg[22]) ? symbol_1 : symbol_0;
2: Data_out <= (Disp_Data_reg[21]) ? symbol_1 : symbol_0;
3: Data_out <= (Disp_Data_reg[20]) ? symbol_1 : symbol_0;
4: Data_out <= (Disp_Data_reg[19]) ? symbol_1 : symbol_0;
5: Data_out <= (Disp_Data_reg[18]) ? symbol_1 : symbol_0;
6: Data_out <= (Disp_Data_reg[17]) ? symbol_1 : symbol_0;
7: Data_out <= (Disp_Data_reg[16]) ? symbol_1 : symbol_0;
8: Data_out <= (Disp_Data_reg[15]) ? symbol_1 : symbol_0;
9: Data_out <= (Disp_Data_reg[14]) ? symbol_1 : symbol_0;
10: Data_out <= (Disp_Data_reg[13]) ? symbol_1 : symbol_0;
11: Data_out <= (Disp_Data_reg[12]) ? symbol_1 : symbol_0;
12: Data_out <= (Disp_Data_reg[11]) ? symbol_1 : symbol_0;
13: Data_out <= (Disp_Data_reg[10]) ? symbol_1 : symbol_0;
14: Data_out <= (Disp_Data_reg[9]) ? symbol_1 : symbol_0;
15: Data_out <= (Disp_Data_reg[8]) ? symbol_1 : symbol_0;
16: Data_out <= (Disp_Data_reg[7]) ? symbol_1 : symbol_0;
17: Data_out <= (Disp_Data_reg[6]) ? symbol_1 : symbol_0;
18: Data_out <= (Disp_Data_reg[5]) ? symbol_1 : symbol_0;
19: Data_out <= (Disp_Data_reg[4]) ? symbol_1 : symbol_0;
20: Data_out <= (Disp_Data_reg[3]) ? symbol_1 : symbol_0;
21: Data_out <= (Disp_Data_reg[2]) ? symbol_1 : symbol_0;
22: Data_out <= (Disp_Data_reg[1]) ? symbol_1 : symbol_0;
23: Data_out <= (Disp_Data_reg[0]) ? symbol_1 : symbol_0;
default:Data_out <= 0;
endcase
end
endmodule
2、單像素控制模塊仿真
仿真激勵
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/03/01 13:09:14
// Design Name:
// Module Name: WS2812B_controller_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module WS2812B_controller_tb();
reg clk_50m;
initial clk_50m <= 1;
always #10 clk_50m <= ~clk_50m;
reg rst_n;
initial begin
rst_n <= 0;
#200
rst_n <= 1;
end
reg [23:0] Disp_Data;
reg Data_valid;
wire Send_done;
wire Data_out;
WS2812B_controller WS2812B_controller_inst(
.Clk (clk_50m),
.Rst_n (rst_n),
.Disp_Data (Disp_Data),
.Data_valid (Data_valid),
.Send_done (Send_done),
.Data_out (Data_out)
);
initial begin
Disp_Data <= 0;
Data_valid <= 0;
#240;
Disp_Data <= 24'h123456;
Data_valid <= 1;
#20;
Data_valid <= 0;
@(posedge Send_done)
#20;
Disp_Data <= 24'h789abc;
Data_valid <= 1;
#20;
Data_valid <= 0;
@(posedge Send_done)
#100;
$stop;
end
endmodule
仿真結果

空閑狀態時輸出為0

多次發送,資料能夠正常銜接

最后發送完后資料能夠變為0

每位符號都能正常發送
結論:該模塊仿真通過,
3、多像素顯示模塊
在該模塊中,例化了一個24位寬、深度為16的簡單雙口RAM核,便于控制每個燈的狀態,同時還例化了ILA和VIO IP核來方便除錯,
RAM對應的地址即為RGB的編號,
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: GDUT
// Engineer: Lclone
//
// Create Date: 2023/03/01 15:29:54
// Design Name:
// Module Name: WS2812B_display
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module WS2812B_display(
input Clk, //時鐘輸入
input Rst_n, //復位信號
output Data_out //資料輸出
);
//RAM的各種信號
wire [23:0] ram_output_data;
wire [23:0] ram_input_data;
reg [3:0] ram_output_addr;
wire [3:0] ram_input_addr;
reg Data_valid;
wire ram_input_en;
//---------------------------
wire Send_done;
reg Send_done_r;//發送完成信號延一拍,方便資料對齊,
wire Data_out_t;
parameter CNT_60US = 3000;
reg [11:0] cnt_60us;
reg act_60us; //幀同步標志,在為高期間會復位單像素控制模塊
ila_0 ila_0_inst (
.clk(Clk), // input wire clk
.probe0(ram_output_addr), // input wire [3:0] probe0
.probe1(ram_output_data), // input wire [23:0] probe1
.probe2(Data_valid), // input wire [0:0] probe2
.probe3(Data_out) // input wire [0:0] probe3
);
vio_0 vio_0_inst (
.clk(Clk), // input wire clk
.probe_out0(ram_input_addr), // output wire [3 : 0] probe_out0
.probe_out1(ram_input_data), // output wire [23 : 0] probe_out1
.probe_out2(ram_input_en) // output wire [0 : 0] probe_out2
);
blk_mem_gen_0 blk_mem_gen_0_inst (
.clka(Clk), // input wire clka
.wea(ram_input_en), // input wire [0 : 0] wea
.addra(ram_input_addr), // input wire [3 : 0] addra
.dina(ram_input_data), // input wire [23 : 0] dina
.clkb(Clk), // input wire clkb
.addrb(ram_output_addr), // input wire [3 : 0] addrb
.doutb(ram_output_data) // output wire [23 : 0] doutb
);
WS2812B_controller WS2812B_controller_inst(
.Clk(Clk),
.Rst_n(~act_60us & Rst_n),
.Disp_Data(ram_output_data),
.Data_valid(Data_valid),
.Send_done(Send_done),
.Data_out(Data_out_t)
);
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
ram_output_addr <= 0;
else if(cnt_60us == CNT_60US - 1 )
ram_output_addr <= 0;
else if(Send_done == 1 & ~act_60us)
ram_output_addr <= ram_output_addr + 1'b1;
else
ram_output_addr <= ram_output_addr;
end
always @(posedge Clk) Send_done_r <= Send_done;
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
Data_valid <= 0;
else if((Send_done_r == 1 & ~act_60us)|cnt_60us == CNT_60US - 1)
Data_valid <= 1;
else
Data_valid <= 0;
end
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
act_60us <= 0;
else if(ram_output_addr == 4'hf & Send_done == 1)
act_60us <= 1;
else if(cnt_60us == CNT_60US - 1)
act_60us <= 0;
else
act_60us <= act_60us;
end
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
cnt_60us <= 0;
else if(cnt_60us == CNT_60US - 1)
cnt_60us <= 0;
else if(act_60us)
cnt_60us <= cnt_60us + 1'b1;
else
cnt_60us <= 0;
end
assign Data_out = (act_60us)? 0:Data_out_t;
endmodule
4、多像素顯示模塊仿真
仿真檔案
`timescale 1ns / 1ps
module display_tb();
reg clk_50m;
initial clk_50m <= 1;
always #10 clk_50m <= ~clk_50m;
reg rst_n;
initial begin
rst_n <= 0;
#200
rst_n <= 1;
end
wire Data_out;
WS2812B_display WS2812B_display_inst(
.Clk(clk_50m),
.Rst_n(rst_n),
.Data_out(Data_out)
);
endmodule
仿真結果

結論:模塊能夠正確給出Data_valid和send_done信號,且60us的幀同步信號也能正確的給出,仿真通過
二、上板驗證
將程式燒入芯片中,通過VIO控制寫入RAM的資料可以改變RGB的狀態,


該模塊能夠正常的改變每個RGB的狀態,該模塊初步驗證可行,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/545700.html
標籤:Verilog
