这次设计一个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 : sym