FPGA + WS2812采灯控制

文章目录

一、WS2812C-2020-V1

1、产品概述

WS2812C-2020-V1是一个集控制电路与发光电路于一体的智能外控LED光源;其外型采用最新的molding封

装工艺,将IC与发光芯片封装在一个2020的封装尺寸中,每个元件即为一个像素点;像素点内部包含了智能数字

接口数据锁存信号整形放大驱动电路,还包含有高精度的内部振荡器和可编程定电流控制部分,有效保证了像素

点光的颜色高度一致。

主要特点:

● IC控制电路与LED点光源共用一个电源。

● 每个通道工作电流5mA.

● 控制电路与RGB芯片集成在一个2020封装的元器件中,构成一个完整的外控像素点。

● 内置信号整形电路,任何一个像素点收到信号后经过波形整形再输出,保证线路波形畸变不会累加。

● 内置上电复位和掉电复位电路。

● 每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。

● 端口扫描频率2KHz/s。

● 串行级联接口,能通过一根信号线完成数据的接收与解码。

● 任意两点传输距离在不超过5米时无需增加任何电路。

● 当刷新速率30帧/秒时,级联数不小于1024点。

● 数据发送速度可达800Kbps。

● 光的颜色高度一致,性价比高。

2、引出端排列及功能

序号 符号 管脚名 功能描述
1 DO 数据输出 控制数据信号输出
2 GND 信号接地和电源接地
3 DI 数据输入 控制数据信号输入
4 VDD 电源 供电管脚

3、数据传输时间


时序波形图

4、数据传输方法

注:其中 D1 为 MCU 端发送的数据,D2、D3、D4 为级联电路自动整形转发的数据。
24bit 数据结:

二、使用WS2812C显示图片

1、静态显示

显示F为例:

首先将图片信息存入rom中,通过读出rom中的数据,将数据中的值通过高低电平的脉宽调制,显示至WS2812C上。
显示模块:

verilog 复制代码
module ws2812b_driver (
    input       wire        clk         ,
    input       wire        rst_n       ,
    input       wire [23:0] data_in     ,//输入的RGB
    input       wire        data_vld    ,

    output      wire        ready       ,
    output      wire        pwm                 //输出波形
);


localparam      IDLE = 3'b001,
                RST  = 3'b010,
                DATA = 3'b100;

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

parameter MAX_RES = 15'd20_000;

reg [2:0]       state_c;//现态
reg [2:0]       state_n;//次态


wire            idle_rst    ;//IDLE -> RST
wire            rst_data    ;//RST -> DATA
wire            data_idle   ;//DATA -> IDLE


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

reg			[14:0]	cnt_res	   	;
wire				add_cnt_res	;
wire				end_cnt_res	;


reg			[5:0]	cnt_time	   	;
wire				add_cnt_time	;
wire				end_cnt_time	;


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

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


reg               pwm_r;

//****************************************************************
//--                状态机
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n ;
    end
end


always @( *) begin
    case (state_c) 
        IDLE : begin
            if(idle_rst) begin
                state_n = RST;
            end
            else begin
                state_n = state_c;
            end
        end

        RST : begin
            if(rst_data) begin
                state_n = DATA;
            end
            else begin
                state_n = state_c;
            end
        end

        DATA : begin
            if(data_idle) begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end
    endcase
end

assign  idle_rst   = state_c == IDLE && data_vld; 
assign  rst_data   = state_c == RST  && end_cnt_res;
assign  data_idle  = state_c == DATA  && end_cnt_num;


//****************************************************************
//--fifo
//****************************************************************
fifo	fifo_inst (
	.aclr ( ~rst_n ),
	.clock ( clk ),
	.data ( fifo_wr_data ),//GRB
	.rdreq ( fifo_rd_req ),
	.wrreq ( fifo_wr_req ),//GRB
	.empty ( fifo_empty ),
	.full ( fifo_full ),
	.q ( fifo_rd_data ),
	.usedw ( )
	);

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

assign fifo_rd_req = end_cnt_bit && ~fifo_empty;
//****************************************************************
//--                复位时间
//****************************************************************


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_res <= 'd0;
    end 
    else if(add_cnt_res)begin 
        if(end_cnt_res)begin 
            cnt_res <= 'd0;
        end
        else begin 
            cnt_res <= cnt_res + 1'b1;
        end 
    end
end 

assign add_cnt_res = state_c == RST;
assign end_cnt_res = add_cnt_res && cnt_res == MAX_RES - 1;

//****************************************************************
//--                数据传输时间
//****************************************************************

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_time <= 'd0;
    end 
    else if(add_cnt_time)begin 
        if(end_cnt_time)begin 
            cnt_time <= 'd0;
        end
        else begin 
            cnt_time <= cnt_time + 1'b1;
        end 
    end
end 

assign add_cnt_time = state_c == DATA;
assign end_cnt_time = add_cnt_time && cnt_time == 1200/20 - 1;



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

