文章目录
- 一、WS2812C-2020-V1
- 1、产品概述
- 2、引出端排列及功能
- 3、数据传输时间
- 4、数据传输方法
- 二、使用WS2812C显示图片
- 1、静态显示
- 2、动态显示
一、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上。
显示模块:
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
数据控制模块:
/*
* @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、动态显示
动态显示图片时,只需要改变数据控制模块,以及所需要显示的数据图片,
/*
* @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