WS2812B

一,芯片介绍

1.管脚说明

在这里插入图片描述

2.数据传输时间

在这里插入图片描述

3.时序波形

在这里插入图片描述

4.数据传输方法

在这里插入图片描述

5.常用电路连接

在这里插入图片描述

二.项目要求

串口发送hex格式数据(00~FF),实时显示到WS2812B上(串口输入什么,WS2812就显示什么)

1.设计思路

通过串口接收到pc机发送的16进制数据后寄存器来,之后在contrl模块中,根据接收到的8bit数据分为两个4bit数据分为上下两位,之后根据这两个高低4bit数据匹配起始地址,之后将高低两个起始地址分别赋给高低两个计数器起始寄存器,通过一个flag信号来控制高低两个寄存器驱动,来控制输出高低位的地址,在通过y计数器来控制y轴,通过这三个计数器来匹配提前存在rom中的地址,之后rom将24bit数据传送到驱动模块达到串口发送什么就显示什么。

三.系统架构设计

在这里插入图片描述

四.模块说明

1.信号端口列表

  • 顶层:
module top( 
    input   wire                clk     ,
    input   wire                rst_n   ,
    input   wire                rx      ,//串口接收
    output  wire                ws2812b_io//io发送
);
  • 串口接收
 module uart_rx#
(
    parameter   BPS = 115200,
    parameter   CLK_FRE = 50_000_000,
    parameter   CHECK_BIT = "NONE"//NONE 不校验 DDO奇校验 EVEN偶校验

)
( 
    input   wire                clk     ,
    input   wire                rst_n   ,
    input   wire                rx      ,//串口接收
    output  wire                ready   ,//表示rx已经准备好接收数据了 
    output  wire     [7:0]      rx_data ,//rx的接收数据
    output  wire                rx_data_vld//rx数据发送信号
);
  • ws2812_control:
module ws2812_control(
    input               clk             ,
    input               rst_n           ,
    input       [7:0]   rx_data         ,//串口接收数据
    input               rx_data_vld     ,//串口数据发送信号
    output      [23:0]  pix_data        ,//rom读取数据
    output              pix_data_vld    ,//rom读取信号
    input               ready                   //可以接收图像数据了
);
  • ws2812_dirver:
module ws2812_dirver( 
    input   wire                clk     ,
    input   wire                rst_n   ,
    input   wire    [23:0]      data_in ,//rom读取数据
    input   wire                fifo_wr_vld,//fifo写信号
    output  reg                 ws2812b_io ,//串行输出
    output  wire                ready      //预备信号
);

2.状态转移图

  • RX:
    在这里插入图片描述

  • ws2812b_contrl:
    在这里插入图片描述

  • Ws2812b_dirver
    在这里插入图片描述

3.时序图

在这里插入图片描述

五.具体代码

/**************************************功能介绍***********************************
Date	: 
Author	: WZY.
Version	: 
Description: 顶层
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire                rx      ,//串口接收
    output  wire                ws2812b_io//io发送
);								 
//---------<参数定义>--------------------------------------------------------- 
wire    [7:0]   rx_data;
wire            rx_data_vld;
wire    [23:0]  pix_data;
wire            pix_data_vld;
wire            ready       ;
//---------<内部信号定义>-----------------------------------------------------

uart_rx#
(
    .BPS( 115200),
    .CLK_FRE ( 50_000_000),
    .CHECK_BIT  ("NONE")//NONE 不校验 DDO奇校验 EVEN偶校验

) uart_rx_inst
( 
    /*input   wire			*/	   .clk		   (clk),
    /*input   wire			*/	   .rst_n	   (rst_n),
    /*input   wire          */     .rx          (rx),
    /*output  wire          */     .ready       (),
    /*output  wire     [7:0]*/     .rx_data     (rx_data),
    /*output  wire          */     .rx_data_vld (rx_data_vld)
);  
    

ws2812_control ws2812_control(
        .clk             (clk),
        .rst_n           (rst_n),
        .rx_data         (rx_data),
        .rx_data_vld     (rx_data_vld),
        .pix_data        (pix_data),
        .pix_data_vld    (pix_data_vld),
        .ready           (ready)        //可以接收图像数据了
);   