assign add_cnt_bit = end_cnt_time;
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 <= 'd0;
    end 
    else if(add_cnt_num)begin 
        if(end_cnt_num)begin 
            cnt_num <= '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;

//****************************************************************
//--            pwm输出
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        pwm_r <= 0;
    end
    else begin
        case (state_c)
            IDLE : begin
                pwm_r <= 0;
            end

            RST : begin
                pwm_r <= 0;
            end

            DATA : begin
                if(fifo_rd_data[23-cnt_bit] == 0) begin
                    if(cnt_time < T0H)begin
                        pwm_r <= 1;
                    end
                    else begin
                        pwm_r <= 0;
                    end
                end
                else if(fifo_rd_data[23-cnt_bit] == 1) begin
                    if(cnt_time < T1H)begin
                        pwm_r <= 1;
                    end
                    else begin
                        pwm_r <= 0;
                    end
                end
            end

            default  : pwm_r <= 0;
                
        endcase
    end
end

assign pwm = pwm_r;
assign ready = state_c == IDLE;

endmodule //led_control

数据控制模块:

verilog 复制代码
/*
 * @Description: 显示图片
 * @Author: Fu Yu
 * @Date: 2023-08-14 10:04:45
 * @LastEditTime: 2023-08-14 15:32:59
 * @LastEditors: Fu Yu
 */

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;	

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


wire        rom_rd_req      ;
wire        rom_rd_data_vld ;
reg         rom_rd_req1     ;
reg         rom_rd_req2     ;
/**************************************************************
                            状态机
**************************************************************/
    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;

//    assign pix_data_vld = add_x_cnt;

    // 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

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



assign rom_rd_req = state == DATA;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rom_rd_req1 <= 0 ;
        rom_rd_req2 <= 0 ;
    end
    else begin
        rom_rd_req1 <= rom_rd_req ;
        rom_rd_req2 <= rom_rd_req1 ;
    end
end

assign pix_data_vld = rom_rd_req2;


endmodule

2、动态显示

动态显示图片时,只需要改变数据控制模块,以及所需要显示的数据图片,

verilog 复制代码
/*
 * @Description: 动态显示图片
 * @Author: Fu Yu
 * @Date: 2023-08-14 15:34:55
 * @LastEditTime: 2023-08-14 17:21:36
 * @LastEditors: Fu Yu
 */

module ws2812_control_dynamic(
    input               clk             ,
    input               rst_n           ,
    output     [23:0]   pix_data        ,
    output              pix_data_vld    ,
    input               ready                   //可以接收图像数据了
);

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

    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			[24:0]	cnt_500ms	   	;
wire				add_cnt_500ms	;
wire				end_cnt_500ms	;

parameter MAX_500MS = 25'd24_999_999;

reg			[5:0]	cnt_offest	   	;
wire				add_cnt_offest	;
wire				end_cnt_offest	;

wire  [4:0] real_row;//0~31

wire        rom_rd_req      ;
wire        rom_rd_data_vld ;
reg         rom_rd_req1     ;
reg         rom_rd_req2     ;
/**************************************************************
                            状态机
**************************************************************/
    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_cnt_500ms)
                                    state <= IDLE;
                default :	state <= IDLE;
        endcase

//****************************************************************
//--500ms
//****************************************************************

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_500ms <= 'd0;
    end 
    else if(add_cnt_500ms)begin 
        if(end_cnt_500ms)begin 
            cnt_500ms <= 'd0;
        end
        else begin 
            cnt_500ms <= cnt_500ms + 1'b1;
        end 
    end
end 

assign add_cnt_500ms = state == DELAY;
assign end_cnt_500ms = add_cnt_500ms && cnt_500ms == MAX_500MS;



/**************************************************************
                        图像数据个数计数器
**************************************************************/       
    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_offest <= 'd0;
    end 
    else if(add_cnt_offest)begin 
        if(end_cnt_offest)begin 
            cnt_offest <= 'd0;
        end
        else begin 
            cnt_offest <= cnt_offest + 1'b1;
        end 
    end
end 

assign add_cnt_offest = end_cnt_500ms;
assign end_cnt_offest = add_cnt_offest && cnt_offest == 32 - 1;

assign real_row = cnt_x + cnt_offest;

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



assign rom_rd_req = state == DATA;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rom_rd_req1 <= 0 ;
        rom_rd_req2 <= 0 ;
    end
    else begin
        rom_rd_req1 <= rom_rd_req ;
        rom_rd_req2 <= rom_rd_req1 ;
    end
end

assign pix_data_vld = rom_rd_req2;


endmodule
相关推荐
乌恩大侠2 小时前
O-RAN Fronthual CU/Sync/Mgmt 平面和协议栈
5g·平面·fpga开发·架构
DS小龙哥15 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师1 天前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战1 天前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩2 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技2 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪2 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望2 天前
EDA技术简介
fpga开发
淘晶驰AK2 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发