一.原理
- 基本原理和静态显示一致,这里加入帧变换的概念
如图所示:第一帧为红色框,显示第一个8 * 8的图像,然后向右平移,显示绿色框内的第二个8 * 8的图像…到最后显示紫色框内最后一个8 * 8的图像。可以设置帧与帧之间延时500ms,在视觉上就得到了滚动的状态
二.设计
-
1.在静态基础上加入帧计数器
-
2.加入一个状态,在此状态计数500ms,跳转到显示下一帧
-
3.rom核中地址值的考量,(横坐标+帧数)取余32+纵坐标乘32
-
4.rom核的数据深度需要调整,不然会溢出,影响最后显示
三.代码
- ws2812_drive.v不做修改,只修改ws2812_ctrl.v
/**************************************功能介绍***********************************
Date : 2023年8月14日14:24:59
Author : Alegg xy.
Version : 1.0
Description: 动态显示
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module ws2812_ctrl3(
input clk ,
input rst_n ,
input ready ,
output [23:0] pix_data,
output pix_data_vld
);
//---------<参数定义>---------------------------------------------------------
localparam IDLE = 'd0,
DATA = 'd1,
DONE = 'd2,
WAIT = 'd3;
parameter MAX_500ms = 25'd25_000_000;
parameter MAX_shifting = 6'd32;
//---------<内部信号定义>-----------------------------------------------------
reg [2:0] cstate ;//现态
reg [2:0] nstate ;//次态
reg [5:0] cnt_x ;
wire add_cnt_x ;
wire end_cnt_x ;
reg [5:0] cnt_y ;
wire add_cnt_y ;
wire end_cnt_y ;
reg [24:0] cnt_500ms ;
wire add_cnt_500ms ;
wire end_cnt_500ms ;
reg [5:0] cnt_shift ;
wire add_cnt_shift ;
wire end_cnt_shift ;
//计偏移量
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_shift <= 'd0;
end
else if(add_cnt_shift)begin
if(end_cnt_shift)begin
cnt_shift <= 'd0;
end
else begin
cnt_shift <= cnt_shift + 1'd1;
end
end
end
assign add_cnt_shift = cstate == DONE;
assign end_cnt_shift = add_cnt_shift && cnt_shift == MAX_shifting - 1'd1;
//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'd1;
end
end
end
assign add_cnt_500ms = cstate == WAIT;
assign end_cnt_500ms = add_cnt_500ms && cnt_500ms == MAX_500ms - 1'd1;
//rom
wire rom_rd_data_vld;
wire rom_rd_req;
reg rom_rd_req_r1;//打一拍
reg rom_rd_req_r2;//打两拍
//横坐标计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_x <= 'd0;
end
else if(add_cnt_x)begin
if(end_cnt_x)begin
cnt_x <= 'd0;
end
else begin
cnt_x <= cnt_x + 1'd1;
end
end
end
assign add_cnt_x = cstate == DATA;
assign end_cnt_x = add_cnt_x && cnt_x == 7;
//纵坐标计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_y <= 'd0;
end
else if(add_cnt_y)begin
if(end_cnt_y)begin
cnt_y <= 'd0;
end
else begin
cnt_y <= cnt_y + 1'd1;
end
end
end
assign add_cnt_y = end_cnt_x;
assign end_cnt_y = add_cnt_y && cnt_y == 7;
//第一段:时序逻辑描述状态转移
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 (ready) begin
nstate = DATA;
end
else begin
nstate = cstate;
end
end
DATA:begin
if (end_cnt_y) begin
nstate = DONE;
end
else begin
nstate = cstate;
end
end
DONE:begin
if (1) begin
nstate = WAIT;
end
else begin
nstate = cstate;
end
end
WAIT :begin
if (end_cnt_500ms) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = IDLE;
endcase
end
//rom例化
rom1 rom1_inst (
.aclr ( ~rst_n ),
.address ( (cnt_x + cnt_shift) % 32 + 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 <= 'd0;
rom_rd_req_r2 <= 'd0;
end
else begin
rom_rd_req_r1 <= rom_rd_req;
rom_rd_req_r2 <= rom_rd_req_r1;
end
end
assign rom_rd_req = cstate == DATA;
assign rom_rd_data_vld = rom_rd_req_r2;
assign pix_data_vld = rom_rd_data_vld;
endmodule