// ws2812_control ws2812_control(
//         .clk             (clk),
//         .rst_n           (rst_n),
//         .rx_data         (8'h12),
//         .rx_data_vld     (1),
//         .pix_data        (pix_data),
//         .pix_data_vld    (pix_data_vld),
//         .ready           (ready)        //可以接收图像数据了
// ); 

ws2812_dirver ws2812_dirver_inst( 
        .clk		(clk),
        .rst_n	    (rst_n),
        .data_in    (pix_data),
        .fifo_wr_vld(pix_data_vld),
        .ws2812b_io (ws2812b_io),
        .ready      (ready)
);	   
endmodule
/**************************************功能介绍***********************************
Date	: 
Author	: WZY.
Version	: 
Description: 串口接收模块()
*********************************************************************************/
//模块例化
// uart_rx#
// (
//     parameter   BPS = 115200,
//     parameter   CLK_FRE = 50_000_000,
//     parameter   CHECK_BIT = "NONE"//NONE 不校验 DDO奇校验 EVEN偶校验

// ) uart_rx_inst
// ( 
//     /*input   wire			*/	   clk		   (clk),
//     /*input   wire			*/	   rst_n	   (rst_n),
//     /*input   wire          */      rx          (),
//     /*output  wire          */      ready       (),
//     /*output  wire     [7:0]*/      rx_data     (),
//     /*output  wire          */      rx_data_vld ()
// );	


//---------<模块及端口声名>------------------------------------------------------
module uart_rx#
(
    parameter   BPS = 115200,
    parameter   CLK_FRE = 50_000_000,
    parameter   CHECK_BIT = "NONE"//NONE 不校验 DDO奇校验 EVEN偶校验

)
( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire                rx      ,//串口接收
    output  wire                ready   ,//表示rx已经准备好接收数据了 
    output  wire     [7:0]      rx_data ,//rx的接收数据
    output  wire                rx_data_vld//rx数据发送信号
);								 
//---------<参数定义>--------------------------------------------------------- 

parameter   MAX_1bit = CLK_FRE/BPS;
//状态机参数定义
localparam  IDLE   = 5'b00001,//空闲状态等待数据输入
            START  = 5'b00010,//开始标志位接收状态
            DATA   = 5'b00100,//数据接收状态
            CHECK  = 5'b01000,//奇偶校验位接收
            STOP   = 5'b10000;//停止标志位接收状态
//---------<内部信号定义>-----------------------------------------------------
reg 	[4:0]	cstate     ;//现态
reg	    [4:0]	nstate     ;//次态

wire    idle2start;
wire    start2data;
wire    data2stop ;
wire    data2check;
wire    check2stop;
wire    stop2idle ;
reg [7:0]   rx_data_r;
reg         temp;//校验位接收
reg         flag;//数据有效使能

//下降沿检测参数
reg     rx_0;
reg     rx_1;
wire    nege;

//计数器参数
reg         [3:0]   num             ;
reg			[8:0]	cnt_start	   	;
wire				add_cnt_start	;
wire				end_cnt_start	;

reg			[11:0]	cnt_data	   	;
wire				add_cnt_data	;
wire				end_cnt_data	;

reg			[2:0]	cnt_num	   	;
wire				add_cnt_num	;
wire				end_cnt_num	;

reg			[8:0]	cnt_stop	   	;
wire				add_cnt_stop	;
wire				end_cnt_stop	;

reg                 rx_data_vld_r;

//****************************************************************
//                  rx控制状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE  : begin
                    if (idle2start) begin
                        nstate = START;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        START : begin
                    if (start2data) begin
                        nstate = DATA;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        DATA  : begin
                    if (data2stop) begin
                        nstate = STOP;
                    end
                    else if (data2check) begin
                        nstate = CHECK;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        CHECK : begin
                    if (check2stop) begin
                        nstate = STOP;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        STOP  : begin
                    if (stop2idle) begin
                        nstate = IDLE;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        default : nstate = IDLE;
    endcase
end

