学习 WS2812B 的详细笔记

一、ws2812b概述

WS2812B是一种常见的RGB LED灯带,每个灯珠内部都有一个芯片控制,通过发送特定的时序数据来控制其亮灭。发送数据时,需要按照一定的时序发送24位RGB数据,其中高位在前低位在后,格式为GRB。发送数据时,需要注意不仅仅是发送高电平或低电平,而是要发送占空比不同的PWM波,比如给予一定的高电平和低电平时间。重置码是发送一个持续280us的低电平信号。可以先发送一组24位的数据,然后接一个重置信号表示一组结束。数据采用单总线,归零码的通讯方式, 每个像素点接收24bit数据,溢出的数据传输给下一个像素点

WS2812B作为一种常见的RGB LED(三色LED)驱动芯片,具有以下优点和缺点:

优点:

  1. 集成度高:WS2812B芯片将RGB LED和控制电路集成在一个小型封装中,简化了设计和布线过程。
  2. 控制灵活:每个WS2812B芯片都具有独立的控制电路,可以通过单线串联多个LED,并根据需要独立控制每个LED的颜色和亮度。
  3. 易于编程:WS2812B芯片与微控制器通信采用简单的串行通信协议,使得编程和控制相对容易。
  4. 色彩丰富:每个WS2812B芯片都能够显示256级亮度的红、绿、蓝三个颜色通道,通过调整三个颜色的亮度可以实现丰富多彩的效果。

缺点:

  1. 电源需求高:WS2812B芯片对电源的要求较高,供电电源需要稳定而强大,以保证每个芯片的正常工作。
  2. 刷新频率限制:由于通信协议的限制,WS2812B的刷新频率相对较低,控制大量LED时可能会出现刷新延迟。
  3. 单点故障影响范围大:因为WS2812B芯片串联连接,当某个芯片发生故障时,可能会影响整个串联链路的正常工作。

需要注意的是,以上是对WS2812B芯片本身的优缺点进行的讨论,实际使用中还需要根据具体应用场景和需求综合考虑。

二、ws2812b数据传输特性

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、ws2812b模块划分

ws2812b 的设计分为两个部分,一部分为ws2812b_control模块,另一部分为ws2812b_drive模块。程序设计主要通过状态机实现,系统框架图如下:

在这里插入图片描述

四、IP设计

4.1 ROM IP

ROM在本次工程的主要作用是将要显示的图像数据进行存储,在ws2812b_control模块的配合下输出像素数据,假设 输入图像如下:
在这里插入图片描述

一个像素大小为8*32的图像,IP配置如下,当然,也可以根据图像的实际大小对ROM的参数大小进行修改:
在这里插入图片描述

4.2 fifo IP

为了避免数据在各模块的传输过程中存在丢失,fifo主要是在ws2812b_drive模块对输入的数据进行缓存(数据缓冲),配置如下:
在这里插入图片描述

五、ws2812b_control模块

5.1 状态跳转

该模块只有简单的两个状态,
在这里插入图片描述

IDLE:空闲状态,无数据传输,输出高电平,当pix_data_vld信号到来时跳转到DATA;

DATA:数据位,通过计数器产生读取ROM的特定地址,从而读出数据输出,假如数据传输计数结束,跳转到IDLE;

5.2 设计文件

5.2.1 静态显示
/************************************************************** 
@Function:   提供64个RGB像素数据,给到ws2812接口模块,用于验证接口模块
**************************************************************/
module ws2812_control(
    input                   clk             ,
    input                   rst_n           ,
    output          [23:0]  pix_data        ,
    output                  pix_data_vld    ,
    input                   ready                   //可以接收图像数据了
);

    parameter   IDLE     =   0,
                DATA     =   1,
                DONE     =   2;

    reg     [2:0]   state   ;

    reg	[5:0] cnt_x;
    wire		  add_x_cnt,end_x_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 ;

// 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;	  //灰色


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

/**************************************************************
                        图像数据个数计数器
**************************************************************/       
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_x <= 'd0;						
        else    if(add_x_cnt) begin				
            if(end_x_cnt)						
                cnt_x <= 'd0;  				
            else									
                cnt_x <= cnt_x + 1'b1;		
        end											
    assign add_x_cnt = state == DATA;
    assign end_x_cnt = add_x_cnt && cnt_x == 8 - 1;


    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_cnt;
    assign end_y_cnt = add_y_cnt && cnt_y == 8 - 1;

    
