目录
一、实验要求
二、实验设计
NO.1模块电路的设计
A、设计思路
B、具体实现
NO.2总实验的设计
A、设计思路
B、具体实现
- 随机数模块random_gen
- 数码管模块seg_led
- 消抖模块key_debounce
- 矩阵模块(含数码管)martrix_led
- 总实现模块
三、仿真设计
- 模块电路的仿真设计
- 总实验的仿真设计
四、一些问题
一、实验要求:
题目 : 自动视力测试仪的设计与实现
实验背景:
目前视力的普查多采用灯箱视力表,由专职医务人员利用指示棒作视标的随机指示,通过体检者能否判读出视标的开口方向,最终得知视力值。这种方式虽然简便易行,但是医务人员的劳动强度大,而且医护人员在检测中无意识的暗示也可能对体检者的检查结果造成干扰。本题目设计实现一个自动的视力测试模拟装置,能够使体检者单人完成整个视力检测过程,达到节省人力、减少人为因素对检测结果干扰等目的。
基本要求:
1.用 8×8 点阵显示的 0.1、0.2、0.4、0.6、0.8、1.0、1.2、1.5 视标如下图示意所示,视标的上、下、左、右四个开口方向随机生成;
2.BTN0 为启动键,按下后 8×8 点阵显示视力为 0.8 的视标(方向随机),同时数码管
DISP3~DISP2 显示与视标对应的视力;
3.BTN6~BTN3 分别对应视标“上”、“下”、“左”、“右”四个开口方向。按下某个方向键后,若选择正确,则进入高一级视标的显示,否则进入低一级视标的显示。数码管 DISP3~DISP2 实时显示与视标对应的视力;
4.测试的最高视力为 1.5,最低视力为 0.1。如果 0.1 视标的方向选择错误,点阵显示“X”图案,并记视力为 0.1- (DISP3~DISP1);
5.测试结束后数码管显示最终的视力测试结果,同时点阵根据视力的高低显示不同的表情图案;
6.重新按 BTN0 开始新一轮测试。
提高要求:
- 增加左右眼测试模式。左眼、右眼分别测试结束后,数码管同时显示左右眼的视力;
- 进行算法优化,实现快速测量;
- 自拟其他功能。
模块电路要求:
在 8×8 双色点阵上设计并实现视标图案显示,显示 0.8 视力的 4 个方向的视标图案,每秒切换一种,要求完成仿真并在实验板上下载显示。
二、实验设计
NO.1模块电路的设计
1、矩阵模块
module martrix_led(
input clk , // 时钟信号
input rst_n , // 复位信号
input en , // 点阵使能信号
output reg [7:0] row , // 点阵行选通信号
output wire [7:0] r_col, // 红色点阵列选通信号
output wire [7:0] g_col // 绿色点阵列选通信号
);
//parameter define
parameter MAX_NUM = 16'd50_000 ; // 对点阵刷行新信号(50MHz)计数1ms所需的计数值
parameter MAX_NUM_1 = 26'd50_000_000;
//reg define
reg [25:0] cnt1 ;
reg [15:0] cnt0 ; // 点阵行刷新计数器
reg [7:0] col_disp ; // 当前点阵显示的数据
reg [7:0] disp_temp[7:0]; // 创建显示缓存
// 显示 0.8
initial
begin
disp_temp[0] = 8'b0000_0000;
disp_temp[1] = 8'b0000_0000;
disp_temp[2] = 8'b0011_1100;
disp_temp[3] = 8'b0000_0100;
disp_temp[4] = 8'b0000_0100;
disp_temp[5] = 8'b0011_1100;
disp_temp[6] = 8'b0000_0000;
disp_temp[7] = 8'b0000_0000;
end
// 红色和绿色同时显示会组合为黄色
assign r_col = col_disp; // 默认显示红色点
assign g_col = 8'b0; // 绿色点不显示
//计数器对点阵刷新信号进行1ms计数,
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt0 <= 16'b0;
end
else if (cnt0 == MAX_NUM - 1'b1) begin
cnt0 <= 16'b0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
//计数器进行1s计数
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt1 <= 26'b0;
end
else if (cnt1 == MAX_NUM_1 - 1'b1) begin
cnt1 <= 26'b0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
//每一秒进行一次顺时针旋转
integer x,y;
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
disp_temp[0] = 8'b0000_0000;
disp_temp[1] = 8'b0000_0000;
disp_temp[2] = 8'b0011_1100;
disp_temp[3] = 8'b0000_0100;
disp_temp[4] = 8'b0000_0100;
disp_temp[5] = 8'b0011_1100;
disp_temp[6] = 8'b0000_0000;
disp_temp[7] = 8'b0000_0000;
end
else if (cnt1 == MAX_NUM_1 - 1'b1) begin
for(x=0;x<8;x=x+1)
begin
for(y=0;y<8;y=y+1)
begin
disp_temp[y][x]<=disp_temp[7-x][y];
end
end
end
end
//计时1ms时,切换点阵的行刷新信号
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0)
row <= 8'b0111_1111;
else if(cnt0 == MAX_NUM - 1'b1) begin
row <= {row[6:0],row[7]};
end
end
//控制点阵的行显示信号,刷新到每一行时显示对应的数值
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
col_disp <= 8'b0;
end
else begin
if(en) begin
case (row)
8'b1111_1110 : col_disp <= disp_temp[7] ;
8'b1111_1101 : col_disp <= disp_temp[6] ;
8'b1111_1011 : col_disp <= disp_temp[5] ;
8'b1111_0111 : col_disp <= disp_temp[4] ;
8'b1110_1111 : col_disp <= disp_temp[3] ;
8'b1101_1111 : col_disp <= disp_temp[2] ;
8'b1011_1111 : col_disp <= disp_temp[1] ;
8'b0111_1111 : col_disp <= disp_temp[0] ;
default :begin
col_disp <= 8'b0;
end
endcase
end
else begin
col_disp <= 8'b0;
end
end
end
endmodule
2、顶层模块:
module martrix_led_top(
input ext_clk_in,
input ext_rst_n ,
output [7:0] row ,
output [7:0] r_col ,
output [7:0] g_col
);
//wire define
wire clk_50m;
wire rst_n ;
assign rst_n = ~ext_rst_n ;
assign clk_50m = ext_clk_in ;
martrix_led martrix_led_inst0(
.clk (clk_50m),
.rst_n (rst_n ),
.en (1'b1 ),
.row (row ),
.r_col (r_col ),
.g_col (g_col )
);
endmodule
NO.2总实验的设计
A.设计思路
前言:此处直接将提高要求考虑在内一并设计
1.大小分组:
图中的图案按大小(此处记作state)分为八个,state[0]~state[7]分别对应1.5~0.1的图案
2.方向分组:
每个图案依照题意有四个:上下左右,此处以: [3:0]fangxiang代表:右,下,左,上(依次顺时针旋转)
3.左右眼:
设置一个变量:[1:0]eye,分别代表左右眼
4.开始后的状态设置:
A、未开机状态:矩阵、数码管都不亮
B、开机状态:矩阵显示正方形,边长为4,处于正中间;数码管DISP7~DISP5和DISP3~DISP1都显示0.0
C、测试状态:BTN6~BTN3分别代表上下左右按下代表选择,数码管实时更新
D、结束状态:当得到结果时数码管显示最终状态,矩阵显示该视力的旋转效果(即为结算画面),若是最差劲视力则显示叉号X
5、逻辑设计:
A、state逻辑:从0.8视力开始,每按错一次图案右移,按对一次图案左移,直到找到恰好适合当前视力的图案(即前后两次对错不同)
B、方向逻辑:载入八位随机数(实际上是伪随机数)rand_num,当开始后BTN1~BTN6每次按下,都将随机数赋值给fangxiang(只去后四位rand_num)
C、四个方向设置逻辑:利用循环设计四个方向
D、结果逻辑:每次按BTN时都将结果实时反馈给数码管,当结局确定时数码管锁死。此处采用变量[3:0]result[1:0]作为结果,同时采用judge作为判断是否结束的依据(0为未结束1为结束)
B、具体实现
1、随机数模块random_gen:
注:该模块中已经将种子(seed)在初始化中赋值,不采用从外确定是否载入随机数的方法(此处处理可以方便仿真)。
module random_gen(
input clk ,
input rst_n ,
//input load ,
//input [7:0] seed ,
output reg [7:0] rand_num
);
//初始化
initial
begin
rand_num=8'b10011101;
end
always@(posedge clk )begin
if(rst_n == 1'b0) begin
rand_num <= 8'b0;
end
/*else if(load == 1'b1)begin //载入随机数种子
rand_num <= seed;
end*/
else begin //进行反馈移位
rand_num[0] <= rand_num[7];
rand_num[1] <= rand_num[0];
rand_num[2] <= rand_num[1];
rand_num[3] <= rand_num[2];
rand_num[4] <= rand_num[3]^rand_num[7];
rand_num[5] <= rand_num[4]^rand_num[7];
rand_num[6] <= rand_num[5]^rand_num[7];
rand_num[7] <= rand_num[6];
end
end
endmodule
2、数码管模块
注:由于数码管只显示1.5~0.1,以及0.1-共9种情况,所以不必设计所有数字(但是要注意小数点)
module seg_led(
input clk , // 时钟信号
input rst_n , // 复位信号
input [7:0] data , // 2位数码管要显示的数值
input en , // 数码管使能信号
input start,
output reg [7:0] seg_sel, // 数码管位选,最左侧数码管为最高位
output reg [7:0] seg_led // 数码管段选
);
//初始化
initial
begin
seg_sel <= 8'b1111_1111;
seg_led <= 8'b0000_0000;
end
//parameter define
parameter MAX_NUM = 16'd50_000 ; // 对数码管驱动时钟(50MHz)计数1ms所需的计数值
// 1ms = 1000us = 1000_000ns
// 1000_000ns / 20ns = 50000
//reg define
reg [16:0] cnt0 ; // 数码管驱动时钟计数器
reg [7:0] cnt_sel ; // 数码管位选计数器
reg [3:0] num_disp ; // 当前数码管显示的数据
initial
begin
cnt_sel=8'b0000_0001;
cnt0=0;
num_disp=0;
end
//每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
always @ (posedge clk ) begin
if (rst_n == 1'b0) begin
cnt0 <= 16'b0;
end
else if (cnt0 == MAX_NUM - 1'b1) begin
cnt0 <= 16'b0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
//cnt_sel从0计数到1,用于选择当前处于显示状态的数码管
always @ (posedge clk ) begin
if (rst_n == 1'b0)
cnt_sel <= 8'b0;
else if(cnt0 == MAX_NUM - 1'b1) begin
cnt_sel <= {cnt_sel[6:0],cnt_sel[7]};
end
end
//控制数码管位选信号,使2位数码管轮流显示
always @ (posedge clk ) begin
if(rst_n == 1'b0) begin
seg_sel <= 8'b1111_1111; //位选信号低电平有效
num_disp <= 4'd0;
end
else if(start==0)
begin
seg_sel <= 8'b1111_1111; //位选信号低电平有效
num_disp <= 4'd0;
seg_led <= 8'h00;
end
else begin
if(en) begin
case (cnt_sel)
8'b0000_0001 :begin
seg_sel <= 8'b1111_1110; //显示右眼数码管第4位
case(data[3:0])
4'b1000: seg_led <= 8'h00;
default: seg_led <= 8'h00;
endcase
end
8'b0000_0010 :begin
seg_sel <= 8'b1111_1101; //显示右眼数码管第3位
case(data[3:0])
4'b1000: seg_led <= 8'h40;
default: seg_led <= 8'h00;
endcase
end
8'b0000_0100 :begin
seg_sel <= 8'b1111_1011; //显示右眼数码管第2位
case(data[3:0])
4'b0000: seg_led <= 8'h6d;
4'b0001: seg_led <= 8'h5b;
4'b0011: seg_led <= 8'h7f;
4'b0100: seg_led <= 8'h7d;
4'b0101: seg_led <= 8'h66;
4'b0110: seg_led <= 8'h5b;
4'b0111: seg_led <= 8'h06;
4'b1000: seg_led <= 8'h06;
default: seg_led <= 8'h3f;
endcase
end
8'b0000_1000 :begin
seg_sel <= 8'b1111_0111; //显示右眼数码管第1位
case(data[3:0])
4'b0000: seg_led <= 8'h86;
4'b0001: seg_led <= 8'h86;
4'b0010: seg_led <= 8'h86;
default: seg_led <= 8'hBf;
endcase
end
8'b0001_0000 :begin
seg_sel <= 8'b1110_1111; //显示左眼数码管第4位
case(data[7:4])
4'b1000: seg_led <= 8'h00;
default: seg_led <= 8'h00;
endcase
end
8'b0010_0000 :begin
seg_sel <= 8'b1101_1111; //显示左眼数码管第3位
case(data[7:4])
4'b1000: seg_led <= 8'h40;
default: seg_led <= 8'h00;
endcase
end
8'b0100_0000 :begin
seg_sel <= 8'b1011_1111; //显示左眼数码管第2位
case(data[7:4])
4'b0000: seg_led <= 8'h6d;
4'b0001: seg_led <= 8'h5b;
4'b0011: seg_led <= 8'h7f;
4'b0100: seg_led <= 8'h7d;
4'b0101: seg_led <= 8'h66;
4'b0110: seg_led <= 8'h5b;
4'b0111: seg_led <= 8'h06;
4'b1000: seg_led <= 8'h06;
default: seg_led <= 8'h3f;
endcase
end
8'b1000_0000 :begin
seg_sel <= 8'b0111_1111; //显示左眼数码管第1位
case(data[7:4])
4'b0000: seg_led <= 8'h86;
4'b0001: seg_led <= 8'h86;
4'b0010: seg_led <= 8'h86;
default: seg_led <= 8'hBf;
endcase
end
default: seg_led <= 8'h00;
endcase
end
else begin
seg_sel <= 8'b1111_1111; //使能信号为0时,所有数码管均不显示
num_disp <= 4'b0;
end
end
end
endmodule
3、消抖模块key_debounce
注:此处修改过的多按键消抖中,key_flag不具有实际意义
module key_debounce(sys_clk,sys_rst_n,key,key_flag,key_value);
parameter N=7;
parameter delay_cnt = 32'd1000000; //定义按键计数器初值 20ns X 1000000 = 20ms
input sys_clk; //外部50M时钟
input sys_rst_n; //外部复位信号,低有效
input [N-1:0]key; //外部按键输入
output reg [N-1:0]key_flag; //按键数据有效信号
output reg [N-1:0]key_value; //按键消抖后的数据
initial
begin
key_value={N{1'b0}};
end
//reg define
reg [31:0] key_cnt;
reg [N-1:0]key_reg;
//*****************************************************
//** 时序逻辑,寄存按键引脚的值
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
key_reg <= {N{1'b0}};
end
else begin
key_reg <= key;
end
end
//*****************************************************
//** 时序逻辑,检测到按键变化稳定后开始倒计时
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
key_cnt <= 32'd0;
end
else begin
if(key_reg != key) //一旦检测到按键状态发生变化(有按键被按下或释放)
key_cnt <= delay_cnt; //给延时计数器重新装载初始值(计数时间为20ms)
else if(key_reg == key) begin //在按键状态稳定时,计数器递减,开始20ms倒计时
if(key_cnt > 32'd0)
key_cnt <= key_cnt - 1'b1;
else
key_cnt <= key_cnt;
end
end
end
//*****************************************************
//** 时序逻辑,倒计时结束,输出按键触发标志
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
key_flag <= {N{1'b0}};
key_value <= {N{1'b0}};
end
else begin
if(key_cnt == 32'd1) begin //当计数器递减到1时,说明按键稳定状态维持了20ms
key_flag <= {N{1'b1}}; //此时消抖过程结束,给出一个时钟周期的标志信号
key_value <= key; //并寄存此时按键的值
end
else begin
key_flag <= {N{1'b0}};
key_value <= key_value;
end
end
end
endmodule
4、矩阵模块(含数码管)martrix_led
注:此处结算动画为当前视力图案顺时针转圈
module martrix_led(
input clk , // 时钟信号
input rst_n , // 复位信号
input en , // 点阵使能信号
input [6:0] BTN , //七个BTN
output reg [7:0] row , // 点阵行选通信号
output wire [7:0] r_col, // 红色点阵列选通信号
output wire [7:0] g_col, // 绿色点阵列选通信号
output [7:0] disp_sel,
output [7:0] seg_sel,
output [3:0] state_out,
output [1:0] fangxiang_out,
output [1:0] eye_out,
output start_out,
output judge_out
);
//parameter define
parameter MAX_NUM = 16'd50_000 ; // 对点阵刷行新信号(50MHz)计数1ms所需的计数值
parameter MAX_NUM_1 = 26'd50_000_000;
//reg define
reg [25:0] cnt1 ; //结算动画计数器
reg [15:0] cnt0 ; // 点阵行刷新计数器
reg [7:0] col_disp ; // 当前点阵显示的数据
reg [7:0] disp_temp[3:0][7:0][7:0]; // 创建显示缓存
reg [7:0] disp_temp_temp[7:0] ; // 创建最终结果显示缓存
reg [3:0] state; //状态量
reg [3:0] state_temp; //前一个状态
reg [3:0] fangxiang_pre;
reg [1:0] fangxiang; //方向量
reg judge; //判断是否正确
reg [3:0] result[1:0]; //左右眼的最终结果
reg start; //BTN0是否启动
reg [1:0] eye; //左右眼启动数据
//循环变量
integer i,j,x,y;
// 初始化
initial
begin
//状态量初始化:状态3:0.8
state=4'b0011;
fangxiang=2'b00;
fangxiang_pre=2'b00;
//启动
start=0;
eye=2'b00;
//判断
judge=0;
//1.5
disp_temp[0][0][0] = 8'b0000_0000;
disp_temp[0][0][1] = 8'b0000_0000;
disp_temp[0][0][2] = 8'b0001_1000;
disp_temp[0][0][3] = 8'b0000_1000;
disp_temp[0][0][4] = 8'b0001_1000;
disp_temp[0][0][5] = 8'b0000_0000;
disp_temp[0][0][6] = 8'b0000_0000;
disp_temp[0][0][7] = 8'b0000_0000;
//1.2
disp_temp[0][1][0] = 8'b0000_0000;
disp_temp[0][1][1] = 8'b0000_0000;
disp_temp[0][1][2] = 8'b0001_1100;
disp_temp[0][1][3] = 8'b0000_0100;
disp_temp[0][1][4] = 8'b0001_1100;
disp_temp[0][1][5] = 8'b0000_0000;
disp_temp[0][1][6] = 8'b0000_0000;
disp_temp[0][1][7] = 8'b0000_0000;
//1.0
disp_temp[0][2][0] = 8'b0000_0000;
disp_temp[0][2][1] = 8'b0000_0000;
disp_temp[0][2][2] = 8'b0001_1100;
disp_temp[0][2][3] = 8'b0000_0100;
disp_temp[0][2][4] = 8'b0000_0100;
disp_temp[0][2][5] = 8'b0001_1100;
disp_temp[0][2][6] = 8'b0000_0000;
disp_temp[0][2][7] = 8'b0000_0000;
//0.8
disp_temp[0][3][0] = 8'b0000_0000;
disp_temp[0][3][1] = 8'b0000_0000;
disp_temp[0][3][2] = 8'b0011_1100;
disp_temp[0][3][3] = 8'b0000_0100;
disp_temp[0][3][4] = 8'b0000_0100;
disp_temp[0][3][5] = 8'b0011_1100;
disp_temp[0][3][6] = 8'b0000_0000;
disp_temp[0][3][7] = 8'b0000_0000;
//0.6
disp_temp[0][4][0] = 8'b0000_0000;
disp_temp[0][4][1] = 8'b0011_1110;
disp_temp[0][4][2] = 8'b0000_0010;
disp_temp[0][4][3] = 8'b0000_0010;
disp_temp[0][4][4] = 8'b0000_0010;
disp_temp[0][4][5] = 8'b0011_1110;
disp_temp[0][4][6] = 8'b0000_0000;
disp_temp[0][4][7] = 8'b0000_0000;
//0.4
disp_temp[0][5][0] = 8'b0000_0000;
disp_temp[0][5][1] = 8'b0011_1110;
disp_temp[0][5][2] = 8'b0000_0010;
disp_temp[0][5][3] = 8'b0000_0010;
disp_temp[0][5][4] = 8'b0000_0010;
disp_temp[0][5][5] = 8'b0000_0010;
disp_temp[0][5][6] = 8'b0011_1110;
disp_temp[0][5][7] = 8'b0000_0000;
//0.2
disp_temp[0][6][0] = 8'b0000_0000;
disp_temp[0][6][1] = 8'b0111_1110;
disp_temp[0][6][2] = 8'b0000_0010;
disp_temp[0][6][3] = 8'b0000_0010;
disp_temp[0][6][4] = 8'b0000_0010;
disp_temp[0][6][5] = 8'b0000_0010;
disp_temp[0][6][6] = 8'b0000_0010;
disp_temp[0][6][7] = 8'b0111_1110;
//0.1
disp_temp[0][7][0] = 8'b1111_1111;
disp_temp[0][7][1] = 8'b0000_0001;
disp_temp[0][7][2] = 8'b0000_0001;
disp_temp[0][7][3] = 8'b0000_0001;
disp_temp[0][7][4] = 8'b0000_0001;
disp_temp[0][7][5] = 8'b0000_0001;
disp_temp[0][7][6] = 8'b0000_0001;
disp_temp[0][7][7] = 8'b1111_1111;
//顺时针旋转逻辑:四个方向都有了
for(i=1;i<4;i=i+1)
begin
for(j=0;j<8;j=j+1)
begin
for(x=0;x<8;x=x+1)
begin
for(y=0;y<8;y=y+1)
begin
disp_temp[i][j][y][x]=disp_temp[i-1][j][7-x][y];
end
end
end
end
end
// 红色和绿色同时显示会组合为黄色
assign r_col = col_disp; // 默认显示红色点
assign g_col = 8'b0; // 绿色点不显示
//启动与否以及左右眼的选择
always@(posedge BTN[2] or posedge BTN[1] or negedge start)
begin
if(!start)
begin
eye=2'b00;
end
else if(BTN[1])
begin
eye[0]=~eye[0];
end
else if(BTN[2])
begin
eye[1]=~eye[1];
end
end
always@(posedge BTN[0] or posedge eye[0] or posedge eye[1])
begin
if(BTN[0]==1'b1)
begin
start = ~start;
end
else if(eye[0]==1)
begin
if(eye[1]==1)
start=0;
end
end
assign eye_out=eye;
assign start_out=start;
//计数器对点阵刷新信号进行1ms计数,
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt0 <= 16'b0;
end
else if (cnt0 == MAX_NUM - 1'b1) begin
cnt0 <= 16'b0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
//计数器进行1s计数
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt1 <= 26'b0;
end
else if (cnt1 == MAX_NUM_1 - 1'b1) begin
cnt1 <= 26'b0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
//计时1ms时,切换点阵的行刷新信号
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0)
row <= 8'b0111_1111;
else if(cnt0 == MAX_NUM - 1'b1) begin
row <= {row[6:0],row[7]};
end
end
//结果赋值
always@(negedge start or posedge clk or posedge BTN[1] or posedge BTN[2])
begin
if(!start)begin
result[0] = 4'b1111;
result[1] = 4'b1111;
end
else if(BTN[2])begin
if(eye==2'b10)begin
if(state_temp==4'b0111&&state==4'b0111)result[1]<=4'b1000;
end
end
else if(BTN[1])begin
if(eye==2'b01)begin
if(state_temp==4'b0111&&state==4'b0111)result[0]<=4'b1000;
end
end
else if(eye==2'b10)begin
result[1]<=state;
if(state_temp==4'b0111&&state==4'b0111)result[1]<=4'b1000;
end
else if(eye==2'b01)begin
result[0]<=state;
if(state_temp==4'b0111&&state==4'b0111)result[0]<=4'b1000;
end
end
//状态改变:(判断)
always@(negedge start or posedge BTN[3] or posedge BTN[4] or posedge BTN[5] or posedge BTN[6])
begin
if(!start)begin
state = 4'b0011;
state_temp = 4'b0011;
judge=0;
end
else if(BTN[6])
begin
if(judge==0)
begin
if(fangxiang_pre==2'b11)begin
state=state-1;
if(state==state_temp)begin
judge=1;
state=state+1;
end
else state_temp=state+1;
end
else begin
state=state+1;
if(state==state_temp)judge=1;
else state_temp=state-1;
end
if(state==4'b1111)begin
judge=1;
state=4'b0000;
state_temp=4'b0001;
end
if(state==4'b1000)begin
judge=1;
state=4'b0111;
state_temp=4'b0111;
end
end
end
else if(BTN[5])begin
if(judge==0)
begin
if(fangxiang_pre==2'b01)begin
state=state-1;
if(state==state_temp)begin
judge=1;
state=state+1;
end
else state_temp=state+1;
end
else begin
state=state+1;
if(state==state_temp)judge=1;
else state_temp=state-1;
end
if(state==4'b1111)begin
judge=1;
state=4'b0000;
state_temp=4'b0001;
end
if(state==4'b1000)begin
judge=1;
state=4'b0111;
state_temp=4'b0111;
end
end
end
else if(BTN[4])begin
if(judge==0)
begin
if(fangxiang_pre==2'b10)begin
state=state-1;
if(state==state_temp)begin
judge=1;
state=state+1;
end
else state_temp=state+1;
end
else begin
state=state+1;
if(state==state_temp)judge=1;
else state_temp=state-1;
end
if(state==4'b1111)begin
judge=1;
state=4'b0000;
state_temp=4'b0001;
end
if(state==4'b1000)begin
judge=1;
state=4'b0111;
state_temp=4'b0111;
end
end
end
else if(BTN[3])begin
if(judge==0)
begin
if(fangxiang_pre==2'b00)begin
state=state-1;
if(state==state_temp)begin
judge=1;
state=state+1;
end
else state_temp=state+1;
end
else begin
state=state+1;
if(state==state_temp)judge=1;
else state_temp=state-1;
end
if(state==4'b1111)begin
judge=1;
state=4'b0000;
state_temp=4'b0001;
end
if(state==4'b1000)begin
judge=1;
state=4'b0111;
state_temp=4'b0111;
end
end
end
end
assign state_out=state;
assign judge_out=judge;
//载入随机数
wire [7:0] rand_num;
random_gen random_gen_inst0(
.clk (clk),
.rst_n (1),
.rand_num (rand_num)
);
always@(posedge BTN[1] or posedge BTN[2] or posedge BTN[3] or posedge BTN[4] or posedge BTN[5] or posedge BTN[6] )
begin
if(BTN[1]|BTN[2]|BTN[3]|BTN[4]|BTN[5]|BTN[6])begin
if(judge==0)begin
fangxiang_pre=fangxiang;
fangxiang=rand_num;
end
else if(judge==1)
begin
fangxiang_pre=fangxiang_pre;
fangxiang=fangxiang;
end
end
end
assign fangxiang_out=fangxiang;
reg [1:0]n;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
col_disp <= 8'b0;
end
else if(en)begin
if(start) begin
//开始初状态
if(eye==2'b00)
begin
case (row)
8'b1111_1110 : col_disp <= 8'b0000_0000 ;
8'b1111_1101 : col_disp <= 8'b0000_0000 ;
8'b1111_1011 : col_disp <= 8'b0011_1100 ;
8'b1111_0111 : col_disp <= 8'b0010_0100 ;
8'b1110_1111 : col_disp <= 8'b0010_0100 ;
8'b1101_1111 : col_disp <= 8'b0011_1100 ;
8'b1011_1111 : col_disp <= 8'b0000_0000 ;
8'b0111_1111 : col_disp <= 8'b0000_0000 ;
default :begin
col_disp <= 8'b0;
end
endcase
end
//视力最差结果:叉号
else if(state==4'b0111&&state_temp==4'b0111)
begin
case (row)
8'b1111_1110 : col_disp <= 8'b1000_0001 ;
8'b1111_1101 : col_disp <= 8'b0100_0010 ;
8'b1111_1011 : col_disp <= 8'b0010_0100 ;
8'b1111_0111 : col_disp <= 8'b0001_1000 ;
8'b1110_1111 : col_disp <= 8'b0001_1000 ;
8'b1101_1111 : col_disp <= 8'b0010_0100 ;
8'b1011_1111 : col_disp <= 8'b0100_0010 ;
8'b0111_1111 : col_disp <= 8'b1000_0001 ;
default :begin
col_disp <= 8'b0;
end
endcase
end
//测试中:
else if(judge==0)
begin
n=0;
case (row)
8'b1111_1110 : col_disp <= disp_temp[fangxiang][state][7] ;
8'b1111_1101 : col_disp <= disp_temp[fangxiang][state][6] ;
8'b1111_1011 : col_disp <= disp_temp[fangxiang][state][5] ;
8'b1111_0111 : col_disp <= disp_temp[fangxiang][state][4] ;
8'b1110_1111 : col_disp <= disp_temp[fangxiang][state][3] ;
8'b1101_1111 : col_disp <= disp_temp[fangxiang][state][2] ;
8'b1011_1111 : col_disp <= disp_temp[fangxiang][state][1] ;
8'b0111_1111 : col_disp <= disp_temp[fangxiang][state][0] ;
default :begin
col_disp <= 8'b0;
end
endcase
end
//最终结果(非最差):转圈圈:
else
begin
if(cnt1 == MAX_NUM_1 - 1'b1)n=(n+1)%4;
case (row)
8'b1111_1110 : col_disp <= disp_temp[(fangxiang+n)%4][state][7] ;
8'b1111_1101 : col_disp <= disp_temp[(fangxiang+n)%4][state][6] ;
8'b1111_1011 : col_disp <= disp_temp[(fangxiang+n)%4][state][5] ;
8'b1111_0111 : col_disp <= disp_temp[(fangxiang+n)%4][state][4] ;
8'b1110_1111 : col_disp <= disp_temp[(fangxiang+n)%4][state][3] ;
8'b1101_1111 : col_disp <= disp_temp[(fangxiang+n)%4][state][2] ;
8'b1011_1111 : col_disp <= disp_temp[(fangxiang+n)%4][state][1] ;
8'b0111_1111 : col_disp <= disp_temp[(fangxiang+n)%4][state][0] ;
default :begin
col_disp <= 8'b0;
end
endcase
end
end
else begin
col_disp <= 8'b0;
end
end
end
seg_led seg_led_inst0(
.clk (clk), // 时钟信号
.rst_n (1), // 复位信号
.data ({result[1],result[0]}), // 2位数码管要显示的数值
.en (1), // 数码管使能信号
.start (start),
.seg_sel(disp_sel), // 数码管位选,最左侧数码管为最高位
.seg_led(seg_sel) // 数码管段选
);
endmodule
5、总实现模块
注:此处的:
output [1:0] fangxiang_out,
output [3:0] state_out,
output [1:0] eye_out,
output start_out
output judge_out
仅是为了在仿真时可以观察到输出状态,在板上并无实际物理意义
module martrix_led_top(
input ext_clk_in,
input ext_rst_n ,
input [6:0]BTN,
output [6:0] key_value,
output [7:0] row ,
output [7:0] r_col ,
output [7:0] g_col ,
output [7:0] disp_sel,
output [7:0] seg_sel,
output [1:0] fangxiang_out,
output [3:0] state_out,
output [1:0] eye_out,
output start_out,
output judge_out
);
//wire define
wire clk_50m;
wire rst_n ;
assign rst_n = ~ext_rst_n ;
assign clk_50m = ext_clk_in ;
key_debounce key_debounce_inst0(
.sys_clk(clk_50m), //外部50M时钟
.sys_rst_n(rst_n), //外部复位信号,低有效
.key(BTN), //外部按键输入
.key_flag(), //按键数据有效信号
.key_value(key_value) //按键消抖后的数据
);
martrix_led martrix_led_inst0(
.clk (clk_50m),
.rst_n (rst_n ),
.en (1'b1 ),
.row (row ),
.r_col (r_col ),
.g_col (g_col ),
.BTN (key_value),
.disp_sel(disp_sel),
.seg_sel(seg_sel),
.fangxiang_out(fangxiang_out),
.state_out(state_out),
.eye_out(eye_out),
.start_out(start_out),
.judge_out(judge_out)
);
endmodule
管脚:
BTN[6] | Input | PIN_123 |
BTN[5] | Input | PIN_122 |
BTN[4] | Input | PIN_121 |
BTN[3] | Input | PIN_91 |
BTN[2] | Input | PIN_89 |
BTN[1] | Input | PIN_20 |
BTN[0] | Input | PIN_61 |
disp_sel[7] | Output | PIN_31 |
disp_sel[6] | Output | PIN_30 |
disp_sel[5] | Output | PIN_70 |
disp_sel[4] | Output | PIN_69 |
disp_sel[3] | Output | PIN_68 |
disp_sel[2] | Output | PIN_67 |
disp_sel[1] | Output | PIN_66 |
disp_sel[0] | Output | PIN_63 |
ext_clk_in | Input | PIN_18 |
ext_rst_n | Input | PIN_124 |
eye_out[1] | Output | |
eye_out[0] | Output | |
fangxiang_out[1] | Output | |
fangxiang_out[0] | Output | |
g_col[7] | Output | PIN_38 |
g_col[6] | Output | PIN_39 |
g_col[5] | Output | PIN_40 |
g_col[4] | Output | PIN_41 |
g_col[3] | Output | PIN_42 |
g_col[2] | Output | PIN_43 |
g_col[1] | Output | PIN_44 |
g_col[0] | Output | PIN_45 |
judge_out | Output | |
key_value[6] | Output | |
key_value[5] | Output | |
key_value[4] | Output | |
key_value[3] | Output | |
key_value[2] | Output | |
key_value[1] | Output | |
key_value[0] | Output | |
r_col[7] | Output | PIN_11 |
r_col[6] | Output | PIN_12 |
r_col[5] | Output | PIN_13 |
r_col[4] | Output | PIN_14 |
r_col[3] | Output | PIN_15 |
r_col[2] | Output | PIN_16 |
r_col[1] | Output | PIN_21 |
r_col[0] | Output | PIN_22 |
row[7] | Output | PIN_1 |
row[6] | Output | PIN_2 |
row[5] | Output | PIN_3 |
row[4] | Output | PIN_4 |
row[3] | Output | PIN_5 |
row[2] | Output | PIN_6 |
row[1] | Output | PIN_7 |
row[0] | Output | PIN_8 |
seg_sel[7] | Output | PIN_51 |
seg_sel[6] | Output | PIN_52 |
seg_sel[5] | Output | PIN_53 |
seg_sel[4] | Output | PIN_55 |
seg_sel[3] | Output | PIN_57 |
seg_sel[2] | Output | PIN_58 |
seg_sel[1] | Output | PIN_59 |
seg_sel[0] | Output | PIN_62 |
start_out | Output | |
state_out[3] | Output | |
state_out[2] | Output | |
state_out[1] | Output | |
state_out[0] | Output |
-
三、仿真设计
1、模块电路的仿真设计
`timescale 1 ns/ 1 ps
module tb_top();
//reg define
reg ext_clk_in;
reg ext_rst_n ;
wire [7:0] row ;
wire [7:0] r_col ;
defparam DUT.martrix_led_inst0.MAX_NUM = 16'd5;
defparam DUT.martrix_led_inst0.MAX_NUM_1 = 26'd5_000;
initial begin
ext_clk_in = 1'b0;
ext_rst_n = 1'b1;
#40
ext_rst_n = 1'b0;
#250_000_000
$finish();
end
//100Mhz
initial begin
forever begin
#10 ext_clk_in = ~ext_clk_in;
end
end
martrix_led_top DUT(
.ext_clk_in(ext_clk_in),
.ext_rst_n (ext_rst_n ),
.row (row ),
.r_col (r_col ),
.g_col ( )
);
endmodule
仿真图:
截取一段展示:
当处于这一段时:
图案:(实际点阵与图中的左右相反,实际中0在左,而仿真中0在右)
处于这一段时:
图案:
2、总实验的仿真设计
总体框架:如下:
`timescale 1 ns/ 1 ps
module tb_top();
//reg define
reg ext_clk_in;
reg ext_rst_n ;
reg [6:0]BTN;
wire [6:0] key_value ;
wire [0:0] start_out ;
wire judge ;
wire [1:0] eye_out ;
wire [3:0] state_out ;
wire [1:0] fangxiang ;
wire [7:0] row ;
wire [7:0] r_col ;
wire [7:0] disp_sel ;
wire [7:0] seg_sel ;
defparam DUT.martrix_led_inst0.MAX_NUM = 16'd5;
defparam DUT.martrix_led_inst0.MAX_NUM_1 = 26'd40;
defparam DUT.martrix_led_inst0.seg_led_inst0.MAX_NUM = 26'd5;
defparam DUT.key_debounce_inst0.delay_cnt = 32'd5;
initial begin
ext_clk_in = 1'b0;
ext_rst_n = 1'b1;
BTN=7'b000_0000; #100
ext_rst_n = 1'b0;#100
***********************************
//在此处写入代码(BTN键)
***********************************
$finish();
end
//100Mhz
initial begin
forever begin
#10 ext_clk_in = ~ext_clk_in;
end
end
martrix_led_top DUT(
.ext_clk_in(ext_clk_in),
.ext_rst_n (ext_rst_n ),
.BTN (BTN ),
.key_value(key_value),
.start_out(start_out),
.eye_out(eye_out),
.state_out(state_out),
.fangxiang_out (fangxiang ),
.row (row ),
.r_col (r_col ),
.disp_sel (disp_sel ),
.seg_sel (seg_sel ),
.judge_out (judge )
);
endmodule
其中,折叠部分填写预设的BTN按键值:
当BTN分别按下:BTN0(开始),BTN1(右眼),BTN2(左眼)时,因为不兼容导致关闭,然后按下BTN0,BTN1,然后一直按错到0.1-时,显示X和0.1-:
输入折叠部分程序:
//NO.1 左右都按则归零
BTN[0]=1;#120
BTN[0]=0;#1480
BTN[1]=1;#120
BTN[1]=0;#2000
BTN[2]=1;#120
BTN[2]=0;#2000
//NO.2 一直按错到0.1-
BTN[0]=1;#120
BTN[0]=0;#2000
BTN[1]=1;#120
BTN[1]=0;#2000
BTN[3]=1;#120
BTN[3]=0;#2000
BTN[4]=1;#120
BTN[4]=0;#2000
BTN[4]=1;#120
BTN[4]=0;#2000
BTN[3]=1;#120
BTN[3]=0;#2000
BTN[4]=1;#120
BTN[4]=0;#2000
//后面再继续按将不变
BTN[3]=1;#120
BTN[3]=0;#2000
仿真图:
可以看到显示X,以及数码管(下面两行)显示值为:0.0,0.1-(左眼未测试,所以是0.0)
四、一些问题
实物班子可能不好使,修改代码见网址: