HEU大二数电时序逻辑电路设计实验

Contents


1 实验目的

2 实验仪器

3 子任务

3.1 消抖电路

  • 实验原理
  • 实验步骤
  • 具体实现
  • 问题解决

3.2 简易篮球比赛计分器

  • 实验原理
  • 实验步骤
  • 具体实现
  • 问题解决

4 结论心得


1 实验目的

  • 掌握时序逻辑电路的一般设计方法。
  • 掌握消抖电路的设计方法。
  • 通过 V e r i l o g Verilog Verilog 语言实现一个简单篮球记分器。

2 实验仪器

  • F P G A FPGA FPGA 开发板 D E 1 − S o C DE1-SoC DE1−SoC,如图所示。

HEU大二数电时序逻辑电路设计实验

3 子任务

3.1 消抖电路

通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。按键抖动会引起一次按键被误读多次。为确保CPU对键的一次闭合仅作一次处理,必须去除键抖动。在键闭合稳定时读取键的状态,并且必须判别到键释放稳定后再作处理。

实验原理

HEU大二数电时序逻辑电路设计实验

抖动时间的长短由按键的机械特性决定,一般为 5 m s ~ 10 m s 5ms~10ms 5ms~10ms 。这是一个很重要的时间参数,在很多场合都要用到。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。单片机一般采用延迟重采样的方式进行消抖。当检测到信号为低时,延迟一段时间(一般为 20 m s 20ms 20ms ),再次检测信号是否为低,如果为低,则证明按键按下,否则认为按键没有按下,继续下一次检查。
最后,通过实现如下功能的电路来验证消抖是否成功。

  • 消抖脉冲为 500 H z 500Hz 500Hz。
  • 用按键作为消抖电路的输入,消抖结果控制一个十进制计数器,用 1 1 1 位数码管显示计数结果。

计数器迭代的流程如图所示:

机械按键触发 消抖电路消抖 计数器迭代一次 数码管显示迭代结果

实验步骤

  1. 根据消抖脉冲要求设计分频计数器。
  2. 设计数码管显示电路。
  3. 编写 V e r i l o g Verilog Verilog 程序并进行调试。
  4. 下载到 F P G A FPGA FPGA 开发板上验证程序相关功能。

具体实现

在 Q u a r t u s Quartus Quartus 中先编写消抖电路程序并封装成一个模块,程序如下所示:

Codes

 module xiaodou
(
  input      clk   , //输入时钟信号,开发板上是50MHz
  input      rst_n , //复位键(低电平触发)
  input      key_in, //对应的机械按键
  output reg clk_500hz, //分频出的500Hz时钟脉冲信号
  output     key_done //按键按下动作完成标志
);
reg [25:0]div_cnt; //分频计数器

always@(posedge clk or negedge rst_n) //获得500Hz时钟脉冲信号
begin
    if(!rst_n)
      begin
	     div_cnt <= 0;
		 clk_500hz <= 0;
	  end
    else if(div_cnt == 99999) //计数十万次反转状态
      begin
	     div_cnt <= 0;
		 clk_500hz <= ~clk_500hz;
	  end
	else 
      begin
	     div_cnt <= div_cnt + 1;
		 clk_500hz <= clk_500hz;
	  end
end


reg qout;
reg key_tmp1,key_tmp2;
parameter n = 10;
reg [25:0] cnt;

always@(posedge clk_500hz or negedge rst_n) 
begin
    if(!rst_n)
		  begin
			 cnt <= 0;
			 qout <= 0;
		  end	  
	else if(key_in == 0) //按键按下
	 begin
	   if(cnt == n-1) //持续2ms的话判定按下
		  begin
			 cnt <= cnt;
			 qout <= 1;
		  end
	   else
		  begin
			 cnt <= cnt+1;
			 qout <= 0;
		  end
	 end
	else
	 begin
		qout <= 0;
		cnt <= 0;
	 end
end	

/*提取前后按键信号*/
always@(posedge clk_500hz or negedge rst_n)
begin
	  if(!rst_n)
	    begin
		 key_tmp1 <= 0;
		 key_tmp2 <= 0;
	    end
	  else 
	    begin
		 key_tmp1 <= qout;
		 key_tmp2 <= key_tmp1;
	    end	  
