自动视力测试仪的设计与实现BUPT
在实物中有问题
修改:
总实现模块
module martrix_led_top(
input ext_clk_in,
input ext_rst_n ,
input [6:0]BTN,
output [7:0] row , // ������ѡͨ�ź�
output [7:0] r_col , // ������ѡͨ�ź�
output [7:0] g_col , // ������ѡͨ�ź�
output [7:0] disp_sel,
output [7:0] seg_sel
);
//wire define
wire clk_50m;
wire rst_n ; //��λ�ź�
assign rst_n = ~ext_rst_n ;
assign clk_50m = ext_clk_in ;
wire [6:0]BTN_pulse;
debounce debounce_inst0(
.clk(ext_clk_in),
.rst(rst_n),
.key(~BTN),
.key_pulse(BTN_pulse)
);
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 (BTN_pulse),
.disp_sel(disp_sel),
.seg_sel(seg_sel)
);
endmodule
消抖:
module debounce (clk,rst,key,key_pulse);
parameter N = 7; //要消除的按键的数量
input clk;
input rst;
input [N-1:0] key; //输入的按键
output [N-1:0] key_pulse; //按键动作产生的脉冲
reg [N-1:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值
reg [N-1:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值
wire [N-1:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲
//利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
always @(posedge clk or negedge rst)
begin
if (!rst) begin
key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
key_rst_pre <= key_rst; //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
end
end
assign key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
reg [19:0] cnt; //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器
//产生20ms延时,当检测到key_edge有效是计数器清零开始计数
always @(posedge clk or negedge rst)
begin
if(!rst)
cnt <= 20'h0;
else if(key_edge)
cnt <= 20'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; //延时后检测电平寄存器变量
reg [N-1:0] key_sec;
//延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec <= {N{1'b1}};
else if (cnt==20'hf4240)
key_sec <= key;
end
always @(posedge clk or negedge rst)
begin
if (!rst)
key_sec_pre <= {N{1'b1}};
else
key_sec_pre <= key_sec;
end
assign key_pulse = key_sec_pre & (~key_sec);
endmodule
数码管:
module seg_led(
input clk , // 时钟信号
input rst_n , // 复位信号
input [7:0] data , // 2位数码管要显示的数值
input en , // 数码管使能信号
output reg [7:0] seg_sel, // 数码管位选,最左侧数码管为最高位
output reg [7:0] seg_led // 数码管段选
);
//parameter define
parameter MAX_NUM = 16'd50000 ; // 对数码管驱动时钟(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;
end
//每当计数器对数码管驱动时钟计数时间达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
//cnt_sel从0计数到1,用于选择当前处于显示状态的数码管
always @ (posedge clk or negedge rst_n) 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 or negedge rst_n) begin
if(!rst_n) begin
seg_sel <= 8'b1111_1111; //位选信号低电平有效
num_disp <= 4'd0;
end
else begin
if(en) begin
case (cnt_sel)
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'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
//控制数码管段选信号,显示字符
/*
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
seg_led <= 8'h00;
else begin
case (num_disp)
4'd0 : seg_led <= 8'h3f; //显示数字 0
4'd1 : seg_led <= 8'h06; //显示数字 1
4'd2 : seg_led <= 8'h5b; //显示数字 2
4'd3 : seg_led <= 8'h4f; //显示数字 3
4'd4 : seg_led <= 8'h66; //显示数字 4
4'd5 : seg_led <= 8'h6d; //显示数字 5
4'd6 : seg_led <= 8'h7d; //显示数字 6
4'd7 : seg_led <= 8'h07; //显示数字 7
4'd8 : seg_led <= 8'h7f; //显示数字 8
4'd9 : seg_led <= 8'h6f; //显示数字 9
default:
seg_led <= 8'h00; //默认全灭
endcase
end
end*/
endmodule
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
);
//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 [1:0] fangxiang; //方向量
reg judge; //判断是否正确
reg [3:0] result[1:0]; //左右眼的最终结果
reg [0:0] start; //BTN0是否启动
reg [1:0] eye; //左右眼启动数据
//循环变量
integer i,j,x,y;
// 显示
initial
begin
//状态量初始化:状态3:0.8
state=4'b0011;
fangxiang=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 clk)
begin
if(BTN[0])
begin
start = ~start;
end
if(start)
begin
if(BTN[1])
begin
eye[0]=~eye[0];
end
if(BTN[2])
begin
eye[1]=~eye[1];
end
if(eye==2'b11)
begin
start=0;
end
end
else
begin
eye=2'b00;
end
end
//计数器对点阵刷新信号进行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@(posedge clk or negedge start or posedge BTN[1] or posedge BTN[2])
begin
if(!start)begin
state = 4'b0011;
state_temp = 4'b0011;
judge=0;
result[0] = 4'b1111;
result[1] = 4'b1111;
end
else if(BTN[2])begin
if(eye==2'b10)begin
//result[1] <= state;
if(state_temp==4'b0111&&state==4'b0111)result[1]<=4'b1000;
judge = 0;
state = 4'b0011;
state_temp = state;
end
else begin
result[1]<=result[1];
end
end
else if(BTN[1])begin
if(eye==2'b01)begin
//result[0] <= state;
if(state_temp==4'b0111&&state==4'b0111)result[0]<=4'b1000;
judge = 0;
state = 4'b0011;
state_temp = state;
end
else begin
result[0]<=result[0];
end
end
else if(state==4'b1111)begin
judge=1;
state=4'b0000;
state_temp=4'b0001;
end
else if(state==4'b1000)begin
judge=1;
state=4'b0111;
state_temp=4'b0111;
end
else if(BTN[6]&&judge==0)begin
if(fangxiang==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
end
else if(BTN[5]&&judge==0)begin
if(fangxiang==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
end
else if(BTN[4]&&judge==0)begin
if(fangxiang==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
end
else if(BTN[3]&&judge==0)begin
if(fangxiang==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
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
//载入随机数
wire [7:0] rand_num;
random_gen(
.clk (clk),
.rst_n (rst_n),
.rand_num (rand_num)
);
always@(posedge clk)
begin
if(BTN[1]+BTN[2]+BTN[3]+BTN[4]+BTN[5]+BTN[6]!=0)begin
if(judge==0)begin
fangxiang=rand_num;
end
end
else fangxiang=fangxiang;
end
//控制点阵的行显示信号,刷新到每一行时显示对应的数值
always @ (posedge clk or negedge judge) begin
if(!judge)begin
for(x=0;x<8;x=x+1)begin
disp_temp_temp[x]<= disp_temp[fangxiang][state][x];
end
end
else if (judge==1&&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_temp[y][x]<= disp_temp_temp[7-x][y];
end
end
end
end
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
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
case (row)
8'b1111_1110 : col_disp <= disp_temp_temp[7] ;
8'b1111_1101 : col_disp <= disp_temp_temp[6] ;
8'b1111_1011 : col_disp <= disp_temp_temp[5] ;
8'b1111_0111 : col_disp <= disp_temp_temp[4] ;
8'b1110_1111 : col_disp <= disp_temp_temp[3] ;
8'b1101_1111 : col_disp <= disp_temp_temp[2] ;
8'b1011_1111 : col_disp <= disp_temp_temp[1] ;
8'b0111_1111 : col_disp <= disp_temp_temp[0] ;
default :begin
col_disp <= 8'b0;
end
endcase
end
end
else begin
col_disp <= 8'b0;
end
end
end
seg_led(
.clk (clk), // 时钟信号
.rst_n (rst_n), // 复位信号
.data ({result[1],result[0]}), // 2位数码管要显示的数值
.en (1), // 数码管使能信号
.seg_sel(disp_sel), // 数码管位选,最左侧数码管为最高位
.seg_led(seg_sel) // 数码管段选
);
endmodule