/* 
    always@(*)
        case(cnt_y)
            0       :   pix_data = RED      ;
            1       :   pix_data = ORANGE   ;
            2       :   pix_data = YELLOW   ;
            3       :   pix_data = GREEN    ;
            4       :   pix_data = CYAN     ;
            5       :   pix_data = BLUE     ;
            6       :   pix_data = PURPPLE  ;
            7       :   pix_data = GRAY     ;
            default :   pix_data = RED      ;
        endcase */

    assign      rom_rd_req = state == DATA ;

    rom	rom_inst (
	.aclr       (  ~rst_n           ),
	.address    (  cnt_x + cnt_y*8  ),
	.clock      (  clk              ),
	.rden       (  rom_rd_req       ),
	.q          (  pix_data         )
	);


    always @(posedge clk or negedge rst_n)begin 
        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 
    end

    assign      rom_rd_data_vld = rom_rd_req_r2;
    assign      pix_data_vld = rom_rd_data_vld;


endmodule
5.2.1 动态显示
module ws2812_control2(
    input                   clk             ,
    input                   rst_n           ,
    output          [23:0]  pix_data        ,
    output                  pix_data_vld    ,
    input                   ready                   //可以接收图像数据了
);

    parameter   IDLE     =   0,
                DATA     =   1,
                DONE     =   2,
                DELAY    =   3;
    
    parameter           DLEAT_MAX = 1500_0000;

    reg [2:0]           state;

    reg	[5:0]           cnt_x;
    wire		        add_x_cnt,end_x_cnt;	

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

    reg	[6:0]	        cnt_offset;
    wire				add_cnt_offset,end_cnt_offset;

    reg [24:0]	        delay_cnt;
    wire				add_delay_cnt,end_delay_cnt	;
    

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

/**************************************************************
                          状态机
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
                IDLE		:	if(ready)
                                    state <=DATA;
                DATA		:	if(end_y_cnt)
                                    state <=DELAY;
                DELAY       :   if (end_delay_cnt) begin
                                    state <= IDLE;
                end
                default :	state <= IDLE;
        endcase
//****************************************************************
//--        帧间隔
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        delay_cnt <= 'd0;
    end 
    else if(add_delay_cnt)begin 
        if(end_delay_cnt)begin 
            delay_cnt <= 'd0;
        end
        else begin 
            delay_cnt <= delay_cnt + 1'b1;
        end 
    end
end 

assign add_delay_cnt = state == DELAY;
assign end_delay_cnt = add_delay_cnt && delay_cnt == DLEAT_MAX - 1;


/**************************************************************
                        图像数据个数计数器
**************************************************************/       
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_x <= 'd0;						
        else    if(add_x_cnt) begin				
            if(end_x_cnt)						
                cnt_x <= 'd0;  				
            else									
                cnt_x <= cnt_x + 1'b1;		
        end											
    assign add_x_cnt = state == DATA;
    assign end_x_cnt = add_x_cnt && cnt_x == 8 - 1;


    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_cnt;
    assign end_y_cnt = add_y_cnt && cnt_y == 8 - 1;

//偏移计数器

    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_offset <= 'd0;
        end 
        else if(add_cnt_offset)begin 
            if(end_cnt_offset)begin 
                cnt_offset <= 'd0;
            end
            else begin 
                cnt_offset <= cnt_offset + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_offset = end_delay_cnt;
    assign end_cnt_offset = add_cnt_offset && cnt_offset == 32 - 1;
    
    wire    [4:0]   real_row;// 0~32     

    assign          real_row = cnt_x + cnt_offset;

//****************************************************************
//--rom
//****************************************************************
    assign      rom_rd_req = state == DATA ;

    rom	rom_inst (
	.aclr       ( ~rst_n        ),
	.address    ( real_row + cnt_y*32 ),
	.clock      ( clk           ),
	.rden       ( rom_rd_req    ),
	.q          ( pix_data      )
	);

//打拍
    always @(posedge clk or negedge rst_n)begin 
        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 
    end

    assign      rom_rd_data_vld = rom_rd_req_r2 ;
    assign      pix_data_vld = rom_rd_data_vld  ;


endmodule

六、 ws2812b_drive模块

6.1 状态跳转

在这里插入图片描述

IDLE:空闲状态,无数据传输,输出低电平,当pix_data_vld信号到来时跳转到RST;

RST:复位状态,输出低电平,400us复位时间结束后跳转到DATA;

DATA:数据位,假如64个像素数据传输结束,跳转到IDLE;

6.2 设计文件

