基于EP3C40F780C8-FPGA-的出租车计价器
基于EP3C40F780C8 FPGA 的出租车计价器
基于EP3C40F780C8 verilog语言 FPGA 的出租车计价器
本次出租车计费系统设计最终具有以下功能:
分辨率为1s的误时系统,1km里程的路程模块,同时将费用通过计算显示在数码管上面可以实现,路程和时间同时计费,以及含有两个按键输入,用来判断车辆是否在行驶过程中,以及是否开始计费,计费模块每1km,往上加1.3元,每1.5min,往上加1.5元。同时,用户可以从数码管看到费用,里程等信息。数码管的前两位是里程信息,后两位是时间信息,最后四位显示的则是费用信息。费用信息是通过一个500hz的动态数码管显示的。
文章目录
一、系统模块
1.时钟分频模块
代码如下(示例):
module div_clk(clk,fs,clko);
input clk; //输入系统时钟频率clk=50M
input wire[31:0] fs;
output reg clko; //输出时钟clko
parameter N=25_000_000; //注意N取值是系统时钟的二分频
reg[31:0] Add;
always@(posedge clk)
begin
if(Add < N/fs)
Add = Add + 1;
else
begin //满足累加器溢出条件
Add = 0;
clko = !clko;
end
end
endmodule //这里的分频fs值为多少,则分多少频率,但是有范围1—25000000
2.时间模块
代码如下:
module timewait (clk,reset,pluse_flag,min,time_enable);//clk为1HZ脉冲60个脉冲为1分钟
input clk;
input reset;
input pluse_flag;
output [7:0] min; //输出的分
output time_enable;//输出的控制计费的信号
reg [7:0] min;
reg [7:0]cnt;
always@(posedge clk or negedge reset)//异步复位
begin
if(!reset)//低电平有效
begin//复位
min<=8'H00;
end
else if (pluse_flag ==1'b1)
begin
if(cnt>8'd59)//用于确定一分钟的分辨率
begin
cnt=0;
if(min[3:0]==4'B1001)//分的低四位是9
begin
min[3:0]<=4'B0000;//清零
if(min[7:4]==4'B0101)//分的高四位是5
min[7:4]<=4'B0000;//清零
else
min[7:4]<=min[7:4]+4'B0001;//分高四位非5加1
end
else
min[3:0]<=min[3:0]+4'B0001;//分的低四位不是9加一
end
else
begin
cnt=cnt+1'b1;
end
end
end
assign time_enable=((min[7:0]>8'b0000_0010)?1'b1:1'b0) ;//标志位为1代表时间超过两分钟,开始计费
endmodule
由于出租车在行驶的过程中,会出现等候乘客、以及等候红绿灯的情况,这种情况仍然需要成本,按照设计要求超过两分钟,每分钟计费1.5元。clk为输入1hz信号,reset为复位按键,pluse_flag为选择等候时间计费信号,min为分的两个数据,time_enable是时间到达2分钟的标志。对于时间模块的操作则是每当来一个时钟脉冲,就开始判断pluse_flag是否为1,若为1则说明,系统开始停车,开始采用时间计费,同时,time_enable用来存储时间min是否超过2的标志位。
3. 里程模块
代码如下:
module distance (wheel,start_flag,reset, distance_enable,distance);//端口的定义
input wheel,start_flag,reset;
output distance_enable;
output [7:0]distance;
reg [7:0]distance;
reg [8:0]cnt;
always @( posedge wheel or negedge reset)//车轮脉冲和异步复位配合按键决定是否开始计算里程
begin
if(!reset)//低电平复位
begin
distance<=8'H00;
end
else if(start_flag==1'b1)
begin
if(cnt>9'd500)//用于确定distance分辨率
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
distance[3:0]<=distance[3:0]+4'B0001;//distance的低四位没有计到9的时候加一
end
else
begin
cnt=cnt+1'b1;
end
end
end
assign distance_enable=( distance [7:0]>8'b0000_0011)?1'b1:1'b0;//标志位为1代表里程超过三公里,开始按照第二种方案计费
endmodule
由于硬件实物的板子内存有限,乘除运算会占用大量内存,因此本设计假设车轮20m给一个脉冲信号wheel,于是500个脉冲是1km,在3km之内是起步价,当公里数超过3km之后,按照1km收取1.3元的计算。本模块中cnt在1hz的触发下计数,1hz加一次,当加满500次时,distance加1,实现行程加1km,行程超过3km以上,1km收取1.3元。wheel为车轮脉冲信号,start_flag是开始整体计费模块,reset为复位, distance是输出的行程,distance_enable是三公里到达的标志。
4.费用模块
费用模块在本次设计中其实是分为三个模块的,首先介绍的是时间费用模块:时间费用模块便是根据时间模块的输出量time_enable和clk脉冲信号进行计算的,先由系统判断是否是误时计费状态,如果是,那就对feepay_time进行自加15操作,来对应实际操作中的每分钟15元。时间费用模块框图如u5所示,与时间费用模块类似的是里程费用模块,里程模块框图如u4所示:
代码如下:
module feepay_time ( reset,fee_time, clk,pluse_flag, time_enable);//时间计费模块1hz脉冲,60个为一分钟
input reset;
input clk;
input time_enable;
input pluse_flag;
output[14:0] fee_time;
reg [14:0] fee_time;
reg [5:0] cnt;
always@(posedge clk or negedge reset)
begin
if(!reset)
fee_time<=0;
else if (cnt >= 60)
begin
cnt<=0;
if ( time_enable>=1 && pluse_flag )
begin
if( fee_time>=9999 )
begin
fee_time<=0;
end
else
fee_time<=fee_time+15;
end
else
fee_time<=fee_time;
end
else
cnt=cnt+1'b1;
end
endmodule //结束时间计费模块
///
module feepay ( reset,fee, wheel, distance_enable,pluse_flag);//里程费用模块
input reset;
input wheel;
input distance_enable;
input pluse_flag;
output[14:0] fee;
reg [14:0] fee;
reg [8:0] cnt;
always@(posedge wheel or negedge reset)
begin
if(!reset)
fee<= 0;
else if (cnt >= 500) //38
begin
cnt=0;
if ( distance_enable==1 && pluse_flag ==0 )
begin
if ( fee >= 9999 )
begin
fee <= 0;
end
else
fee<=fee+13;
end
else
fee<= fee;
end
else
cnt=cnt+1'b1;
end
endmodule //里程费用模块结束
/
module fee(start_flag,clk, reset,fee,fee_time,fee_a,fee_b,fee_c,fee_d);//整体费用模块端口的定义
input clk,reset,start_flag;
input [14:0]fee;
input [14:0]fee_time;
output [3:0]fee_a;
output [3:0]fee_b;
output [3:0]fee_c;
output [3:0]fee_d;
reg[3:0] fee_a;
reg[3:0] fee_b;
reg[3:0] fee_c;
reg[3:0] fee_d;
reg[14:0] fee_deal;
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
fee_a=0;
fee_b=0;
fee_c=0;
fee_d=0;
end
else if(start_flag==1)
begin
fee_deal=fee+fee_time+50;
fee_a[3:0]=fee_deal/1000;
fee_b[3:0]=(fee_deal-1000*fee_a)/100;
fee_c[3:0]=(fee_deal-1000*fee_a-100*fee_b)/10;
fee_d[3:0]=fee_deal%10;
end
else
begin
fee_a[3:0]<= fee_a[3:0];
fee_b[3:0]<= fee_b[3:0];
fee_c[3:0]<= fee_c[3:0];
fee_d[3:0]<= fee_d[3:0];
end
end
endmodule
5.显示模块
代码如下:
module scan_led(clk,DA,DB,DC,DD,DE,DF,DG,DH,a,b,c,d,e,f,g,p,scan);
input clk;//数码管扫描时钟 共阴极
input [3:0] DA,DB,DC,DD,DE,DF,DG,DH;
output a,b,c,d,e,f,g,p;
output reg[2:0] scan;
reg [7:0]sel;
reg [4:0]num;
always@(posedge clk)
begin
scan<=scan+1'b1;
case(scan) //3-8译码器 和 数据选择器
0: begin num=DB; end
1: begin num=DC; end
2: begin num=DD; end
3: begin num=DE; end
4: begin num=DF; end
5: begin num=DG+10; end//要加小数点位
6: begin num=DH; end
7: begin num=DA; end
endcase
end
assign {a,b,c,d,e,f,g,p} = //字符译码器
(num ==0)? 8'b11111100: //"0"
(num ==1)? 8'b01100000: //"1"
(num ==2)? 8'b11011010: //"2"
(num ==3)? 8'b11110010: //"3"
(num ==4)? 8'b01100110: //"4"
(num ==5)? 8'b10110110: //"5"
(num ==6)? 8'b10111110: //"6"
(num ==7)? 8'b11100000: //"7"
(num ==8)? 8'b11111110: //"8"
(num ==9)? 8'b11110110: //"9"
(num ==10)? 8'b11111101: //"0."
(num ==11)? 8'b01100001: //"1."
(num ==12)? 8'b11011011: //"2."
(num ==13)? 8'b11110011: //"3."
(num ==14)? 8'b01100111: //"4."
(num ==15)? 8'b10110111: //"5."
(num ==16)? 8'b10111111: //"6."
(num ==17)? 8'b11100001: //"7."
(num ==18)? 8'b11111111: //"8."
(num ==19)? 8'b11110111: //"9."
8'b00000000; //"-";
endmodule
显示方面,由于测试的开发板内部集成有3-8译码器,所以在输出的时候只写了一个三位的scan。
二、顶层模块以及实测结果
顶层模块代码如下所示:
module top(clk_50M, reset,start_flag,a,b,c,d,e,f,g,p,scan,wheel,pluse_flag); // 端口的定义
input clk_50M,reset,start_flag,pluse_flag,wheel;//总的时钟信号,复位信号,开始信号,等待信号
output [2:0] scan;//数码管的输出
output a,b,c,d,e,f,g,p;
wire [7:0]distance;//公里
wire [7:0] min;//分
wire [14:0] fee;
wire [14:0] fee_time;
wire [3:0] fee_a;
wire [3:0] fee_b;
wire [3:0] fee_c;
wire [3:0] fee_d;
wire distance_enable; //公里控制费用的信号
wire time_enable; //时间控制费用的信号
wire time_hz,led_hz;
wire DA,DB,DC,DD,DE,DF,DG,DH;
div_clk u0(.clk(clk_50M),.fs(60),.clko(time_hz));//调用计数分频模块 1Hz
div_clk u1(.clk(clk_50M),.fs(350),.clko(led_hz));//调用数码管分频模块 350Hz
distance u2(.wheel(wheel),.start_flag(start_flag),.reset(reset),.distance_enable(distance_enable),.distance(distance));//调用里程模块
timewait u3(.clk(time_hz),.reset(reset),.pluse_flag(pluse_flag),.min(min), .time_enable(time_enable));//调用计时模块
feepay u4(.reset(reset),.fee(fee),.wheel(wheel),.distance_enable(distance_enable),.pluse_flag(pluse_flag));
feepay_time u5(.reset(reset),.fee_time(fee_time),.pluse_flag(pluse_flag),.clk(time_hz),.time_enable(time_enable));
fee u6(.start_flag(start_flag),.clk(clk_50M),.reset(reset),.fee(fee),.fee_time(fee_time),.fee_a(fee_a),.fee_b(fee_b),.fee_c(fee_c),.fee_d(fee_d));
scan_led u7
(
.clk(led_hz),
.DA(distance[7:4]), .DB(distance[3:0]), .DC(min[7:4]), .DD(min[3:0]), .DE(fee_a), .DF(fee_b), .DG(fee_c), .DH(fee_d),
.a(a), .b(b), .c(c), .d(d), .e(e), .f(f), .g(g), .p(p), .scan(scan)
);
endmodule
顶层模块主要是对系统模块的调用
上图是整体的RTL级原理图
下图在实测结果中总费用是5+3 1.3+6 1.5=17.9元,所以实验结果也符合预期。
主要参考文献
(1)谭会生、瞿遂春,《EDA技术综合应用实例与分析》,西安电子科技大学出版社,2004
(2)曹昕燕、周凤臣等,《EDA技术实验与课程设计》,清华大学出版社,2006