一、 实验目标
熟练掌握 Verilog HDL 硬件描述语言,能够在实际工程中熟练使用Quartus II 软件进行可编程逻辑器件的基本开发, 能够熟练使用 Modelsim 软件进行系统的功能仿真验证, 能够正确的使用实验室各种实验仪器设备如示波器、信号发生器进行问题分析,具备使用合适的实验仪器和仿真软件,对汽车电子信息领域的复杂工程问题能够进行合理的分析、优化、计算和设计,对工程故障能够快速的定位、解决的能力。
二、实验内容
里程显示是汽车仪表盘最基本的功能之一,汽车行驶里程数是对车况评价、车俩维护、行车安全等进行评估的重要依据,本实验是一个汽车里程表的显示系统设计,并显示距下次汽车保养剩余公里数。
三、实验要求
1、 使用 Verilog HDL 硬件描述语言,设计汽车里程表显示系
统,使用按键模拟汽车行驶里程的增加;
2、 对设计的汽车里程显示系统进行功能仿真,验证设计正确性,对出现的错误进行记录、分析;
3、 在FPGA 开发平台上进行板上验证,观察实验现象,分析设计合理性,总结实验心得。
四、实验预习(实验原理、整体设计方案等)
(一)、实验基本原理:
本次实验通过控制MINI_FPGA开发板上配有的6个七段数码管 DIG1-DIG6(当正放 MINI_FPGA 开发板时,从左至右依次数过去)模拟显示汽车仪表盘上的行驶里程,同时通过按键操作,使得数码管上的里程数值不断增加。基于这两个实验要求,我们需要使用到数码管显示数值的功能,以及按键操作功能。同时,为了使按键操作能够被准确的判断,需要在按键操作功能中增加按键消抖功能。为了使得数码管显示的里程数能够随着按键的按下,数值不断的增加,需要利用计数器功能将二者联系起来,使六位数码管的数值显示数值能够正常增加和进位。
(二)、整体设计方案:
1.数码管正常显示对应数值
先确定控制动态扫描的周期为:Timex = 8'd200 ,然后通过Count变量重复在动态扫描周期内重复计数、清零产生片选信号cs(由于本次实验仅启用了两个数码管,所以cs的值在0和1之间变换),再将片选信号进行译码,使得所需的对应数码管启用,由此完成数码管的启用。然后将待显示的数据变量SingleNum初始化为零,然后通过片选信号的不断变化,控制由Testbench上的测试文件数据赋给待显示的数据变量SingleNum,再将待显示的数据变量SingleNum译码成共阴极数码管驱动格式,使得启用的数码管显示出对应的数字。
2.按键操作使数码管数值不断增加
首先,为保证按键操作判断的准确性,需要加入按键消抖功能,具体的按键消抖功能操作为:我们首先需要一个模块来检测按键是否抖动,如果抖动,给计时模块一个标志位开始计时,记满20ms,再给输出消抖后按键信号模块一个标志位进行采样。理清思路,整个程序分为三个模块,模块之间相互关联,关联之处需要一个起到连接作用的器件,也就是标志位。比如将flag作为标志位,检测到按键抖动之后,将flag作为计时开始的条件。
然后我们需要用到计数器功能,将数码管显示与按键操作联系起来,使得数码管显示的里程数随着按键的操作能够不断增加,具体的计数器设计为:通过输入变量cycle设定计数周期,然后利用时钟clk进行计数器计数,每一个时钟周期计数器进行一次计数,直到累加到个位(bit)的值达到一个计数周期,然后个位(bit)清零,十位(ten)进一,以此循环进行。
在计数过程中可以通过输入变量rst_n、enable_n进行复位和使能功能。当rst_n值为0时,计数复位为0;值为1时,计数累加。当enable_n值为0时,计数器暂停计数,保留计数的累计值(当然此时也可以通过rst_n值变为1从而使计数器清零);当enable_n值为1时,计数累加。
五、测试验证(仿真、硬件测试)
仿真测试文件:

图5.1

图5.2
仿真结果图:

图5.3