end
	
	assign key_done = key_tmp1 & (~ key_tmp2);	
		
endmodule 

消抖模块电路逻辑图

HEU大二数电时序逻辑电路设计实验

然后编写十进制计数器程序,代码如下:

Codes

 module count
(
   input   clk,
   input   rst_n,
   input   key,
   output  reg[6:0] seg


);
reg[3:0]cnt;
wire key_done;
xiaodou  u1
(
  .clk       (clk     ), //需要分频的信号为50Mhz时钟脉冲信号
  .rst_n     (rst_n   ), //复位键
  .key_in    (key     ), //机械按键
  .clk_500hz (clk_500hz), //分频得到的500Hz时钟脉冲信号
  .key_done  (key_done) //按键动作完成标志
);

always@(posedge clk_500hz or negedge rst_n)
begin
 if(!rst_n)
    cnt <= 0;
 else if(key_done) //按下动作完成
   begin
    if(cnt == 9) //计数到9后需要清零
	  cnt <= 0;
	else
      cnt <= cnt + 1;
   end
end

always@(cnt)     //数码管显示模块
begin

   case(cnt)    
    0:seg = 7'b0000001;
    1:seg = 7'b1001111;
    2:seg = 7'b0010010; 
    3:seg = 7'b0000110; 
    4:seg = 7'b1001100; 
    5:seg = 7'b0100100; 
    6:seg = 7'b0100000; 
    7:seg = 7'b0001111; 
    8:seg = 7'b0000000; 
    9:seg = 7'b0000100; 
   endcase
end

endmodule

十进制计数器电路逻辑图

HEU大二数电时序逻辑电路设计实验

十进制计数器引脚分配方案

HEU大二数电时序逻辑电路设计实验

下载验证

HEU大二数电时序逻辑电路设计实验

问题解决

个人认为,这个实验的主要难点在于如何分频出 500 H z 500Hz 500Hz 的时钟脉冲信号。由于临近期末时间紧迫,我没法系统地深入学习 V e r i l o g Verilog Verilog 语言,因此消抖模块的程序也是找的现成的程序进行学习理解。在最初编写的程序里,我的代码是没有以下这段的。

always@(posedge clk_500hz or negedge rst_n)
begin
	  if(!rst_n)
	    begin
		 key_tmp1 <= 0;
		 key_tmp2 <= 0;
	    end
	  else 
	    begin
		 key_tmp1 <= qout;
		 key_tmp2 <= key_tmp1;
	    end	  
end

我以为,只要计数够 2 m s 2ms 2ms (就是稳定状态)还是低电平就可以了,这段代码没啥存在的必要,但实际验证貌似还是会有不成功的情况。后来才发现,从原理上来说,机械按键按下存在前沿抖动和后沿抖动,因此需要取前后按键信号的按下状态来进行强判定是否按下。

3.2 简易篮球比赛计分器

利用开发板上现有的按键、开关和数码管资源实现一个简易篮球比赛计分器。

实验原理

简易篮球比赛计分器的基本功能要求如下:

  • 甲乙两队的得分分别用两个数码管显示
  • 每次可以给甲队或乙队加上 1 1 1 分, 2 2 2 分或 3 3 3 分,比分显示范围 00 00 00 到 99 99 99 。

篮球计分器主要由以下模块构成

带消抖电路的加分按键模块 计数器模块 计分对象控制模块 数码管显示电路

实验步骤

  1. 根据功能要求设计各模块电路。
  2. 对各模块进行逻辑和算法分析。
  3. 编写 V e r i l o g Verilog Verilog 程序并进行调试。
  4. 下载到 F P G A FPGA FPGA 开发板上验证程序相关功能。

具体实现

下面以甲队计分的过程为例描述计分器的设计过程:

加分按键模块三个按钮有一个按下 判断甲队加分控制端开关是否拉为高电平 若甲队加分开关为高电平则加对应分数 数码管显示甲队分数

