一、 实验目标
熟练掌握 Verilog HDL 硬件描述语言,能够在实际工程中熟练使用Quartus II 软件进行可编程逻辑器件的基本开发, 能够熟练使用 Modelsim 软件进行系统的功能仿真验证, 能够正确的使用实验室各种实验仪器设备如示波器、信号发生器进行问题分析,具备使用合适的实验仪器和仿真软件,对汽车电子信息领域的复杂工程问题能够进行合理的分析、优化、计算和设计,对工程故障能够快速的定位、解决的能力。
二、实验内容
车速显示是汽车仪表盘的基本功能之一,对车速进行实时的显示是保证驾驶员安全行驶的有力保障,本实验是设计一个汽车速度显示系统,通过一个按键模拟油门,另一个按键模拟刹车,实现车速的准确显示。
三、实验要求
1、 根据 FPGA 开发流程对实验项目进行合理分析,设计系统方案;
2、 使用 Verilog HDL 硬件描述语言,设计汽车速度显示系统,使用按键操作模拟刹车及油门,当未按键时车速保持;
3、 对设计的汽车速度显示系统进行功能仿真,验证设计正确性,对出现的错误进行记录、分析;
4、 在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作为计时开始的条件。
然后,需要设置两个按键为油门和刹车,分别控制显示管显示汽车速度数值的增加和减少,在没有按键按下时,也需要保持汽车速度(理想情况下,实际情况下汽车速度会由于摩擦阻力的影响逐渐减小),具体如下图:

图4.1
油门功能按键,通过油门功能按键的按下汽车行驶速度不断增加。



图4.2
刹车功能按键,通过刹车功能按键的按下汽车行驶速度不断减少。
同时代码通过key_out变量的选择,使得两个按键的按下逻辑更为准确,且确保,两个按键都未按下时,数码管显示的汽车行驶速度保持原值不变。
五、测试验证(仿真、硬件测试)
仿真测试文件:

图5.1
仿真结果图:

