实验报告:自动视力测试仪的设计与实现BUPT

目录

一、实验要求

二、实验设计

NO.1模块电路的设计

A、设计思路

B、具体实现

NO.2总实验的设计

A、设计思路

B、具体实现

  1. 随机数模块random_gen
  2. 数码管模块seg_led
  3. 消抖模块key_debounce
  4. 矩阵模块(含数码管)martrix_led
  5. 总实现模块

三、仿真设计

  1. 模块电路的仿真设计
  2. 总实验的仿真设计

四、一些问题

一、实验要求:

题目 :  自动视力测试仪的设计与实现

实验背景:

目前视力的普查多采用灯箱视力表,由专职医务人员利用指示棒作视标的随机指示,通过体检者能否判读出视标的开口方向,最终得知视力值。这种方式虽然简便易行,但是医务人员的劳动强度大,而且医护人员在检测中无意识的暗示也可能对体检者的检查结果造成干扰。本题目设计实现一个自动的视力测试模拟装置,能够使体检者单人完成整个视力检测过程,达到节省人力、减少人为因素对检测结果干扰等目的。

基本要求:

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 开始新一轮测试。

提高要求:

  1. 增加左右眼测试模式。左眼、右眼分别测试结束后,数码管同时显示左右眼的视力; 
  2. 进行算法优化,实现快速测量;
  3. 自拟其他功能。

模块电路要求:

 在 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]InputPIN_123
BTN[5]InputPIN_122
BTN[4]InputPIN_121
BTN[3]InputPIN_91
BTN[2]InputPIN_89
BTN[1]InputPIN_20
BTN[0]InputPIN_61
disp_sel[7]OutputPIN_31
disp_sel[6]OutputPIN_30
disp_sel[5]OutputPIN_70
disp_sel[4]OutputPIN_69
disp_sel[3]OutputPIN_68
disp_sel[2]OutputPIN_67
disp_sel[1]OutputPIN_66
disp_sel[0]OutputPIN_63
ext_clk_inInputPIN_18
ext_rst_nInputPIN_124
eye_out[1]Output
eye_out[0]Output
fangxiang_out[1]Output
fangxiang_out[0]Output
g_col[7]OutputPIN_38
g_col[6]OutputPIN_39
g_col[5]OutputPIN_40
g_col[4]OutputPIN_41
g_col[3]OutputPIN_42
g_col[2]OutputPIN_43
g_col[1]OutputPIN_44
g_col[0]OutputPIN_45
judge_outOutput
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]OutputPIN_11
r_col[6]OutputPIN_12
r_col[5]OutputPIN_13
r_col[4]OutputPIN_14
r_col[3]OutputPIN_15
r_col[2]OutputPIN_16
r_col[1]OutputPIN_21
r_col[0]OutputPIN_22
row[7]OutputPIN_1
row[6]OutputPIN_2
row[5]OutputPIN_3
row[4]OutputPIN_4
row[3]OutputPIN_5
row[2]OutputPIN_6
row[1]OutputPIN_7
row[0]OutputPIN_8
seg_sel[7]OutputPIN_51
seg_sel[6]OutputPIN_52
seg_sel[5]OutputPIN_53
seg_sel[4]OutputPIN_55
seg_sel[3]OutputPIN_57
seg_sel[2]OutputPIN_58
seg_sel[1]OutputPIN_59
seg_sel[0]OutputPIN_62
start_outOutput
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)

四、一些问题

实物班子可能不好使,修改代码见网址:

自动视力测试仪的设计与实现BUPT-CSDN博客文章浏览阅读68次,点赞2次,收藏3次。【代码】自动视力测试仪的设计与实现BUPT。https://blog.csdn.net/weixin_72501900/article/details/135222420?spm=1001.2014.3001.5501

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值