在 Q u a r t u s Quartus Quartus 中编写程序,总代码如下:

Codes

module ball
(
	input [2:0] o, //表示对应加1,2,3分按键的状态
	input   m,  //甲队加分控制端
	input   m1, //乙队加分控制端
   input   clk, //50MHz时钟脉冲信号
   input   rst_n, //复位键
   input   key, 
   output  reg [6:0] ans1, 
	output  reg [6:0] ans2, 
	output  reg [6:0] ans3, 
	output  reg [6:0] ans4  
);

integer i;
reg [3:0] l;  //甲队分数的十位
reg [3:0] r;  //甲队分数的个位
reg [3:0] l1; //乙队分数的十位
reg [3:0] r1; //乙队分数的个位
//reg [3:0] key1;
//reg [3:0] key2;
//reg [3:0] key3;
wire key_done; //有按键按下
xiaodou  u1  //消抖模块,代码与子任务3.1中略有不同
(
  .clk       (clk     ),
  .rst_n     (rst_n   ),
  .key_in1   (o[0]   ),
  .key_in2	 (o[1]	 ),
  .key_in3   (o[2]    ),
  .clk_500hz (clk_500hz),
  .key_done  (key_done)
);


always@(posedge clk_500hz or negedge rst_n)
	begin
		if (!rst_n) begin  //复位
			l=0; r=0; l1=0; r1=0;
		end
		else begin
			if (key_done) begin //当有加分按键按下时
			for (i=0; i<=2; i=i+1)
			begin
				if (!o[i]) begin //判断是哪个按键按下
					case (i)
						0:begin
							if (m) begin //给甲队加分
								r=r+1;
								if (r==10) begin
									r=0;
									l=l+1;
								end
							end	
							if (m1) begin //给乙队加分
								r1=r1+1;
								if (r1==10) begin
									r1=0;
									l1=l1+1;
								end
							end
						end
						1:begin
							if (m) begin
								r=r+2;
								if (r>=10) begin
									r=r-10;
									l=l+1;
								end
							end
							if (m1) begin
								r1=r1+2;
								if (r1>=10) begin
									r1=r1-10;
									l1=l1+1;
								end
							end
						end
						2:begin
							if (m) begin
								r=r+3;
								if (r>=10) begin
									r=r-10;
									l=l+1;
								end
							end
							if (m1) begin
								r1=r1+3;
								if (r1>=10) begin
									r1=r1-10;
									l1=l1+1;
								end
							end
						end
				endcase
		   end
	   end
	 end
 end

end

always @ (l)
	begin
		case(l)    
			0:ans1 = 7'b0000001;
			1:ans1 = 7'b1001111;
			2:ans1 = 7'b0010010; 
			3:ans1 = 7'b0000110; 
			4:ans1 = 7'b1001100; 
			5:ans1 = 7'b0100100; 
			6:ans1 = 7'b0100000; 
			7:ans1 = 7'b0001111; 
			8:ans1 = 7'b0000000; 
			9:ans1 = 7'b0000100; 
		endcase
	end

always @ (r)
	begin
		case(r)    
			0:ans2 = 7'b0000001;
			1:ans2 = 7'b1001111;
			2:ans2 = 7'b0010010; 
			3:ans2 = 7'b0000110; 
			4:ans2 = 7'b1001100; 
			5:ans2 = 7'b0100100; 
			6:ans2 = 7'b0100000; 
			7:ans2 = 7'b0001111; 
			8:ans2 = 7'b0000000; 
			9:ans2 = 7'b0000100; 
		endcase
	end
always @ (l1)
	begin
		case(l1)    
			0:ans3 = 7'b0000001;
			1:ans3 = 7'b1001111;
			2:ans3 = 7'b0010010; 
			3:ans3 = 7'b0000110; 
			4:ans3 = 7'b1001100; 
			5:ans3 = 7'b0100100; 
			6:ans3 = 7'b0100000; 
			7:ans3 = 7'b0001111; 
			8:ans3 = 7'b0000000; 
			9:ans3 = 7'b0000100; 
		endcase
	end