/**************************************************************  
            一个bit的周期1200ns,时钟50M,一共60个时钟周期
            0码     高电平  300ns
                    低电平  900ns
            1码     高电平  600ns
                    低电平  600ns
**************************************************************/
module ws2812_drive (
    input               clk             ,
    input               rst_n           ,
    input       [23:0]  pix_data        ,
    input               pix_data_vld    ,
    output              ready           ,       //可以接收图像数据了
    output  reg         ws2812_io       
);

    parameter       IDLE    =   0   ,
                    RST     =   1   ,
                    DATA    =   2   ;

    reg     [1:0]   state           ;

    wire            idle2rst        ;
    wire            rst2data        ;
    wire            data2idle       ;

    wire            fifo_wr_req     ;
    wire            fifo_rd_req     ;
    wire    [23:0]  fifo_wr_data    ;
    wire    [23:0]  fifo_rd_data    ;
    wire            fifo_empty      ;
    wire            fifo_full       ;

    reg	    [11:0]  cnt_cyc         ;
    wire    		add_cyc_cnt,end_cyc_cnt;	

    reg	    [4:0]   cnt_bit         ;
    wire    		add_bit_cnt,end_bit_cnt;	

    reg	    [5:0]   cnt_num         ;
    wire    		add_num_cnt,end_num_cnt;	

    reg	    [31:0]  cnt_rst         ;
    wire    		add_rst_cnt,end_rst_cnt;

    localparam      T0H     =   300/20  ,
                    T0L     =   900/20  ,
                    T1H     =   600/20  ,
                    T1L     =   600/20  ;

    parameter       RST_MAX =   400_000/20;

/**************************************************************
                            状态机
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
            IDLE    :   if(idle2rst)
                            state <= RST;
            RST     :   if(rst2data)
                            state <= DATA;
            DATA    :   if(data2idle)
                            state <= IDLE;
            default :   state <= IDLE;
        endcase

    assign idle2rst    =    state == IDLE && pix_data_vld;      //已经存放了一帧图像数据了
    assign rst2data    =    state == RST  && end_rst_cnt;       //400us复位时间结束
    assign data2idle   =    state == DATA && end_num_cnt;   

/**************************************************************
                            缓存图像数据
**************************************************************/
    fifo	fifo_inst (
        .aclr   (~rst_n         ),
        .clock  (clk            ),
        .data   (fifo_wr_data   ),      //24bit  GRB
        .rdreq  (fifo_rd_req    ),
        .wrreq  (fifo_wr_req    ),
        .empty  (fifo_empty     ),
        .full   (fifo_full      ),
        .q      (fifo_rd_data   ),      //24bit  GRB
        .usedw  (               )
	);

    assign  fifo_wr_data    =   {pix_data[15:8],pix_data[23:16],pix_data[7:0]};
    assign  fifo_wr_req     =   pix_data_vld && ~fifo_full;

    assign  fifo_rd_req     =   end_bit_cnt && ~fifo_empty;

/**************************************************************
                        数据时间线控制    
**************************************************************/
//一共64*24*1200ns
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_cyc <= 'd0;						
        else    if(add_cyc_cnt) begin				
            if(end_cyc_cnt)						
                cnt_cyc <= 'd0;  				
            else									
                cnt_cyc <= cnt_cyc + 1'b1;		
        end											
    assign add_cyc_cnt = state == DATA;
    assign end_cyc_cnt = add_cyc_cnt && cnt_cyc == 1200/20 - 1;     //一个bit的持续时间

    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bit <= 'd0;						
        else    if(add_bit_cnt) begin				
            if(end_bit_cnt)						
                cnt_bit <= 'd0;  				
            else									
                cnt_bit <= cnt_bit + 1'b1;		
        end											
    assign add_bit_cnt = end_cyc_cnt;
    assign end_bit_cnt = add_bit_cnt && cnt_bit == 24 - 1;

    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_num <= 'd0;						
        else    if(add_num_cnt) begin				
            if(end_num_cnt)						
                cnt_num <= 'd0;  				
            else									
                cnt_num <= cnt_num + 1'b1;		
        end											
    assign add_num_cnt = end_bit_cnt;
    assign end_num_cnt = add_num_cnt && cnt_num == 64 - 1;
/**************************************************************
                        复位计数器
**************************************************************/	
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_rst <= 'd0;						
        else    if(add_rst_cnt) begin				
            if(end_rst_cnt)						
                cnt_rst <= 'd0;  				
            else									
                cnt_rst <= cnt_rst + 1'b1;		
        end											
    assign add_rst_cnt = state == RST;
    assign end_rst_cnt = add_rst_cnt && cnt_rst == RST_MAX - 1;      //复位时间400us

