基于FPGA的出租车计费系统设计---第一版--郝旭帅电子设计团队
欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的出租车计费系统设计---第一版
功能说明:
1. 收费标准(里程):起步价5元,包括三公里;三公里之后,每公里2元(不到一公里,不收费)。
2. 收费标准(低速等待费):当计费开始,车辆速度低于某一速度时,开始收取低速等待费,三分钟收取2元,不足三分钟不收费
3. 整体收费=里程计费+低速等待费
4. 设置有计费开始/停止按钮。
5. 设置有档位控制按钮,0档(停止不动),1档(3分钟一公里),2档(1分钟一公里)。当计费开始,0档和1档位需要累加低速等待计费。
6. 利用六个数码管显示信息。
7. 显示模式一:第一个数码管显示是否计费(A:计费停止, C:计费开始),第二个数码管显示档位(0、1、2),第三个至六个显示应收钱数。
8. 显示模式二:总计运行时间
9. 显示模式三:前三个里程,后三个里程收费
10. 显示模式四:前三个低速等待时长(分钟),后三个低速等待费。
11. 设置有显示模式切换按钮。
12. 计费停止后,重新按下计费开始,上述所有统计从0开始。
13. 为了下板后,能够比较快速显示出效果,所有的时间加速30倍.
使用平台:本次设计应用Altera的平台设计(芯片:EP4CE10F17C8N)。
仿真平台:Modelsim。
作者QQ:746833924
说明:本篇设计中不涉及到IP和原语,代码在其他平台依然可以适用;当其他板卡电路不同时,会导致不同的现象出现,如有需要修改代码请联系作者;如需作者使用的板卡,请联系作者;
设计思想如下:
key_ctrl模块负责将外部输入的按键进行消抖,产生按键按下时的脉冲。
taxi_meter_ctrl模块负责根据外部输入的三个脉冲和出租车计费的规则产生对应的输出数据。
seven_tube_drive模块负责将taxi_meter_ctrl产生的数据显示到数码管上。
key_ctrl模块设计思想为:按键信号是由外部机械式按键产生,每次按下或者抬起时,会产生一定的抖动。如果直接对其进行边沿检测就会导致多次触发。故而需要设计按键消抖,进而对消抖之后的波形进行边沿检测。消抖原理为:外部按键信号发生改变后,如果能够持续20ms,没有新的改变,就认为此次改变不是抖动,而是真正的按下,然后进行采样即可。
// 记录任意边沿之后没有遇到新的边沿的时间长度是否达到20Ms
//---------------------------------------------------------------------------------------
always@(posedgeclk)begin
if(rst_n ==1'b0)
cnt_20ms <=20'd0;
else
if(pulse_key_negedge ==1'b1||pulse_key_posedge ==1'b1)
cnt_20ms <=20'd1;
else
if(cnt_20ms >20'd0&&cnt_20ms <T_20ms)
cnt_20ms <=cnt_20ms +1'b1;
else
cnt_20ms <=20'd0;
end
// ---------------------------------------------------------------------------------------
// 任意边沿之后没有遇到新的边沿的时间长度达到20Ms,认为按键稳定,此时采样
//--------------------------------------------------------------------------------------
always@(posedgeclk)begin
if(rst_n ==1'b0)
key_wave <=1'b1;
else
if(cnt_20ms ==T_20ms)
key_wave <=key_rr;
else
key_wave <=key_wave;
end
//--------------------------------------------------------------------------------------
// 对消抖之后的按键信号进行边沿检测
//---------------------------------------------------------------------------------------------
initialkey_wave_r =1'b1;
always@(posedgeclk)key_wave_r <=key_wave;
assignflag_neg =(key_wave_r ==1'b1&&key_wave ==1'b0)?1'b1:1'b0;
assignflag_pos =(key_wave_r ==1'b0&&key_wave ==1'b1)?1'b1:1'b0;
//--------------------------------------------------------------------------------------------
taxi_meter_ctrl模块的设计原理如下:
根据flag_start_stop的信号,确定运行状态。
always @ (posedge clk) begin if (rst_n == 1'b0) state_run <= 1'b0; else if (flag_start_stop == 1'b1) state_run <= ~state_run; else state_run <= state_run; end
根据flag_gear的信号,确定出租车的档位。
always @ (posedge clk) begin if (rst_n == 1'b0) gear <= 2'd0; else if (flag_gear == 1'b1) if (gear < 2'd2) gear <= gear + 1'b1; else gear <= 2'd0; else gear <= gear; end
确定公里数,以及根据规则,确定钱数。
停止运行了后,公里数不要清除(也需要翻看),公里数需要在下一次启动时,清除;
flag_mileage_add是公里数递增的脉冲。
always @ (posedge clk) begin if (rst_n == 1'b0) mileage <= 10'd0; else if (state_run == 1'b0 && flag_start_stop == 1'b1) mileage <= 10'd0; else if (state_run == 1'b1) if (flag_mileage_add == 1'b1) if (mileage < 10'd999) mileage <= mileage + 1'b1; else mileage <= mileage; else mileage <= mileage; else mileage <= mileage; end always @ (posedge clk) begin if (rst_n == 1'b0) money_mileage <= 10'd0; else if (mileage < 10'd4) money_mileage <= 10'd5; else money_mileage <= (mileage - 10'd3) * 2 + 5; end
产生flag_mileage_add的逻辑为:记录三分钟,如果是0档,则计数器不动;如果是1档,则计数器加1(由于加速30倍,所以加30);如果是2档,则计数器加3(由于加速30倍,所以加90);由于每次并不是加1,所以最大值并一定正好能达到,所以超过最大值时,减去最大值。
always @ (posedge clk) begin if (rst_n == 1'b0) begin counter <= 64'd0; flag_mileage_add <= 1'b0; end else if (state_run == 1'b1) if (gear == 2'd1) if (counter < 34'd9_000_000_000 - 1'b1) begin counter <= counter + 30; flag_mileage_add <= 1'b0; end else begin counter <= counter - (34'd9_000_000_000 - 1'b1); flag_mileage_add <= 1'b1; end else if (gear == 2'd2) if (counter < 34'd9_000_000_000 - 1'b1) begin counter <= counter + 90; flag_mileage_add <= 1'b0; end else begin counter <= counter - (34'd9_000_000_000 - 1'b1); flag_mileage_add <= 1'b1; end else begin counter <= counter; flag_mileage_add <= 1'b0; end else begin counter <= 64'd0; flag_mileage_add <= 1'b0; end end
记录整体的运行时间;加速30倍;
always @ (posedge clk) begin if (rst_n == 1'b0) cnt_1s_all_timer <= 26'd0; else if (state_run == 1'b1) if (cnt_1s_all_timer < T_1s - 1'b1) cnt_1s_all_timer <= cnt_1s_all_timer + 30; else cnt_1s_all_timer <= cnt_1s_all_timer - (T_1s - 1'b1); else cnt_1s_all_timer <= 26'd0; end always @ (posedge clk) begin if (rst_n == 1'b0) sec_counter_all_timer <= 32'd0; else if (state_run == 1'b0 && flag_start_stop == 1'b1) sec_counter_all_timer <= 32'd0; else if (cnt_1s_all_timer >= T_1s - 1'b1) sec_counter_all_timer <= sec_counter_all_timer + 1'b1; else sec_counter_all_timer <= sec_counter_all_timer; end
确定低速等待时间;加速30倍;计数器为1分钟的计数器;
然后根据1分钟的计数器,确定等待了多长时间;
always @ (posedge clk) begin if (rst_n == 1'b0) min_wait_timer <= 32'd0; else if (state_run == 1'b1) if (gear == 2'd0 || gear == 2'd1) if (min_wait_timer < T_60s - 1'b1) min_wait_timer <= min_wait_timer + 30; else min_wait_timer <= min_wait_timer - (T_60s - 1'b1); else min_wait_timer <= min_wait_timer; else min_wait_timer <= 32'd0; end always @ (posedge clk) begin if (rst_n == 1'b0) wait_min <= 32'd0; else if (state_run == 1'b0 && flag_start_stop == 1'b1) wait_min <= 32'd0; else if (state_run == 1'b1 && (gear == 2'd0 || gear == 2'd1) && min_wait_timer >= T_60s - 1'b1) wait_min <= wait_min + 1'b1; else wait_min <= wait_min; end
根据低速等待时间,确定低速等待费。
always @ (posedge clk) begin if (rst_n == 1'b0) money_wait <= 32'd0; else money_wait <= (wait_min/3) * 2; end
最终的费用等于里程费用加上低速等待费。
always @ (posedge clk) begin if (rst_n == 1'b0) money_all <= 11'd0; else money_all <= money_mileage + money_wait; end
根据切换显示的脉冲,确定需要显示的模式;
always @ (posedge clk) begin if (rst_n == 1'b0) state_show <= 2'd0; else if (flag_show == 1'b1) state_show <= state_show + 1'b1; else state_show <= state_show; end
一个模式:显示车辆运行状态,档位和收费。
initial data0[23:20] = 4'ha; always @ (posedge clk) begin if (rst_n == 1'b0) data0[23:20] <= 4'ha; else if (state_run == 1'b1) data0[23:20] <= 4'hc; else data0[23:20] <= 4'ha; end always @ (posedge clk) begin if (rst_n == 1'b0) data0[19:16] <= 4'h0; else data0[19:16] <= {2'd0,gear}; end always @ (posedge clk) begin if (rst_n == 1'b0) data0[15:0] <= 16'd0; else data0[3:0] <= money_all % 10; data0[7:4] <= money_all/10 % 10; data0[11:8] <= money_all/100 % 10; data0[15:12] <= money_all/1000 % 10; end
第二个模式:显示整体的运行时间(换算成为时分秒)。
always @ (posedge clk) begin if (rst_n == 1'b0) data1 <= 24'd0; else data1[23:20] <= sec_counter_all_timer / 3600 / 10 % 10; data1[19:16] <= sec_counter_all_timer / 3600 % 10; data1[15:12] <= sec_counter_all_timer % 3600 / 60 / 10 % 10; data1[11:8] <= sec_counter_all_timer % 3600 / 60 % 10; data1[7:4] <= sec_counter_all_timer % 60 / 10 % 10; data1[3:0] <= sec_counter_all_timer % 60 % 10; end
第三个模式:显示历程,历程计费。
always @ (posedge clk) begin if (rst_n == 1'b0) data2 <= 24'd0; else data2[23:20] <= mileage / 100; data2[19:16] <= mileage / 10 % 10; data2[15:12] <= mileage % 10; data2[11:8] <= money_mileage / 100; data2[7:4] <= money_mileage / 10 % 10; data2[3:0] <= money_mileage % 10; end
第四个模式:显示低速等待计费和低速等待时间。
always @ (posedge clk) begin if (rst_n == 1'b0) data3 <= 24'd0; else data3[23:20] <= wait_min / 100; data3[19:16] <= wait_min / 10 % 10; data3[15:12] <= wait_min % 10; data3[11:8] <= money_wait / 100; data3[7:4] <= money_wait / 10 % 10; data3[3:0] <= money_wait % 10; end
根据外部的显示脉冲,确定需要显示的模式;
always @ (posedge clk) begin if (rst_n == 1'b0) state_show <= 2'd0; else if (flag_show == 1'b1) state_show <= state_show + 1'b1; else state_show <= state_show; end always @ (posedge clk) begin if (rst_n == 1'b0) data <= 23'd0; else case (state_show) 2'd0 : data <= data0; 2'd1 : data <= data1; 2'd2 : data <= data2; 2'd3 : data <= data3; default : data <= data0; endcase end
以上为taxi_meter_crtl的设计思想;
七段数码管为普通六位一体的共阳极数码,采用动态驱动的方式,在此不再赘述。
下板后,我们就可以看到出租车计费系统的运行情况。
讲解和演示视频(链接)如下:
https://www.bilibili.com/video/BV184421D7MT/?vd_source=b5405faeab8632f02533bcbfc5e52e55
本设计所有内容(设计代码、设计工程)链接为:
链接:https://pan.baidu.com/s/1vLDLaQckYVFUMS8hQolcaQ
提取码:01v6
本篇内容中有部分资源来源于网络,如有侵权,请联系作者。
如果您觉得本公众号还不错的话,可以推给身边的朋友们,感谢并祝好!