assign idle2start = cstate == IDLE  && nege;//检测到下降沿跳转
assign start2data = cstate == START && end_cnt_num;//接收1bit开始位
assign data2stop  = cstate == DATA  && end_cnt_num && CHECK_BIT == "NONE";//若没有校验位则跳转到stop状态
assign data2check = cstate == DATA  && end_cnt_num && CHECK_BIT != "NONE";
assign check2stop = cstate == CHECK && end_cnt_num;//校验位接收
assign stop2idle  = cstate == STOP  && end_cnt_num;//接收1bit结束位
            
//第三段:描述输出,时序逻辑或组合逻辑皆可
always @(*) begin
    if (!rst_n) begin
        rx_data_r = 8'b0;
        temp =0;
    end
    else case (cstate)
        DATA    : if (cnt_data == MAX_1bit/2) begin  //中间取样  
                    rx_data_r[cnt_num] = rx_0;
                    end
        CHECK   : if (cnt_data == MAX_1bit/2) begin //中间取样
                    temp = rx_0;
                    end
        default:rx_data_r = rx_data_r; 
    endcase

end            
assign rx_data = rx_data_r;
assign rx_data_vld = (CHECK_BIT == "NONE")?data2stop:flag;  
assign ready       = cstate == IDLE;   
            
//****************************************************************
//                          1bit数据
//**************************************************************** 
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_data <= 12'd0;
    end 
    else if(add_cnt_data)begin 
        if(end_cnt_data)begin 
            cnt_data <= 12'd0;
        end
        else begin 
            cnt_data <= cnt_data + 1'b1;
        end 
    end
end 

assign add_cnt_data = cstate != IDLE;
assign end_cnt_data = add_cnt_data && cnt_data == MAX_1bit-1;
//****************************************************************
//                          选择数据位计数器
//****************************************************************


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_num <= 3'd0;
    end 
    else if(add_cnt_num)begin 
        if(end_cnt_num)begin 
            cnt_num <= 3'd0;
        end
        else begin 
            cnt_num <= cnt_num + 1'b1;
        end 
    end
end 

assign add_cnt_num = end_cnt_data;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;

//****************************************************************
//                  数据控制
//****************************************************************
always @(*) begin
    case (cstate)
        IDLE :  num = 1;     
        START:  num = 1;
        DATA :  num = 8;
        CHECK:  num = 1;
        STOP :  num = 1;
        default: num = 1;
    endcase
end
//****************************************************************
//                  校验位检测
//****************************************************************
always @(*) begin
    if (!rst_n) begin
        flag = 0;
    end
    else if (cstate == IDLE) begin
        flag = 0;
    end
    else if (cstate == CHECK && end_cnt_num) begin
        if (CHECK_BIT == "DDO") begin
            if (temp == (~^rx_data_r)) begin
                flag = 1;
            end
            else begin
              flag = 0;
            end
        end
        else if (CHECK_BIT == "EVEN") begin
            if (temp == (^rx_data_r)) begin
                flag = 1;
            end
            else begin
              flag = 0;
            end
        end
    end
    else begin
      flag = 0;
    end
end
//****************************************************************
//                          下降沿检测
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rx_0 = 0;
        rx_1 = 0;
    end
    else begin
      rx_0 <= rx;
      rx_1 <= rx_0;
    end
end
assign nege = ~rx_0 && rx_1;