always @ (r1)
	begin
		case(r1)    
			0:ans4 = 7'b0000001;
			1:ans4 = 7'b1001111;
			2:ans4 = 7'b0010010; 
			3:ans4 = 7'b0000110; 
			4:ans4 = 7'b1001100; 
			5:ans4 = 7'b0100100; 
			6:ans4 = 7'b0100000; 
			7:ans4 = 7'b0001111; 
			8:ans4 = 7'b0000000; 
			9:ans4 = 7'b0000100; 
		endcase
	end

endmodule

其中,消抖电路模块代码如下:

Codes

module xiaodou
(
  input      clk   ,
  input      rst_n ,
  input      key_in1,
  input   	 key_in2,
  input 		 key_in3,
  output reg clk_500hz,
  output     key_done
);
reg [25:0]div_cnt;

always@(posedge clk or negedge rst_n) 
begin
    if(!rst_n)
      begin
	     div_cnt <= 0;
		 clk_500hz <= 0;
	  end
    else if(div_cnt == 99999)
      begin
	     div_cnt <= 0;
		 clk_500hz <= ~clk_500hz;
	  end
	else 
      begin
	     div_cnt <= div_cnt + 1;
		 clk_500hz <= clk_500hz;
	  end
end


reg qout;
reg key_tmp1,key_tmp2;
parameter n = 10;
reg [25:0] cnt;

always@(posedge clk_500hz or negedge rst_n) 
begin
    if(!rst_n)
		  begin
			 cnt <= 0;
			 qout <= 0;
		  end	  
	else if(key_in1==0 || key_in2==0 || key_in3==0) //此处与子任务3.1中的条件不同
	 begin
	   if(cnt == n-1)
		  begin
			 cnt <= cnt;
			 qout <= 1;
		  end
	   else
		  begin
			 cnt <= cnt+1;
			 qout <= 0;
		  end
	 end
	else
	 begin
		qout <= 0;
		cnt <= 0;
	 end
end	


	
always@(posedge clk_500hz or negedge rst_n)
begin
	  if(!rst_n)
	    begin
		 key_tmp1 <= 0;
		 key_tmp2 <= 0;
	    end
	  else 
	    begin
		 key_tmp1 <= qout;
		 key_tmp2 <= key_tmp1;
	    end	  
end
	
	assign key_done = key_tmp1 & (~ key_tmp2);	
		
endmodule 

简易篮球计分器电路逻辑图

HEU大二数电时序逻辑电路设计实验

简易篮球计分器引脚分配方案

HEU大二数电时序逻辑电路设计实验

下载验证

HEU大二数电时序逻辑电路设计实验

问题解决

这个实验是一个综合度较高的基础实验,我个人也是遇到了不少的问题,经过不断修改调试,总结了以下几个主要的问题:

  • V e r i l o g Verilog Verilog 语言中一个寄存器变量不能在两个 a l w a y s always always 语句(有限状态机)中赋值。
  • V e r i l o g Verilog Verilog 语言虽然为硬件程序设计语言,但是不同于 C C C 语言等传统软件编程语言,有些复杂逻辑(比如多重 i f if if 语句嵌套,或者是上面那个程序中 if (!rst_n) 改成 if (rst_n)),虽然语法正确,但是系统综合分析就是会失败,报错提示顶层的模块名要跟工程文件名一致。
  • 在主程序向消抖模块传递参数时,不能只把按键存储的寄存器变量 o o o 直接传递,这样它会默认只判断寄存器最低位的状态,不管高位的状态了。

主要的解决方法就是:

  • 从硬件原理上来说,如果一个变量能在两个有限状态机中赋值,那么这个逻辑其实不是那么牢靠的,可能存在冲突现象,因此要避免。
  • 降低算法复杂度,尽量避免多重 i f if if 嵌套。
  • 将某一按键动作判断归一化为只要所有按键中有一个按下就判定按下成立(或逻辑),然后通过循环扫描哪一个按键的电平是低电平(低电平触发)选择具体的按键。

4 心得体会

上一篇:FIFO法生成3*3阵列-----图像处理


下一篇:使用wireshark排除一例网络问题