本文是通过Verilog语言,通过Quartus II平台和FPGA开发板实现的一款出租车计费系统的设计。全文对于EDA技术以及Verilog硬件描述语言的概念以及相关特点进行了介绍与论述。对于该计费系统的实现方式以及不同模块功能的设计也进行了详细具体的介绍。设计过程中考虑了出租车行驶过程中的起步价、里程单价、等候时间等因素来计算总费用。同时,通过设计也能够实现对于计费系统的起步价,单价的设置。此外,在设计过程中,考虑到了出租车在实际的行驶过程中可能遇到的的暂停等待和行驶的不同状态,具有比较强的实用性。完成设计之后首先在Quartus II平台上完成仿真测试,并在最终能够在FPGA开发板上面完成功能验证。
系统设计与实现
系统硬件设计
整个出租车计费器主要分为分频模块、消抖模块、控制模块、计费模块、数码管驱动显示模块、计数器测脉冲模块。
系统开机时数码管显示的全是0,当按下模式切换按键时,控制模块在调节起步价、单价中进行设置起步价、单价。于是切换模式为显示行程、时间。按下开始,先按行程计费,3km以内按照起步价计费,3km以上,按照1元/km的方式计费。如果选择按照时间等候计费,如果时间在2分钟以内不收取费用,之后每超1分钟收取1元的方式计费。
当按键切换到显示速度的模式下,数码管显示当前的车速,由于车速是通过类似于霍尔传感器等将速度转化为脉冲信号,因此仅需要个这个测量速度的模块一个1Hz的门限信号,在这1hz的情况下让计数器计数,于是数码管显示的速度为转速。测速度的模块相当于测量外来信号的频率。从设计要求中可以得出出租车计费器的系统整体框图如图1所示。
数码管显示电路
显示电路采用系统板上的数码管,该数码管为共阳极数码管,数码管的位选接三极管的发射极,基极接引脚,集电极接高电平,通过给基极低电平,控制三极管的导通,于是集电极电流流入发射极,当哪一个数码管的位选打开,哪一个数码管才能亮。数码管的段选也接FPGA的引脚,通过给相应的段码低电平,就会控制数码管亮什么数字。其硬件连接图如图3-4所示。
图3-4 数码管硬件连接图
按键电路
按键是控制数码管切换显示以及是否是距离计费还是按照时间计费。按键的靠近引脚的一段接上拉电阻,默认为高电平,按键另一端接低电平,按下则导通为低电平。其硬件连接图如图3-5所示。
5 按键硬件连接图
LED指示灯与蜂鸣器报警超速电路
当行程计费与时间计费切换时,需要进行指示灯的显示,当行程计费是指示灯常亮,当速度超过一定转速的时候,蜂鸣器会按照一定频率报警提示。其电路连接图如图3-6和3-7所示。
3-6 显示灯硬件图 3-7 蜂鸣器硬件图
系统软件设计
分频模块
按键消抖模块、1hz信号、数码管驱动显示模块、等待计数等模块都需要相应的频率,因此需要对系统时钟50Mhz分频,消抖模块采用的是1000hz信号,数码管驱动显示模块采用的是500hz信号,等待计数等模块用的是1hz信号。Clk为系统时钟输入端,fs是分频系数,cko是分频得到的频率,也即是说,给fs的数值是多少,输出的频率就是多大。比如fs设置为1000,则输出的cko频率就是1000hz。根据要求分频模块如图3-8所示。
按键消抖模块
之所以为按键消抖,是因为往往在按键按下的时候不能立刻达到键稳定的状态,因为按键的机械特性,要经历接触-断开-再接触-再断开的过程,最终要稳定在接触位置,也就是说按键虽然只按下一次之后再放掉,在按键稳定前后,会出现不该有的噪声,如图3-9所示。这样会引起电路的误动作。因此消抖是十分必要的。一般软件消抖皆为延迟消抖,在FPGA中也不例外,一般抖动期为5-10ms。
图3-9 按键抖动
由于分频模块分出1000hz的频率,所以消抖所采用的的频率即为1000hz信号,将按下的按键作为输入,clk选择系统时钟分频得到的1000hz信号,在模块中进行判断,当按键按下时,在1000hz的信号下cnt加1,直到cnt加到100时再次判断输入按键的状态如果仍为0则按键确实按下,于是输出按键按下。这里延时的时间为100×0.001s=10ms。Clk为分频得到时钟,key_in为输入按键值,key_out为消抖输出的按键值。根据要求按键消抖模块如图3-10所示。
数据选择模块
按键选择模式得到一个标志位,如module_Flag、SPEED_Flag、fee_Flag、price_Flag四个模式,当按键按下一次state加1,然后以此控制标志位,当相应的标志位为1时,数据选择模块中,就将相应模式下的数据给输出段,来控制数码管显示数据。Clk为系统时钟输入,distance[7:0]为行程的两个数据,S[7:0]为秒的两个数据,m[7:0]为分钟的两个数据,q0-q7是显示转速的八个数据,fee[7:0]为费用的两个数据。S_fee和price为设置模式下,显示的设置费用和单价。DH、DL、MH、ML、SH、SL、FH、FL分别对应八个数据送到数码管驱动电路中,驱动显示。根据要求设置的数据选择模块如图3-11所示。
数码管显示模块
将数据选择模块中输出的8个数据送到数码管显示块中,通过3-8译码器选通位选,然后控制数码管亮什么数字。
3-8译码器的的设计是通过软件实现的,定义3位的scan状态,在500hz上升沿信号下,每来一次上升沿,scan加1,然后通过case语句实现选通位选,在将段码通过assign语句实现。根据要求其数码管显示模块如图3-12所示。
速度测量模块
速度测量模块又1hz门限信号、计数器、锁存器组成。
当外围电路,诸如霍尔传感器将车轮的转速转化为脉冲信号之后,可以将转化之后的脉冲信号直接给FPGA的引脚。系统十分分频得到1hz的信号。并且在所存模块中完成赋值所存数据。在1hz下标志位timer在上升沿到来时取反,于是timer是一个1s的门限信号,在1s的门限信号下测量输入的脉冲数据得到脉冲的个数,根据外围电路设计计算出车的转速,转而在计算速度。由于条件有限仅仅只是仿真模拟,并没有真正去检测。
所采取的测量脉冲是FPGA的简易频率计,由于外围电路能够将转速转化为脉冲,通过测量脉冲的频率即可得到转速。
所采取的的所存数据是锁存器,这样做的好处是为了能够将1s测得的数据进行保存,以免丢失。
Clk_in为外围电路输入脉冲信号,timer是1s的门限信号,当timer由1到0发生变化,q将清零,重新一轮计数。这样能够保证每一秒都能够测得新的数据。Q为测得的数据并传送给count_cnt模块,这个锁存器的模块时钟选用的是50Mhz系统分频得到的1Hz 的信号,q0-q7为所存的数据输出给数据选择器。Beep_flag是让蜂鸣器响的标志。当测得脉冲超过一定值时,蜂鸣器会报警。根据要求其测量速度模块如图3-13所示。
里程计费模块
由于硬件实物的板子内存有限,乘除运算会占用大量内存,因此本设计假设车速为20m/s,于是50s是1km,在3km之内是起步价,当公里数超过3km之后,按照1km收取1元的计算。
本模块中cnt在1hz的触发下计数,1hz加一次,当加满50次时,distance加1,实现行程加1km,行程超过3km以上,1km收取1元。
Clk为1hz信号,flag是选择按里程计费模式,reset为复位,module_flag是选择数码管显示里程模块,distance是输出的形成,distance_enable是一公里到达的标志。根据要求其里程计费模块如图3-14所示。
等候时间计费模块
由于出租车在行驶的过程中,会出现等候乘客、以及等候红绿灯的情况,这种情况仍然需要成本,因此本设计采用的是在2分钟之内不收取任何费用,当时间达到2分钟以上,按超一分钟收1元的方式计费。
Clk为输入1hz信号,reset为复位按键,flag为选择等候时间计费模式,s为秒的两个数据,m为分的两个数据,time_enable为时间到达1分钟的标志。根据要求其等候时间计费模块如3-15所示。
按键切换模式模块
刚开机时,系统保持默认的0状态,这个状态下没有任何操作,当按下按键key时,此模块的中间变量state实现加1,在state=1的状态下,调节起步价的标志位会置1,这种模式下可以在起步价和单价设置的模块中进行调节。再次按按键key,state会变为2,这种状态下price_Flag为1,这时可以进行单价的设置。当按第三下按键时,状态切换至行程、时间、计价显示模块。第四次按键则会切换至转速显示。
Key为消抖之后的按键,clk为系统时钟,module_Flag为切换至行程、时间、计价显示的标志,SPEED_Flag为显示速度的模式。Price_Flag和fee_Flag为调节起步价和单价的标志位。根据要求按键切换模式模块如图3-16所示。
等候时间与行程计费切换
在这个模块中,是等候时间与行程计费进行互换,当在等候时间模块中,行程会保持不变,价格随着时间的增加而变化。当在行程计费的模块中时,等候时间不会变化,行程超过3公里以上按照相应的规定收费。
本模块的使用完全是在按键切换模式模块调到行程、时间、计价模块下才能起作用。当按键切换至行程、时间、计价模块时,默认为时间计费,当按下按键之后,flag置1,如果不按下按键,flag为0,按下之后切换至里程计费模式,时间停止不动,再次按下按键时,切换至时间计费。因此start是里程计费和时间计费的切换。
Start是另一个按键消抖之后的键值,clk为系统时钟,reset为复位,module_Flag为行程、时间、计价显示模块的标志,flag是切换里程计费和等候时间计费的标志。根据设计其等候时间与行程计费切换模块如图3-17所示。
起步价与单价的设置
起步价与单价的设置,是必须在调节起步价标志位为1或者调节单价标志位为1的情况下才能使用。
在此模块中,按下数值的加键,会改变单价与起步价,当调节单价的标志位为1,就可以调节单价,当调节起步价的标志位为1,就可以调节起步价。
Fee_Flag和price_flag为起步价和单价设置的标志位,set为价格加的按键,reset为复位,clk为系统时钟。Fee为调整好的输出按键值,price为调整好的单价输出,fee为调节好的起步价输出。根据要求其起步价与单价设置的模块如图3-18所示。
计费模块
将设置好的起步价、单价传到计费模块中,以此为基础计费。
Select_clk为数据选通器也就是说,在控制价格加1的模块中运用assign语句,用作数据选通器。当flag为1时,select_clk选择distance_enable,也就是说当数据选则distance_enable时,距离加1公里就计费,当数据选通器选择time_enable时,就是等候时间计费。
fee为最后输出的价格,传输到数据切换数据选择模块中。根据设计其计费总体模块如图3-19所示。
系统的测试
系统的硬件调试
开机时,FPGA系统板状态显示全0,如图4-6所示。于是按下按键到调起步价模式,设置起步价为5元,如图4-7所示。
图4-6 开机状态 图4-7 设置起步价模式
再次按下按键调单价,设置单价为1元/km,如图4-8所示。于是切换到里程、时间、计价模块显示,并按下reset开始,此时是按时间收费,此时显示的是起步价5元,中间四位显示的是等待时间,由于一开始的状态是时间计费,有时间走动。所以显示为1s。于是按下start切换路程计费模式,,此时初始状态如图4-9所示
当50s时间一过时,路程加1公里,此时数码管显示为行程记1km,而价格没有变化,因为还在三公里以内。如图4-10所示。当行程变化为2公里时,总价还是为5元,知道行程变为4公里,总价加1元。如图4-11所示,达到5公里价格也加1,如图4-12所示。
当计费模式切换至等候时间计费模式下,这时发现,当等候时间比2分钟大的时候,才会收费,并且按照1分钟一块钱的方式收费。如图3-13所示。在时间计费模式下,当时间到达4分钟以上时,费用达到10元,于是切换至行程计费,此时路程继续前行,路程达到6公里时,价格又加1元。如图4-14所示。
总结
问题与解决办法
本设计基本达到要求,经过仿真测试与硬件调试,都能够取得良好的效果。但是在设计过程中仍旧有不小的麻烦。
第一步设计方案时,要设计好系统框图,这是FPGA设计的必要条件,但是对于我而言一个人设计这样一个复杂的系统,是十分困难的,而且FPGA Verilog HDL语言不同于C语言,这又是本次设计的一大难点。
第二步进行代码编写的时候,由于采用的是顶层模块的编写,模块的输入输出的对应关系也是一大难点,显示生活中的记程可以达到几百公里,而本设计的公里数仅仅只能达到99公里,这是本设计的一大缺陷,为了使得本设计能够真正的投入使用,毕业设计并不是一个终点,因此在今后还需找机会进行完善设计。
在采用何种方式测量汽车的转速是本设计的另一大难点,于是联想到霍尔传感器可以将轮胎的转速转化为脉冲信号,因此想到运用频率计进行测量脉冲的个数,进而可以测量速度。由于本次实验选择的系统板内存有限,不能进行大量的运算,并没有将车速与行程进行联系。这是本次设计的一大缺陷,但是不妨碍设计完成所有的功能测试。
车速还需要一个外围电路产生,通过霍尔传感器转化为脉冲信号给FPGA进行测量,但是由于在家条件有限,并不能实现这样的效果,只能进行仿真测试系统的准确性。
设计总结
本设计基本达到要求,经过仿真测试与硬件调试,都能够取得良好的效果。但是在设计过程中仍旧有不小的麻烦。
第一步设计方案时,要设计好系统框图,这是FPGA设计的必要条件,但是对于我而言一个人设计这样一个复杂的系统,是十分困难的,而且FPGA Verilog HDL语言不同于C语言,这又是本次设计的一大难点。
第二步进行代码编写的时候,由于采用的是顶层模块的编写,模块的输入输出的对应关系也是一大难点,显示生活中的记程可以达到几百公里,而本设计的公里数仅仅只能达到99公里,这是本设计的一大缺陷,为了使得本设计能够真正的投入使用,毕业设计并不是一个终点,因此在今后还需找机会进行完善设计。
在采用何种方式测量汽车的转速是本设计的另一大难点,于是联想到霍尔传感器可以将轮胎的转速转化为脉冲信号,因此想到运用频率计进行测量脉冲的个数,进而可以测量速度。由于本次实验选择的系统板内存有限,不能进行大量的运算,并没有将车速与行程进行联系。这是本次设计的一大缺陷,但是不妨碍设计完成所有的功能测试。
车速还需要一个外围电路产生,通过霍尔传感器转化为脉冲信号给FPGA进行测量,但是由于在家条件有限,并不能实现这样的效果,只能进行仿真测试系统的准确性。
本设计是我帮助别人做的一次毕业设计,时间仓促,但基本功能都已实现,如果能够模拟转速测量频率,去测量速度与距离。效果会更好。另外附上代码。
代码
顶层文件
module taxi(clk_50M, reset,start,a,b,c,d,e,f,g,p,sel,pluse,led,key,set); // 端口的定义
input clk_50M,reset,start,pluse,key,set;//总的时钟信号,复位信号,开始信号
output[7:0] sel;//数码管的输出
output a,b,c,d,e,f,g,p;
output led;
wire led;
wire [7:0]distance;//公里
wire [7:0] s;//秒
wire [7:0] m;//分
wire [7:0] fee;//费用
wire [3:0] rprice;
wire [7:0]rfee;
wire [31:0]q;
wire [3:0]q0,q1,q2,q3,q4,q5,q6,q7;
wire [3:0]DH,DL,MH,ML,SH,SL,FH,FL;
wire distance_enable; //公里控制费用的信号
wire time_enable; //时间控制费用的信号
wire select_clk; //控制信号
wire hz1,hz2; //数码管的时钟
wire hz; //计数时钟
wire clk_key;
wire timer;
wire key_reg,rkey_reg,rkey_set;
wire module_Flag,SPEED_Flag,flag,price_Flag,fee_Flag,beep_flag;
//*模块的调用*//
div_clk u0(.clk(clk_50M),.fs(1),.cko(hz));//调用计数分频模块
div_clk u1(.clk(clk_50M),.fs(500),.cko(hz1));//调用数码管分频模块
div_clk u2(.clk(clk_50M),.fs(1000),.cko(clk_key));//调用时钟消抖分频模块
control u3(.flag(flag),.distance_enable(distance_enable),.time_enable(time_enable),
.select_clk(select_clk));
distancemokuai u4(.clk(hz),.flag(flag),.reset(reset),.distance(distance),
.distance_enable(distance_enable),.module_Flag(module_Flag));//调用计程模块
timemokuai u5(.clk(hz),.reset(reset),.flag(flag),.s(s),.m(m),
.time_enable(time_enable));//调用计时模块
feemokuai u6(.reset(reset),.price(rprice),.fee(fee),.s_fee(rfee),.select_clk(select_clk),.clk(hz));//调用计费模块
feeprice_set u7(.fee_Flag(fee_Flag),.price_Flag(price_Flag),.set(rkey_set),.reset(reset),
.clk(clk_50M),.fee(rfee),.price(rprice));
scan_led u8
(
.clk(hz1),
.DA(DH), .DB(DL), .DC(MH), .DD(ML), .DE(SH), .DF(SL), .DG(FH), .DH(FL),
.a(a), .b(b), .c(c), .d(d), .e(e), .f(f), .g(g), .p(p), .sel(sel)
);
count_in u9(.clk_in(pluse),.q(q),.timer(timer));
count_cnt u10(.clk(hz),.q(q),.q0(q0),.q1(q1),.q2(q2),.q3(q3),.q4(q4),.q5(q5),.q6(q6),.q7(q7),
.timer(timer),.led(led),.beep_flag(beep_flag));
key_shake u11(.clk(clk_key), .key_in(key), .key_out(key_reg));
key_shake u12(.clk(clk_key), .key_in(start), .key_out(rkey_reg));
key_shake u13(.clk(clk_key), .key_in(set), .key_out(rkey_set));
key_control u14(.key(key_reg),.clk(clk_50M),.module_Flag(module_Flag),.SPEED_Flag(SPEED_Flag),
.price_Flag(price_Flag),.fee_Flag(fee_Flag));
key_control2 u15(.start(rkey_reg),.clk(clk_50M),.flag(flag),.reset(reset),.module_Flag(module_Flag));
switch u16(.clk(clk_50M),.distance(distance),.s(s),.m(m),
.q0(q0),.q1(q1),.q2(q2),.q3(q3),.q4(q4),.q5(q5),.q6(q6),.q7(q7),
.fee(fee),.s_fee(rfee),.price(rprice),.module_Flag(module_Flag),
.SPEED_Flag(SPEED_Flag),.fee_Flag(fee_Flag),.price_Flag(price_Flag),
.DH(DH),.DL(DL),.MH(MH),.ML(ML),.SH(SH),.SL(SL),.FH(FH),.FL(FL));
endmodule//结束顶层模块
分频
module div_clk(clk,fs,cko);
input clk; //输入时钟频率clk=50M
input [31:0]fs;
output cko; //输出时钟cko
reg cko;
parameter N=50_000_000; //定义累加器上限,需要与基准频率f0相等
reg [31:0]ACC; //定义32位累加器ACC
always @(posedge clk) begin //累加器在clk上升沿触发完成累加
if(ACC<N/2-1) ACC = ACC + fs; //累加器按参数步进值累加,注意不是+1
else begin //满足累加器溢出条件
ACC=0;
cko=!cko; //这里cko自身二分频,所以上一句N需要除以2
end
end
endmodule
控制
module control(flag,distance_enable,time_enable,select_clk);
input flag,distance_enable,time_enable;
output select_clk;//输出选择的时钟信号
wire select_clk;
//*当start高电平的时候选择公里计费,输出的时钟信号为distance_enable,当start低电平的时候选择时间计费,输出的时钟信号为time_enable*//
assign select_clk=flag?time_enable:distance_enable;
endmodule//结束控制模块
距离模块
module distancemokuai(clk,flag,reset,distance,distance_enable,module_Flag); //端口的定义
input clk,flag,reset,module_Flag;
output [7:0] distance; // 输出的公里
reg [7:0] distance;
output distance_enable; // 控制计费的公里信号
wire distance_enable;
reg [7:0]cnt;
always@(posedge clk or negedge reset) //异步复位
begin
if(!reset) //低电平复位
begin
distance<=8'H00;
end
else if((flag==1'b0)&&(module_Flag==1'b1))
begin
if(cnt>8'd50)
begin
cnt=0;
if(distance[3:0]==4'B1001) //判断distance的低四位计到了9没有
begin
distance[3:0]<=4'B0000; //计到9清零
if(distance[7:4]==4'B1001) //判断distance的高四位计到了9没有
distance[7:4]<=4'B0000; // //计到9清零
else
distance[7:4]<= distance[7:4]+4'B0001; // distance的高四位没有计到9的时候加一
end
else
begin
distance[3:0]<=distance[3:0]+4'B0001; // distance的低四位没有计到9的时候加一
end
end
else begin cnt=cnt+1; end
end
end
/*
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
distance_enable<=1'b0; //复位
end
else
if(distance[7:0]>8'H01)
begin
distance_enable=1'b1; //输出distance_enable信号
end
end
*/
assign distance_enable=((distance[7:0]>8'H02)&&(cnt==8'd50))?1'b1:1'b0;
endmodule
时间
module timemokuai(clk,reset,flag,s,m,time_enable);// 端口的定义
input clk,reset,flag;
output [7:0] s; //输出的秒
output [7:0] m; //输出的分
output time_enable; //输出的控制计费的信号
reg [7:0] s;
reg [7:0] m;
wire time_enable;
always@(posedge clk or negedge reset) //异步复位
begin
if(!reset) //低电平有效
begin //复位
s<=8'H00;
m<=8'H00;
end
else if(flag==1'b1)
begin
if(s[3:0]==4'B1001) // 秒的低四位是9
begin
s[3:0]<=4'B0000; //清零
if(s[7:4]==4'B0101) // 秒的高四位是5
begin
s[7:4]<=4'B0000; //清零
if(m[3:0]==4'B1001) // 分的低四位是9
begin
m[3:0]<=4'B0000; //清零
if(m[7:4]==4'B1001) // 分的高四位是9
m[7:4]<=4'B0000; //清零
else
m[7:4]<=m[7:4]+4'B0001; // 分的高四位不是9加一
end
else
m[3:0]<=m[3:0]+4'B0001; //分的低四位不是9加一
end
else s[7:4]<=s[7:4]+4'B0001; // 秒的高四位不是5加一
end
else s[3:0]<=s[3:0]+4'B0001; //秒的低四位不是9加一
end
end
assign time_enable=((m[7:0]>8'H01)&&(s[7:0]==8'H00))?1'b1:1'b0;
endmodule
计费
module feemokuai(price,s_fee,select_clk,reset,fee,clk);
input select_clk,reset,clk;
input [3:0]price;
input [7:0]s_fee;
output[7:0] fee;//输出的费用
reg [7:0] fee;
always@(posedge clk or negedge reset) //异步复位
begin
if(!reset) //低电平有效
begin
fee<=s_fee; //设置的起步价
end
else if(select_clk==1'b1)
begin
if(fee[3:0]>=4'B1001)//费用的低四位是不是计到了9
begin
fee[3:0]<=4'B0000;//计到9清零
if(fee[7:4]==4'B1001)// 费用的高四位是不是计到了9
fee[7:4]<=4'B0000; //计到9清零
else fee[7:4]<=fee[7:4]+1'B1;// 费用的高四位没有计到9加1
end
else fee[3:0]<=fee[3:0]+price[3:0];// 费用的低四位没有计到9加1
end
end
endmodule //结束计费模块
费用设置
module feeprice_set(fee_Flag,price_Flag,set,reset,clk,fee,price);
input fee_Flag,price_Flag,set,reset,clk;
output[7:0]fee;
output[3:0]price;
reg[7:0]fee;
reg[3:0]price;
reg set_f;
always@(posedge clk or negedge reset)
begin
if(!reset)begin set_f=0; end
else if(fee_Flag==1'b1)
begin
if(set==0)
begin
if(set_f==1)
begin
if(fee[7:0]==8'H99) fee=8'H00;
else if(fee[3:0]==4'H9) fee=fee+8'H07;
else fee=fee+8'H01;
set_f=0;
end
end
else begin set_f=1;end
end
else if(price_Flag==1'b1)
begin
if(set==0)
begin
if(set_f==1)
begin
if(price[3:0]==4'H9) price=0;
else price=price+4'H1;
set_f=0;
end
end
else begin set_f=1;end
end
end
endmodule
数码管扫描
module scan_led(clk,DA,DB,DC,DD,DE,DF,DG,DH,a,b,c,d,e,f,g,p,sel);
input clk;//数码管扫描时钟
input [3:0]DA,DB,DC,DD,DE,DF,DG,DH;
output a,b,c,d,e,f,g,p;
output [7:0]sel;
reg [2:0]scan;
reg[7:0]sel;
reg [3:0]num;
always @(posedge clk) begin
begin
scan=scan+1;
end
case(scan) //3-8译码器 和 数据选择器
0: begin sel=8'b1111_1110; num=DA; end
1: begin sel=8'b1111_1101; num=DB; end
2: begin sel=8'b1111_1011; num=DC; end
3: begin sel=8'b1111_0111; num=DD; end
4: begin sel=8'b1110_1111; num=DE; end
5: begin sel=8'b1101_1111; num=DF; end
6: begin sel=8'b1011_1111; num=DG; end
7: begin sel=8'b0111_1111; num=DH; end
endcase
end
assign {a,b,c,d,e,f,g,p} = //字符译码器
(num==0)? 8'B0000_0011: //"0"
(num ==1)? 8'B1001_1111: //"1"
(num ==2)? 8'B0010_0101: //"2"
(num ==3)? 8'B0000_1101: //"3"
(num ==4)? 8'B1001_1001: //"4"
(num ==5)? 8'B0100_1001: //"5"
(num ==6)? 8'B0100_0001: //"6"
(num ==7)? 8'B0001_1111: //"7"
(num ==8)? 8'B0000_0001: //"8"
(num ==9)? 8'B0000_1001: //"9"
(num==10)? 8'B1111_1101: //"-"
8'B1111_1111; //熄灭
endmodule
计数
module count_in(clk_in,q,timer);
input clk_in,timer;
output [31:0]q;
reg[31:0]q;
reg led;
always @(posedge clk_in or negedge timer) begin
if(!timer) begin q=0; end
else begin
if((q[15:0]==16'H9999)&&(q[31:16]==16'H9999)) q=0;
else if(q[27:0]==28'H9999999) q=q+32'H6666667;
else if(q[23:0]==24'H999999) q=q+32'H666667;
else if(q[19:0]==20'H99999) q=q+32'H66667;
else if(q[15:0]==16'H9999) q=q+32'H6667;
else if(q[11:0]==12'H999) q=q+32'H667;
else if(q[7:0]== 8'H99) q=q+32'H67;
else if(q[3:0]== 4'H9) q=q+32'H7;
else q=q+32'H1;
end
end
endmodule
计数输出
module count_cnt(clk,q,q0,q1,q2,q3,q4,q5,q6,q7,timer,led,beep_flag);
input clk;
input [31:0]q;
output timer,led,beep_flag;
reg timer,beep_flag;
reg led;
output [3:0]q0,q1,q2,q3,q4,q5,q6,q7;
reg [3:0]q0,q1,q2,q3,q4,q5,q6,q7;
always @(posedge clk)
begin
if((q[31:28]>0)||(q[27:24]>0)||(q[23:20]>0)||(q[19:16]>0)||(q[15:12]>0)||(q[11:8]>0)||(q[7:4]>0)||(q[3:0]>0))
begin
q0=q[3:0];
q1=q[7:4];
q2=q[11:8];
q3=q[15:12];
q4=q[19:16];
q5=q[23:20];
q6=q[27:24];
q7=q[31:28];
end
else if(q[15:12]>4'd4)
begin
beep_flag=1'b1;
end
timer = !timer;
led=!led;
end
endmodule
按键消抖
module key_shake(clk,key_in,key_out);
input clk; //1000Hz时钟
input key_in; //物理按键输入
output key_out; //去抖之后输出
reg [6:0]c; //计数变量
reg key_out;
always @(posedge clk) begin
if(!key_in)
c=c+1; //计数延迟消抖
else c=0;
if(c==100)
key_out=0;
else key_out=1;
end
endmodule
按键控制
module key_control(key,clk,module_Flag,SPEED_Flag,price_Flag,fee_Flag);
input clk, key;
output module_Flag,SPEED_Flag,price_Flag,fee_Flag;
reg key_f;//合理利用标志位,实现单次加减1操作
reg module_Flag,SPEED_Flag,price_Flag,fee_Flag;
reg [2:0]state;
always @(posedge clk) begin
if(key==0)
begin
if(key_f)
begin
state_add();
key_f=0;
end
end
else
begin
state=state;
key_f=1;
end
end
always @(posedge clk)
begin
case(state)
0: begin module_Flag=1'b0; SPEED_Flag=1'b0; price_Flag=1'b0; fee_Flag=1'b0;end
1: begin module_Flag=1'b0; SPEED_Flag=1'b0; price_Flag=1'b0; fee_Flag=1'b1;end
2: begin module_Flag=1'b0; SPEED_Flag=1'b0; price_Flag=1'b1; fee_Flag=1'b0;end
3: begin module_Flag=1'b1; SPEED_Flag=1'b0; price_Flag=1'b0; fee_Flag=1'b0;end
4: begin module_Flag=1'b0; SPEED_Flag=1'b1; price_Flag=1'b0; fee_Flag=1'b0;end
default: begin module_Flag=1'b0; SPEED_Flag=1'b0; price_Flag=1'b0; fee_Flag=1'b0;end
endcase
end
task state_add();
begin
if(state==3'B100) state=3'B000;
else state=state+1;
end
endtask
endmodule
按键控制2
module key_control2(start,clk,flag,reset,module_Flag);
input start,clk,reset,module_Flag;
output flag;
reg flag;
reg key_f;
reg state;
always@(posedge clk or negedge reset)
begin
if(!reset) begin key_f=1;state=0;end
else if(module_Flag==1'B1)
begin
if(start==1)
begin
if(key_f)
begin
state=state+1'b1;
key_f=0;
end
end
else
begin
state=state;
key_f=1;
end
end
end
always@(posedge clk)
begin
case(state)
0:begin flag=0;end
1:begin flag=1;end
default:begin flag=0;end
endcase
end
endmodule
模式选择
module switch(clk,distance,s,m,q0,q1,q2,q3,q4,q5,q6,q7,fee,s_fee,price,module_Flag,SPEED_Flag,fee_Flag,price_Flag,DH,DL,MH,ML,SH,SL,FH,FL);
input clk;
input module_Flag,SPEED_Flag,fee_Flag,price_Flag;
input[7:0] distance,fee;//输入的公里,费用
input[7:0] s;//输入的秒
input[7:0] m;//输入的分
input[7:0] s_fee;
input [3:0]price;
input [3:0]q0,q1,q2,q3,q4,q5,q6,q7;
output [3:0]DH,DL,MH,ML,SH,SL,FH,FL;
reg [3:0]DH,DL,MH,ML,SH,SL,FH,FL;
always @(posedge clk)
begin
if(module_Flag)
begin
DH[3:0]<=distance[7:4];
DL[3:0]<=distance[3:0];
MH[3:0]<=m[7:4];
ML[3:0]<=m[3:0];
SH[3:0]<=s[7:4];
SL[3:0]<=s[3:0];
FH[3:0]<=fee[7:4];
FL[3:0]<=fee[3:0];
end
else if(SPEED_Flag)
begin
DH[3:0]<=q7[3:0];
DL[3:0]<=q6[3:0];
MH[3:0]<=q5[3:0];
ML[3:0]<=q4[3:0];
SH[3:0]<=q3[3:0];
SL[3:0]<=q2[3:0];
FH[3:0]<=q1[3:0];
FL[3:0]<=q0[3:0];
end
else if(fee_Flag)
begin
DH[3:0]<=0;
DL[3:0]<=0;
MH[3:0]<=0;
ML[3:0]<=0;
SH[3:0]<=0;
SL[3:0]<=0;
FH[3:0]<=s_fee[7:4];
FL[3:0]<=s_fee[3:0];
end
else if(price_Flag)
begin
DH[3:0]<=0;
DL[3:0]<=0;
MH[3:0]<=0;
ML[3:0]<=0;
SH[3:0]<=0;
SL[3:0]<=0;
FH[3:0]<=0;
FL[3:0]<=price[3:0];
end
else
begin
DH[3:0]<=distance[7:4];
DL[3:0]<=distance[3:0];
MH[3:0]<=m[7:4];
ML[3:0]<=m[3:0];
SH[3:0]<=s[7:4];
SL[3:0]<=s[3:0];
FH[3:0]<=fee[7:4];
FL[3:0]<=fee[3:0];
end
end
endmodule
quartus代码其实很难理解的,如果大家想采用这样的思路,希望大家把顶层文件看懂,才好下手,代码全都附上去了,没有任何隐藏。如果大家对利用FPGA设计TAXI计费有需求,大家可以参考我的思路。谢谢!