// always @(posedge clk or negedge rst_n) begin
//     if (!rst_n) begin
//         rx_data_vld_r <= 0;
//     end
//     else if (CHECK_BIT == "NONE"&&data2stop) begin
//         rx_data_vld_r <= 1;
//     end
//     else if (CHECK_BIT!= "NONE"&&data2check&&flag) begin
//         rx_data_vld_r <= 1;
//     end
//     else if (CHECK_BIT!= "NONE"&&data2check&&(flag == 0)) begin
//         rx_data_vld_r <= 0;
//     end
//     else begin
//       rx_data_vld_r <= rx_data_vld_r;
//     end
// end
// assign rx_data_vld = rx_data_vld_r;
endmodule
/**************************************************************
@File    :   ws2812_control2.v
@Time    :   2023/08/14 10:04:56
@Author  :   WangHaodong 
@EditTool:   VS Code 
@Font    :   UTF-8 
@Function:   显示一张图片
**************************************************************/
module ws2812_control(
    input               clk             ,
    input               rst_n           ,
    input       [7:0]   rx_data         ,//串口接收数据
    input               rx_data_vld     ,//串口数据发送信号
    output      [23:0]  pix_data        ,//rom读取数据
    output              pix_data_vld    ,//rom读取信号
    input               ready                   //可以接收图像数据了
);

    
    localparam	RED     =   24'hFF0000,   //红色
                ORANGE  =   24'hFF8000,   //橙色
                YELLOW  =   24'hFFFF00,   //黄色
                GREEN   =   24'h00FF00,   //绿色
                CYAN    =   24'h00FFFF,   //青色
                BLUE    =   24'h0000FF,   //蓝色
                PURPPLE =   24'h8000FF,   //紫色
                BLACK   =   24'h000000,   //黑色
                WHITE   =   24'hFFFFFF,   //白色
                GRAY    =   24'hC0C0C0;	  //灰色
    parameter   IDLE    =   0,
                DATA    =   1,
                DOWN   =   2;

    reg     [2:0]   state   ;

    reg	[6:0]     cnt_x_high;
    wire		  add_x_high_cnt,end_x_high_cnt;	

        reg	[6:0] cnt_x_low;
    wire		  add_x_low_cnt,end_x_low_cnt;

    reg	[4:0]     cnt_y;
    wire		  add_y_cnt,end_y_cnt;	

    wire        rom_rd_req      ;
    wire        rom_rd_data_vld ;
    reg         rom_rd_req_r1   ;
    reg         rom_rd_req_r2   ;

//数据寄存
reg     [7:0]       rx_data_r;
//flag控制输出信号
reg                 flag      ;
//起始地址寄存器
reg      [6:0]      num_high  ;//高位起始地址
reg      [6:0]      num_low   ;//地位起始地址

/**************************************************************
                            状态机
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
                IDLE		:	if(ready&&rx_data_vld)
                                    state <=DATA;
                DATA		:	if(end_y_cnt)
                                    state <=DOWN;
                // DOWN       :   if (end_cnt_delay) 
                //                     state <= IDLE;
                default :	state <= IDLE;
        endcase

//****************************************************************
//                      数据寄存
//****************************************************************
always @(*) begin
    if (!rst_n) begin
        rx_data_r <= 0;
    end
    else if (rx_data_vld&&state==IDLE) begin
        rx_data_r <= rx_data;
    end
    else begin
      rx_data_r <= rx_data_r;
    end
end

//****************************************************************
//                      flag使能
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        flag <= 0;
    end
    else if (end_x_high_cnt||end_x_low_cnt) begin
        flag <= ~flag;
    end
    else begin
      flag <= flag;
    end
end
//****************************************************************
//                         起始地址
//****************************************************************
always @(*) begin
    if (!rst_n) begin
        num_high = 0;
    end
    if (state == IDLE && rx_data_vld) begin
        case (rx_data[7:4])
            4'd0:   num_high = 0;
            4'd1:   num_high = 4;
            4'd2:   num_high = 8;
            4'd3:   num_high = 12;
            4'd4:   num_high = 16;
            4'd5:   num_high = 20;
            4'd6:   num_high = 24;
            4'd7:   num_high = 28;
            4'd8:   num_high = 32;
            4'd9:   num_high = 36;
            4'd10:  num_high = 40;
            4'd11:  num_high = 44;
            4'd12:  num_high = 48;
            4'd13:  num_high = 52;
            4'd14:  num_high = 56;
            4'd15:  num_high = 60;
            default:num_high = 1;
        endcase
    end
end

always @(*) begin
    if (!rst_n) begin
        num_low = 0;
    end
    if (state == IDLE && rx_data_vld) begin
        case (rx_data[3:0])
            4'd0:   num_low = 0;
            4'd1:   num_low = 4;
            4'd2:   num_low = 8;
            4'd3:   num_low = 12;
            4'd4:   num_low = 16;
            4'd5:   num_low = 20;
            4'd6:   num_low = 24;
            4'd7:   num_low = 28;
            4'd8:   num_low = 32;
            4'd9:   num_low = 36;
            4'd10:  num_low = 40;
            4'd11:  num_low = 44;
            4'd12:  num_low = 48;
            4'd13:  num_low = 52;
            4'd14:  num_low = 56;
            4'd15:  num_low = 60;
            default:num_low = 1;
        endcase
    end
end

/**************************************************************
                        图像数据个数计数器
**************************************************************/       
//横坐标高位
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_x_high <= 6'd0;	
        else if (state == IDLE && rx_data_vld) begin
            cnt_x_high<=num_high;
        end					
        else    if(add_x_high_cnt) begin				
            if(end_x_high_cnt)						
                cnt_x_high <= num_high;  				
            else									
                cnt_x_high <= cnt_x_high + 1'b1;		
        end											
    assign add_x_high_cnt = (state == DATA)&&(flag == 0);
    assign end_x_high_cnt = add_x_high_cnt && cnt_x_high == num_high+3;