/**************************************************************
                         实现单总线协议
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            ws2812_io <= 0;
        else case(state)
            IDLE    :   ws2812_io <= 0;
            RST     :   ws2812_io <= 0;
            DATA    :   if(fifo_rd_data[23-cnt_bit]) begin    //发送数据1
                            if(cnt_cyc < T1H) 
                                ws2812_io <= 1;
                            else
                                ws2812_io <= 0;
                        end
                        else begin                      //发送数据0
                            if(cnt_cyc < T0H)
                                ws2812_io <= 1;
                            else
                                ws2812_io <= 0;
                        end
            default :   ws2812_io <= 1;
        endcase

    assign ready = state == IDLE;

endmodule

七、TOP设计

/**************************************功能介绍***********************************
Date	: 
Author	: lxx.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk		,
    input				rst_n	,
 
    output              ws2812_io	
);								 

    
//---------<内部信号定义>-----------------------------------------------------
wire      [23:0]        pix_data     ;        
wire                    pix_data_vld ;        


//显示一张图
//ws2812_control ws2812_control_inst(
//    /* input                */.clk         ( clk          ) ,
//    /* input                */.rst_n       ( rst_n        ) ,
//    /* output  reg    [23:0]*/.pix_data    ( pix_data     ) ,
//    /* output               */.pix_data_vld( pix_data_vld ) ,
//    /* input                */.ready       ( ready        ) 
//);

//动态显示
ws2812_control2 ws2812_control2_inst(
    /* input                */.clk         ( clk          ) ,
    /* input                */.rst_n       ( rst_n        ) ,
    /* output  reg    [23:0]*/.pix_data    ( pix_data     ) ,
    /* output               */.pix_data_vld( pix_data_vld ) ,
    /* input                */.ready       ( ready        ) 
);

ws2812_drive ws2812_drive_inst( 
    /* input				*/.clk		    ( clk		     ) ,
    /* input				*/.rst_n	    ( rst_n	     ) , 
    /* input   	[23:0] 	    */.pix_data 	 ( pix_data      ) ,	//
    /* input                */.pix_data_vld  ( pix_data_vld  ) ,
    /* output               */.ready        ( ready         ) ,
    /* output      reg      */.ws2812_io    ( ws2812_io     )     
);							

    
endmodule

八、仿真模块

8.1 仿真文件

 `timescale 1ns/1ns   
 module tb_top();
 //激励信号定义 
    reg				clk  	;
    reg				rst_n	;
 //输出信号定义	 
    wire	 		ws2812_io	;
 //时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;  
    defparam      u_top.ws2812_control2_inst.DLEAT_MAX = 200; //参数重定义
 //模块例化
    top u_top( 
    /* input	 */	.clk	   (clk	      ) ,
    /* input	 */	.rst_n	   (rst_n	  ) ,
    /* output   */  .ws2812_io (ws2812_io )
);								 
 //产生时钟
    initial 		 clk = 1'b1;
    always #(CLOCK_CYCLE/2)  clk = ~ clk;
 //产生激励
    initial  begin
         rst_n = 1'b0;
        #(CLOCK_CYCLE*5);
         rst_n = 1'b1;
    end
 endmodule 

8.2 波形分析

ROM:

在这里插入图片描述
ws2812b_control模块
在这里插入图片描述
fifo写:
在这里插入图片描述
​ 放大:
在这里插入图片描述
fifo读:
在这里插入图片描述
ws2812b_drive模块
在这里插入图片描述
放大:
在这里插入图片描述

  • 46
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WS2812B是一种RGB LED灯带驱动芯片,它具有编程灵活性、高亮度和快速刷新速度等特点。下面是对WS2812B的中文资料的300字回答: WS2812B是一种数字灯带驱动芯片,其内置了RGB LED,可以实现每个LED的独立控制和变色。它采用了高度集成化的设计,包括LED、驱动电路和控制电路,因此可以轻松实现多个LED的级联控制。WS2812B具有广泛的应用领域,例如室内装饰、灯光秀、广告牌和汽车照明等。 WS2812B具有许多优点。首先,它可以通过串行数据线控制多个LED,减少了接线的复杂性。其次,使用WS2812B可以实现灵活的灯光效果,例如颜色变幻、闪烁和渐变等,通过编程可以轻松实现各种复杂的光效。此外,WS2812B具有高亮度和快速刷新速度,能够展现出鲜艳、清晰的灯光效果。此外,WS2812B具有低功耗和长寿命的特点,可以节省能源和延长使用寿命。 在使用WS2812B时,我们需要了解相关的技术参数和使用方法。例如,我们需要知道WS2812B的工作电压、电流和功率等,以保证正常使用和保护电路。此外,我们还需要了解WS2812B的通信协议,以便使用正确的信号格式和通信方式。对于初学者,可以参考WS2812B的相关教程和资料,学习如何连接、配置和控制WS2812B,以实现各种炫酷的灯光效果。 总之,WS2812B是一种功能强大的RGB LED驱动芯片,具有灵活的编程能力、高亮度和快速刷新速度等优势。在掌握相关知识和技术之后,我们可以利用WS2812B打造各种各样的个性化灯光效果,为室内外环境增添更多的色彩和乐趣。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值