图5.2
通过将仿真文件与仿真结果可以看到,在油门按键按下时,数码管所显示的汽车行驶速度不断增加;在刹车按键按下时,数码管所显示的汽车行驶速度不断减少;在两个按键都不按下时,数码管所显示的汽车行驶速度保持不变,这与编写的代码相符合,实验仿真结果与实验仿真测试文件预测结果一致,实验代码符合实验要求。
六、实验小结
汽车速度显示功能在实际的应用当中是十分广泛的,汽车在实际的道路行驶过程中,良好的速度显示不仅能够增加驾驶人对于道路安全以及控制的判断,同时也能够通过道路限速,驾驶人控制速度使得道路行驶更加顺畅,节约道路行驶时间,防止造成道路拥堵。
本次实验通过控制MINI_FPGA开发板上配有的6个七段数码管 DIG1-DIG6(当正放 MINI_FPGA 开发板时,从左至右依次数过去)模拟显示汽车行驶速度,然后通过刹车与油门按键的按下,数码管汽车行驶速度发生相应的变化。
本次实验也是综合了按键消抖实验以及数码管实验,同时隐藏性也添加了计数器功能,可见一些基础性实验对于复杂项目的必要性。多多做一些简单性基础实验,对于我们将来做大项目是十分有帮助的!
七、实验代码
module sy5(
clk ,
rst_n ,
key_in ,
key_in1 ,
Digitron_Out ,
DigitronCS_Out
);
// parameter TIME_20MS = 1000000 ;//为了缩短仿真周期,仿真时可适当缩小
parameter TIME_20MS = 100;
//数码管*******************************************************************
parameter Timex = 8'd200 ;//控制动态扫描周期
parameter CS_NUM= 3'd6 ;//需要用到的数码管个数
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;
input key_in1;
//数码管**********************************************************************
output [7:0]Digitron_Out ;//数码管译码显示数据
output [5:0]DigitronCS_Out;//数码管片选
//输出信号reg定义
reg key_out;
reg key_out1;
/*reg uart_txd_d0;
reg uart_txd_d1;
reg uart_txd_d2;
reg uart_txd_d3;*/
reg key_ff0;
reg key_ff1;
reg key_ff2;
reg key_ff3;
reg flag1;
reg flag2;
reg flag3;
reg flag4;
reg flag5;
reg[15:0] shake_cnt;
wire shake_flag;
//wire flag;
reg[15:0] shake_cnt1;
wire shake_flag1;
// wire flag1;
//数码管**************************************************************************
reg [7:0]Digitron_Out ;
reg [5:0]DigitronCS_Out;
reg [23:0]Result ;//待显示数据
//reg [7:0]Result2 ;//待显示数据
reg [7:0]Count ;//控制动态扫描周期
reg [3:0]SingleNum ;//待显示数据
reg [2:0]cs ;//片选相关中间信号
reg [15:0]CK ;//
/*************************
**************************/
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
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_ff2 <= 1'b1;
key_ff3 <= 1'b1;
end
else begin
key_ff2 <= key_in1 ;
key_ff3 <= key_ff2;
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
*/
//按键二
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
shake_cnt1 <= 0;
end
else if(key_ff3!=1'h1)begin
if(shake_flag1)
shake_cnt1 <= shake_cnt1;
else
shake_cnt1 <= shake_cnt1 + 1;
end
else begin
shake_cnt1 <= 0;
end
end
assign shake_flag1 = shake_cnt1>=TIME_20MS-1;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_out1 <= 1'b1;
end
else if(shake_flag1)begin
key_out1 <= 1'b0;
end
else begin
key_out1 <= 1'b1;
end
end
/*
assign flag1 = (~uart_txd_d2) && (uart_txd_d3);
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
uart_txd_d2 <= 1'b1;
uart_txd_d3 <= 1'b1;
end
else begin
uart_txd_d2 <= key_out1;
uart_txd_d3 <= uart_txd_d2;
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
Result <= 8'd0 ;
end
else
begin
if(flag==1'b1)begin
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
Result[7:4]<=Result[7:4];
end
end
end
*/
//***************************************************************************
always @( posedge clk )
begin
if(!rst_n)begin
CK <= 16'd0 ;
//flag <= 1'b0;
end
else if(CK == 16'd10000)begin
CK <= 16'd0 ;
//flag <=~flag;
end
else
CK <= CK + 16'd1 ;
end
//**************************************************************************
always @( posedge clk )
begin
//if(flag==1'b1)begin //*******************************************
if(!rst_n)
begin
Result<= 24'd0;
flag1<= 1'b1;
flag2<= 1'b1;
flag3<= 1'b1;
flag4<= 1'b1;
flag5<= 1'b1;
end
else
begin
if(key_out==1'd0&&key_out1==1'd1)
begin
if(CK == 16'd10000)begin //*******************************************
Result[3:0]<=Result[3:0]+4'd1;
if(Result[3:0]==4'd10)begin
Result[7:4]<=Result[7:4]+4'd1;//个位进十位
Result[3:0]<=4'd0;
end
else if(Result[7:4]==4'd10)begin
Result[11:8]<=Result[11:8]+4'd1;//十位进百位
Result[7:4]<=4'd0;
end
else if(Result[11:8]==4'd10)begin
Result[15:12]<=Result[15:12]+4'd1;//百位进千位
Result[11:8]<=4'd0;
end
else if(Result[15:12]==4'd10)begin
Result[19:16]<=Result[19:16]+4'd1;//千位进万位
Result[15:12]<=4'd0;
end
else if(Result[19:16]==4'd10)begin
Result[23:20]<=Result[23:20]+4'd1;//万位进十万位
Result[19:16]<=4'd0;
end
else if(Result[23:20]==4'd10)begin
Result<= Result; //十万位满清零
end
//&&Result[19:16]==4'd9&&Result[15:12]==4'd9&&Result[11:8]==4'd9&&Result[7:4]==4'd9&&Result[3:0]==4'd9
end //**************************
end
else if(key_out1==1'd0&&key_out==1'd1)
begin
if(CK == 16'd10000)begin //*******************************************
if(Result!= 24'd0)
begin
Result[3:0]<=Result[3:0]-4'd1;
if(Result[3:0]==4'd0&&Result[23:4]!=4'd0)
flag1<=1'b0;
else if(flag1==1'b0&&Result[23:4]!=4'd0)begin
flag1<=1'b1;
Result[3:0]<=4'd9;
Result[7:4]<=Result[7:4]-4'd1;//个位不足十位减一
if(Result[7:4]==4'd0&&Result[23:8]!=4'd0)
flag2<=1'b0;
end
//*******************************************
else if(flag2==1'b0&&Result[23:8]!=4'd0)begin
//if(Result[23:8]!=4'd0)begin
flag2<=1'b1;
Result[7:4]<=4'd9;
Result[11:8]<=Result[11:8]-4'd1;//十位不足百位减一
if(Result[11:8]==4'd0&&Result[23:12]!=4'd0)
flag3<=1'b0;
/*end
else
Result[7:4]<=4'd0;*/
end
else if(flag3==1'b0&&Result[23:12]!=4'd0)begin
//if(Result[23:12]!=4'd0)begin
flag3<=1'b1;
Result[11:8]<=4'd9;
Result[15:12]<=Result[15:12]-4'd1;//百位不足千位减一
if(Result[15:12]==4'd0&&Result[23:16]!=4'd0)
flag4<=1'b0;
/*end
else
Result[11:8]<=4'd0;*/
end
else if(flag4==1'b0&&Result[23:16]!=4'd0)begin
//if(Result[23:16]!=4'd0)begin
flag4<=1'b1;
Result[15:12]<=4'd9;
Result[19:16]<=Result[19:16]-4'd1;//千位不足万位减一
if(Result[19:16]==4'd0&&Result[23:20]!=4'd0)
flag5<=1'b0;
/*end
else
Result[15:12]<=4'd0;*/
end
else if(flag5==1'b0&&Result[23:20]!=4'd0)begin
//if(Result[23:20]!=4'd0)begin
flag5<=1'b1;
Result[19:16]<=4'd9;
Result[23:20]<=Result[23:20]-4'd1;//万位不足十万位减一
/*end
else
Result[19:16]<=4'd0;*/
end
else if(Result[23:20]==4'd0)begin
Result[23:20]<=4'd0;//万位不足十万
end
else
Result<=Result;
end
else
Result<= 24'd0;
//end//*********************************************************
end
end
else if(key_out1==1'd0&&key_out==1'd0)
Result<= 24'd0;
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'd2: SingleNum <= Result[11:8];//百位
3'd3: SingleNum <= Result[15:12];//千位
3'd4: SingleNum <= Result[19:16];//万位
3'd5: SingleNum <= Result[23:20];//十万位
/*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/23/2022 10:19:44"
// Verilog Test Bench template for design : sy5
//
// Simulation tool : ModelSim (Verilog)
//
`timescale 1 ps/ 1 ps
module sy5_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg key_in;
reg key_in1;
reg rst_n;
// wires
wire [5:0] DigitronCS_Out;
wire [7:0] Digitron_Out;
// assign statements (if any)
sy5 i1 (
// port map - connection between master ports and signals/registers
.DigitronCS_Out(DigitronCS_Out),
.Digitron_Out(Digitron_Out),
.clk(clk),
.key_in(key_in),
.key_in1(key_in1),
.rst_n(rst_n)
);
initial
begin
clk = 0 ;
rst_n = 0 ;
key_in = 1'b1 ;
key_in1 = 1'b1 ;
#1000000;
rst_n= 1 ;
key_in= 1'b0 ;
#1000000;
key_in = 1'b1 ;
#1000000;
key_in1 = 1'b0 ;
#1000000;
key_in1 = 1'b1 ;
#1000000;
$stop;
end
always #10 clk = ~clk ;
endmodule