//横坐标低位
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_x_low <= 6'd0;
        else if (state == IDLE && rx_data_vld) begin
            cnt_x_low <= num_low;
        end						
        else    if(add_x_low_cnt) begin				
            if(end_x_low_cnt)						
                cnt_x_low <= num_low;  				
            else									
                cnt_x_low <= cnt_x_low + 1'b1;		
        end											
    assign add_x_low_cnt = state == DATA&&(flag==1);
    assign end_x_low_cnt = add_x_low_cnt && cnt_x_low == num_low+3;

//纵坐标
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_y <= 'd0;						
        else    if(add_y_cnt) begin				
            if(end_y_cnt)						
                cnt_y <= 'd0;  				
            else									
                cnt_y <= cnt_y + 1'b1;		
        end											
    assign add_y_cnt = end_x_low_cnt;
    assign end_y_cnt = add_y_cnt && cnt_y == 8 - 1;

wire    [31:0]  address;
wire            data_out;
reg             flag_0;
reg             flag_1;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        flag_0 <= 0;
        flag_1 <= 0;
    end
    else begin
      flag_0 <= flag;
      flag_1 <= flag_0;
    end
end
//存放了一张图片
    rom	rom_inst (
        .aclr       ( ~rst_n ),
        .address    (  address ),
        .clock      ( clk ),
        .rden       (rom_rd_req),
        .q          (data_out)
	);

assign  pix_data = flag_1?(data_out?BLUE:BLACK)
                    :(data_out?RED:BLACK);
   

    assign rom_rd_req = state == DATA;
    assign address = flag?(cnt_y*64+cnt_x_low):(cnt_y*64+cnt_x_high);
    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            rom_rd_req_r1 <= 0;
            rom_rd_req_r2 <= 0;
        end
        else begin
            rom_rd_req_r1 <= rom_rd_req;
            rom_rd_req_r2 <= rom_rd_req_r1;
        end

    assign rom_rd_data_vld = rom_rd_req_r2;
    assign pix_data_vld = rom_rd_data_vld;//打两拍使得读出数据和fifo中写入数据同步





endmodule

