文章目录
- 一.原理
- 二.设计
- 三.代码
- 注:本文续前文https://blog.csdn.net/weixin_67803687/article/details/132305311?spm=1001.2014.3001.5502后
一.原理
- 基本原理和静态显示一致,这里加入帧变换的概念
如图所示:第一帧为红色框,显示第一个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- 本项目完整代码详见https://download.csdn.net/download/weixin_67803687/88219021?spm=1001.2014.3001.5501