图5.4
通过将仿真文件与仿真结果可以看到,由图一,当按键按下后,f值(中间变量,用于代替显示数码管显示的数值)不断增加,由图二,当按键按下的次数达到10次时,数值进行进位(十进制,满十进一),使得十位上的数码管也显示对应的数值,实验仿真结果与实验仿真测试文件预测结果一致,实验代码符合实验要求。
六、实验小结
本次实验是通过控制操作MINI_FPGA开发板上的按键,使得配有的6个七段数码管 DIG1-DIG6模拟显示汽车仪表盘上行驶里程,并且随着按键按下次数的增加,数码管显示的里程数值也不断的增加。
本次实验综合了按键消抖实验以及数码管实验,使得理论上的功能,在实际的应用中得到了体现,数码管显示以及按键的控制对于FPGA的学习是基础而又必须的,只有熟练学习了这样基础的实验,才能在一些比较复杂的实验项目中应用的得心应手,使得复杂的实验项目更加完善和细致。同时我在本次实验课程中又熟练了Quartus以及modelsim软件的使用,能够自己将代码功能连接在一起,同时也可以利用modelsim软件书写简单的测试文件进行仿真,更加熟悉了实验代码编写的基本结构,以及一些常用的数据类型。对于FPGA的学习要耐得下心,认真仔细分析自己功能所需要的代码如何进行编写和完善,只有不断的学习,自己才能够不断的提高和进步!
七、实验代码
module sy3(
clk ,
rst_n ,
key_in ,
f,
Digitron_Out ,
DigitronCS_Out
);
// parameter TIME_20MS = 1000000 ;//为了缩短仿真周期,仿真时可适当缩小
parameter TIME_20MS = 100;
//数码管*******************************************************************
parameter Timex = 8'd200 ;//控制动态扫描周期
parameter CS_NUM= 3'd2 ;//需要用到的数码管个数
parameter N_0 = 8'b0011_1111, N_1 = 8'b0000_0110, N_2 = 8'b0101_1011,
N_3 = 8'b0100_1111, N_4 = 8'b0110_0110, N_5 = 8'b0110_1101,
N_6 = 8'b0111_1101, N_7 = 8'b0000_0111, N_8 = 8'b0111_1111,
N_9 = 8'b0110_1111;
//输入信号定义
input clk ;
input rst_n ;
input key_in;
//数码管**********************************************************************
output [7:0]Digitron_Out ;//数码管译码显示数据
output [5:0]DigitronCS_Out;//数码管片选
output [5:0]f;
//输出信号reg定义
reg key_out;
reg uart_txd_d0;
reg uart_txd_d1;
reg key_ff0;
reg key_ff1;
reg[15:0] shake_cnt;
wire shake_flag;
wire flag;
//数码管**************************************************************************
reg [7:0]Digitron_Out ;
reg [5:0]DigitronCS_Out;
reg [5:0]f;
reg [7:0]Result ;//待显示数据
//reg [7:0]Result2 ;//待显示数据
reg [7:0]Count ;//控制动态扫描周期
reg [3:0]SingleNum ;//待显示数据
reg [2:0]cs ;//片选相关中间信号
/*************************
*************
**************************/
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_ff0 <= 1'b1;
key_ff1 <= 1'b1;
end
else begin
key_ff0 <= key_in ;
key_ff1 <= key_ff0;
end
end
/************************
消抖 20MS
**************************/
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
shake_cnt <= 0;
end
else if(key_ff1!=1'h1)begin
if(shake_flag)
shake_cnt <= shake_cnt;
else
shake_cnt <= shake_cnt + 1;
end
else begin
shake_cnt <= 0;
end
end
assign shake_flag = shake_cnt>=TIME_20MS-1;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_out <= 1'b1;
end
else if(shake_flag)begin
key_out <= 1'b0;
end
else begin
key_out <= 1'b1;
end
end
//**************************************************
//**************************************************
assign flag = (~uart_txd_d0) && (uart_txd_d1);
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
uart_txd_d0 <= 1'b1;
uart_txd_d1 <= 1'b1;
end
else begin
uart_txd_d0 <= key_out;
uart_txd_d1 <= uart_txd_d0;
end
end
//*******************************************
//按键触发result计数
//*******************************************
/*always @( posedge clk )
begin
if(!rst_n)
Result <= 8'd0 ;
else if(key_out == 1'b0)
begin
Result[3:0]<=Result[3:0]+4'd1;
if(Result[3:0]==4'd9)
Result[7:4]<=Result[7:4]++4'd1;
else
Result[7:4]<=Result[7:4];
end
else
Result[3:0]<=Result[3:0];
end
*/
//***********************************************if(keyout==1)
always @( posedge clk )
begin
if(!rst_n)
begin
f<= 6'd0;
Result <= 8'd0 ;
end
else
begin
if(flag==1'b1)begin
f<= f+6'd1;
Result[3:0]<=Result[3:0]+4'd1;
if(Result[3:0]==4'd9)begin
Result[7:4]<=Result[7:4]+4'd1;
Result[3:0]<=4'd0;
end
/*else if(Result[3:0]==4'd10)
Result[3:0]<=4'd0;*/
else
Result[7:4]<=Result[7:4];
end
end
end
//**************************************************************************
//数码管**********************************************************************
//-----设置动态扫描周期-------------------
always @( posedge clk )
begin
if(!rst_n)
Count <= 8'd0 ;
else if(Count == Timex)
Count <= 8'd0 ;
else
Count <= Count + 8'd1 ;
end
//---根据扫描周期产生片选信号----------
always @ ( posedge clk )
begin
if(!rst_n)
cs <= 3'd0 ;
else if(Count == Timex)
begin
if(cs == CS_NUM - 3'd1)
cs <= 3'd0 ;
else
cs <= cs + 3'd1 ;
end
end
//------结合电路将片选信号译码---------
always @ ( posedge clk )
begin
if(!rst_n)
DigitronCS_Out <= 6'd0 ;
else
begin
case(cs)
3'd0: DigitronCS_Out <= 6'b11_1110;
3'd1: DigitronCS_Out <= 6'b11_1101;
3'd2: DigitronCS_Out <= 6'b11_1011;
3'd3: DigitronCS_Out <= 6'b11_0111;
3'd4: DigitronCS_Out <= 6'b10_1111;
3'd5: DigitronCS_Out <= 6'b01_1111;
default: DigitronCS_Out <= 6'b11_1111;
endcase
end
end
//---------根据输入将待显示分配到个位、十位----------------
always @ ( posedge clk )
begin
if(!rst_n)
SingleNum <= 4'd0 ;
else
begin
case(cs)
3'd0: SingleNum <= Result[3:0];//个位
3'd1: SingleNum <= Result[7:4];//十位
/*3'd5: SingleNum <= Result2[3:0];//个位
3'd6: SingleNum <= Result2[7:4];//十位*/
default: SingleNum <= 4'd0;
endcase
end
end
//---待显示数据译码成共阴极数码管驱动格式-------
always @( posedge clk )
begin
if(!rst_n)
Digitron_Out <= 8'd0 ;
else
begin
case(SingleNum)
4'd0: Digitron_Out <= N_0;//个位
4'd1: Digitron_Out <= N_1;//十位
4'd2: Digitron_Out <= N_2;//个位
4'd3: Digitron_Out <= N_3;//十位
4'd4: Digitron_Out <= N_4;//个位
4'd5: Digitron_Out <= N_5;//十位
4'd6: Digitron_Out <= N_6;//个位
4'd7: Digitron_Out <= N_7;//十位
4'd8: Digitron_Out <= N_8;//个位
4'd9: Digitron_Out <= N_9;//十位
default: Digitron_Out <= 8'd0;
endcase
end
end
endmodule
八、测试代码
// Copyright (C) 1991-2013 Altera Corporation
// Your use of Altera Corporation's design tools, logic functions
// and other software and tools, and its AMPP partner logic
// functions, and any output files from any of the foregoing
// (including device programming or simulation files), and any
// associated documentation or information are expressly subject
// to the terms and conditions of the Altera Program License
// Subscription Agreement, Altera MegaCore Function License
// Agreement, or other applicable license agreement, including,
// without limitation, that your use is for the sole purpose of
// programming logic devices manufactured by Altera and sold by
// Altera or its authorized distributors. Please refer to the
// applicable agreement for further details.
// *****************************************************************************
// This file contains a Verilog test bench template that is freely editable to
// suit user's needs .Comments are provided in each section to help the user
// fill out necessary details.
// *****************************************************************************
// Generated on "11/04/2022 11:05:47"
// Verilog Test Bench template for design : sy3
//
// Simulation tool : ModelSim (Verilog)
//
`timescale 1 ps/ 1 ps
module sy3_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg key_in;
reg rst_n;
// wires
wire [5:0] DigitronCS_Out;
wire [7:0] Digitron_Out;
wire [5:0] f;
// assign statements (if any)
sy3 i1 (
// port map - connection between master ports and signals/registers
.DigitronCS_Out(DigitronCS_Out),
.Digitron_Out(Digitron_Out),
.clk(clk),
.f(f),
.key_in(key_in),
.rst_n(rst_n)
);
initial
begin
clk = 0 ;
rst_n = 0 ;
key_in = 1'b1 ;
#1000;
rst_n= 1 ;
key_in= 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in= 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in= 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in= 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in= 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
key_in = 1'b0 ;
#10000;
key_in = 1'b1 ;
#10000;
$stop;
end
always #10 clk = ~clk ;
endmodule