/**************************************功能介绍***********************************
Date	: 
Author	: WZY.
Version	: 
Description: 这是项目的逻辑状态机模块
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module ws2812_dirver( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire    [23:0]      data_in ,//rom读取数据
    input   wire                fifo_wr_vld,//fifo写信号
    output  reg                 ws2812b_io ,//串行输出
    output  wire                ready      //预备信号
);								 
//---------<参数定义>--------------------------------------------------------- 
 //状态机参数定义
 parameter IDLE = 3'b001,//空闲状态
           RST  = 3'b010,//复位状态
           DATA = 3'b100;//数据传输状态    

//---------<内部信号定义>-----------------------------------------------------

reg 	[2:0]	cstate      ;//现态
reg	    [2:0]	nstate      ;//次态
wire            idle2rst    ;
wire            rst2data    ;
wire            data2idle   ;


//fifoIP核参数定义
wire    [23:0]  fifo_wr_data;
wire    [23:0]  fifo_rd_data;
wire            empty       ;
wire            full        ;
wire            fifo_rd_req ;
wire            fifo_wr_req ;

//复位参数定义
reg			[14:0]	cnt_rst	   	;
wire				add_cnt_rst	;
wire				end_cnt_rst	;

//数据传输参数定义
reg			[5:0]	cnt_cyc	   	;
wire				add_cnt_cyc	;
wire				end_cnt_cyc	;

reg			[4:0]	cnt_bit	   	;
wire				add_cnt_bit	;
wire				end_cnt_bit	;

reg			[5:0]	cnt_num	   	;
wire				add_cnt_num	;
wire				end_cnt_num	;


//****************************************************************
//                  状态机
//****************************************************************

//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE  : begin
            if (idle2rst) begin
                nstate = RST;
            end
            else begin
                nstate = cstate;
            end
        end
        RST   : begin
            if (rst2data) begin
                nstate = DATA;
            end
            else begin
                nstate = cstate;
            end
        end
        DATA : begin
            if (data2idle) begin
                nstate = IDLE;
            end
            else begin
                nstate = cstate;
            end
        end
        default : nstate = IDLE;
    endcase
end
assign idle2rst  = cstate == IDLE && fifo_wr_vld;//当检测到读使能时由空闲状态转换到复位状态
assign rst2data  = cstate == RST  && end_cnt_rst;//当复位完成后转到数据输入状态
assign data2idle = cstate == DATA && end_cnt_num;//当数据输入完成后返回空闲状态           
//第三段:描述输出,时序逻辑或组合逻辑皆可


//****************************************************************
//                      IP核FIFO读取
//****************************************************************           
fifo_test	fifo_test_inst (
	.aclr ( ~rst_n ),
	.clock ( clk ),
	.data ( fifo_wr_data ),
	.rdreq ( fifo_rd_req ),
	.wrreq ( fifo_wr_req),
	.empty ( empty ),
	.full ( full ),
	.q ( fifo_rd_data ),
	.usedw (  )
	);       

assign fifo_wr_data = {data_in[15:8],data_in[23:16],data_in[7:0]} ;//RGB->GRB
assign fifo_wr_req = fifo_wr_vld&&~full;//当检测到写使能并且不为满时拉高
assign fifo_rd_req = end_cnt_bit&& ~empty;//每次读取计时到一个数据后并且不为空时读出一个数据
    
//****************************************************************
//                  复位计时
//****************************************************************    


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_rst <= 15'd0;
    end 
    else if(add_cnt_rst)begin 
        if(end_cnt_rst)begin 
            cnt_rst <= 15'd0;
        end
        else begin 
            cnt_rst <= cnt_rst + 1'b1;
        end 
    end
end 

assign add_cnt_rst = cstate == RST;
assign end_cnt_rst = add_cnt_rst && cnt_rst == 400_000/20 - 1;

//****************************************************************
//                      数据传输计时
//****************************************************************


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_cyc <= 6'd0;
    end 
    else if(add_cnt_cyc)begin 
        if(end_cnt_cyc)begin 
            cnt_cyc <= 6'd0;
        end
        else begin 
            cnt_cyc <= cnt_cyc + 1'b1;
        end 
    end
end 

assign add_cnt_cyc = cstate == DATA;
assign end_cnt_cyc = add_cnt_cyc && cnt_cyc == 1200/20 - 1;




always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_bit <= 5'd0;
    end 
    else if(add_cnt_bit)begin 
        if(end_cnt_bit)begin 
            cnt_bit <= 5'd0;
        end
        else begin 
            cnt_bit <= cnt_bit + 1'b1;
        end 
    end
end 

assign add_cnt_bit = end_cnt_cyc;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 24-1;


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_num <= 6'd0;
    end 
    else if(add_cnt_num)begin 
        if(end_cnt_num)begin 
            cnt_num <= 6'd0;
        end
        else begin 
            cnt_num <= cnt_num + 1'b1;
        end 
    end
end 

assign add_cnt_num = end_cnt_bit;
assign end_cnt_num = add_cnt_num && cnt_num == 64-1;
//****************************************************************
//                      用户接口
//****************************************************************

always @(posedge clk or negedge rst_n) begin
    case (cstate)
        IDLE : ws2812b_io = 0;
        RST  : ws2812b_io = 0;
        DATA : begin
                    if (fifo_rd_data[23-cnt_bit] == 1) begin
                        ws2812b_io = (cnt_cyc <30)?1:0;
                    end
                    else  begin
                        ws2812b_io = (cnt_cyc<15)?1:0;
                    end
                end
        default: ws2812b_io = 0;
    endcase
end

//****************************************************************
//                      ready控制
//****************************************************************
assign ready = cstate == IDLE;
endmodule
`timescale 1ns/1ns
    
module top_tb();

//激励信号定义 
    reg				 clk  	;
    reg				 rst_n	;
    reg              rx     ;

//输出信号定义	 
    wire            ws2812b_io;

//时钟周期参数定义	
    parameter		CYCLE = 20;  
    // defparam        top_inst.ws2812_control.MAX_500S = 10; 

//模块例化
top top_inst( 
        .clk		(clk),
        .rst_n	    (rst_n),
        .rx         (rx),
        .ws2812b_io (ws2812b_io)
);	
// ws2812_control ws2812_control_inst(
//     .clk             (clk),
//     .rst_n           (rst_n),
//     .rx_data         (8'h12),
//     .rx_data_vld     (1),
//     .pix_data        (),
//     .pix_data_vld    (),
//     .ready           (1)        //可以接收图像数据了
// );
//产生时钟
    initial 		 clk = 1'b0;
    always #(CYCLE/2)  clk = ~ clk;

//产生激励
    initial  begin 
        rst_n = 0;
        rx    = 1 ;
        #20
        rst_n = 1;
        #20
        rx = 1  ;
        #(CYCLE*10)
        rx = 0  ;   //开始位发送
        #(CYCLE*10)
        rx = 1  ;   //发送数据1
        #(CYCLE*10)
        rx = 0  ;   //发送数据0
        #(CYCLE*10)
        rx = 1  ;   //发送数据1
        #(CYCLE*10)
        rx = 0  ;   //发送数据0
        #(CYCLE*10)
        rx = 1  ;   //发送数据1
        #(CYCLE*10)
        rx = 0  ;   //发送数据0
        #(CYCLE*10)
        rx = 1 ;   //发送数据1
        #(CYCLE*10)
        rx = 0  ;   //发送数据0
        // #(CYCLE*10)
        // rx = 1  ;   //发送标志位0//偶校验
        #(CYCLE*10)
        rx = 1  ;  //发送结束位

        // wait(top_inst.ws2812_dirver_inst.ready);
        // rx = 0  ;   //开始位发送
        // #(CYCLE*10)
        // rx = 0  ;   //发送数据1
        // #(CYCLE*10)
        // rx = 0  ;   //发送数据0
        // #(CYCLE*10)
        // rx = 1  ;   //发送数据1
        // #(CYCLE*10)
        // rx = 0  ;   //发送数据0
        // #(CYCLE*10)
        // rx = 1  ;   //发送数据1
        // #(CYCLE*10)
        // rx = 1  ;   //发送数据0
        // #(CYCLE*10)
        // rx = 1 ;   //发送数据1
        // #(CYCLE*10)
        // rx = 0  ;   //发送数据0
        // // #(CYCLE*10)
        // // rx = 1  ;   //发送标志位0//偶校验
        // #(CYCLE*10)
        // rx = 1  ;  //发送结束位
        #(CYCLE*1000);
    end

// initial begin
//     rst_n = 0;
//     #20
//     rst_n = 1;
//     #(CYCLE*100);
// end

endmodule 

六.仿真

  • 顶层:
    在这里插入图片描述

  • Rx:
    在这里插入图片描述
    可以看出来接收数据跟发送波形相同,并且在下一次数据接收到来之前数据保持不变达到了寄存的目的

  • ws2812b_contrl
    在这里插入图片描述
    可以看到起始地址跟接收到的数据相互匹配正确,并且也把其实地址正确的赋值给了两个计数寄存器,并且最终地址正确

七.上板验证

ws2812b

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值