module TaxiFare( input clk_50MHz, // 开发板50MHz时钟 input reset, // 复位信号 input distance_btn, // 里程按键(每按=0.5公里) input time_btn, // 等待按键(每按=1分钟) output [7:0] seg_out, // 数码管段选 output [7:0] dig_sel // 数码管位选 ); // 分频模块:50MHz -> 1KHz扫描时钟 reg [15:0] clk_div; wire scan_clk = clk_div[15]; // 1KHz扫描时钟 always @(posedge clk_50MHz or posedge reset) begin if (reset) clk_div <= 0; else clk_div <= clk_div + 1; end // 按键消抖模块 wire distance_pulse, time_pulse; debounce db_dist(.clk(clk_50MHz), .btn(distance_btn), .pulse(distance_pulse)); debounce db_time(.clk(clk_50MHz), .btn(time_btn), .pulse(time_pulse)); // 计费参数 localparam BASE_PRICE = 800; // 起步价8元(800分) localparam BASE_DISTANCE = 5; // 2.5公里=5个0.5公里单位 localparam PRICE_PER_KM = 80; // 0.8元/0.5公里(1.6元/公里) localparam PRICE_PER_MIN = 100; // 1元/分钟 // 计价器核心逻辑 reg [7:0] distance_count; // 0.5公里单位计数 reg [7:0] time_count; // 分钟计数 reg [15:0] total_cents; // 总费用(分) always @(posedge clk_50MHz or posedge reset) begin if (reset) begin distance_count <= 0; time_count <= 0; total_cents <= BASE_PRICE; end else begin // 里程计数 if (distance_pulse) begin distance_count <= distance_count + 1; // 超过起步里程(2.5公里)后计费 if (distance_count >= BASE_DISTANCE - 1) total_cents <= total_cents + PRICE_PER_KM; end // 等待时间计数 if (time_pulse) begin time_count <= time_count + 1; total_cents <= total_cents + PRICE_PER_MIN; end end end // 数码管显示模块(显示金额整数部分) display_ctrl disp( .clk_scan(scan_clk), .value(total_cents/100), // 转换为元单位 .seg(seg_out), .dig(dig_sel) ); endmodule // 按键消抖模块 module debounce( input clk, input btn, output pulse ); reg [19:0] count; reg btn_reg; always @(posedge clk) begin if (btn !== btn_reg) begin btn_reg <= btn; count <= 0; end else if (count < 20'd99999) count <= count + 1; end assign pulse = (count == 20'd99999) ? btn : 0; endmodule // 数码管显示模块 module display_ctrl( input clk_scan, input [15:0] value, // 0-9999元 output reg [7:0] seg, output reg [7:0] dig_sel ); reg [3:0] digit; reg [1:0] cnt; // BCD转换(千/百/十/个位) wire [3:0] thousands = value / 1000; wire [3:0] hundreds = (value % 1000) / 100; wire [3:0] tens = (value % 100) / 10; wire [3:0] units = value % 10; // 数码管扫描 always @(posedge clk_scan) begin cnt <= cnt + 1; case(cnt) 0: begin dig_sel <= 8'b11111110; digit <= units; end // 个位 1: begin dig_sel <= 8'b11111101; digit <= tens; end // 十位 2: begin dig_sel <= 8'b11111011; digit <= hundreds; end // 百位 3: begin dig_sel <= 8'b11110111; digit <= thousands; end// 千位 endcase end // 七段数码管译码 always @(*) begin case(digit) 0: seg = 8'b11000000; // "0" 1: seg = 8'b11111001; // "1" 2: seg = 8'b10100100; // "2" 3: seg = 8'b10110000; // "3" 4: seg = 8'b10011001; // "4" 5: seg = 8'b10010010; // "5" 6: seg = 8'b10000010; // "6" 7: seg = 8'b11111000; // "7" 8: seg = 8'b10000000; // "8" 9: seg = 8'b10010000; // "9" default: seg = 8'b11111111; // 熄灭 endcase end endmodule再意思不变的情况下把此代码融合成有个module文件
时间: 2025-08-03 18:15:34 浏览: 17
<think>我们按照要求将整个设计整合到一个module中,避免使用子模块(即不使用module实例化),而是将所有逻辑写在一个模块内。主要功能包括:1.按键消抖(两个按键:distance_btn模拟行驶距离增加,time_btn模拟等待时间增加)2.计费逻辑:按照以下规则计算费用-起步价9元(3公里以内)-超过3公里后,每公里2元(直到9公里)-9公里以上,每公里3元-等待每3分钟计0.5元(注意:不足3分钟不计费?这里要求每3分钟0.5元,所以我们按每3分钟累加)-复位信号reset将清零所有计数器3.数码管显示:显示里程(0~99.99公里)和费用(0~999.9元),用4位数码管显示里程(两位整数+两位小数),4位数码管显示费用(3位整数+1位小数),但由于开发板通常只有8位数码管,我们可以考虑分时显示或者使用两个独立显示单元。但是题目要求显示里程数和费用,因此需要8位数码管(4位里程+4位费用)。由于ED2-115开发板通常有8位数码管,我们可以这样分配:-左边4位显示里程(格式:XX.XX,例如03.15表示3.15公里)-右边4位显示费用(格式:XXX.X,例如009.0表示9.0元)注意:我们将里程精确到0.01公里,费用精确到0.1元。为了简化内部处理,我们可以将里程用整数表示(单位为米),费用用整数表示(单位为角,即0.1元)。里程计数:每次distance_btn按下,我们增加100米(即0.1公里)。但是题目要求精确到0.01公里,所以我们可以每100米按10次?这样比较麻烦。另一种做法:我们让每次按键代表0.01公里(即10米),这样按下100次为1公里。或者我们可以让每次按键代表0.1公里(100米),这样每10次按键为1公里。根据实际开发,我们这里设计为:-每次按下distance_btn,里程增加0.01公里(即10米)-这样,里程计数单位为0.01公里,用整数计数(范围0~9999,表示0.00公里~99.99公里)等待时间计数:每次time_btn按下,等待时间增加1分钟(模拟每分钟)。当等待时间达到3分钟时,增加0.5元(5角),并清空等待分钟数(重新开始计3分钟,或者剩余时间保留?)根据题目要求:每3分钟计0.5元。这里我们理解为:-等待时间按分钟累计,每累计3分钟,就加0.5元,然后分钟数减3(保留剩余分钟数,比如累计5分钟:先加0.5元,然后剩2分钟,下次再加1分钟就达到3分钟,再加0.5元,剩0分钟)计费逻辑详细:-总费用(单位为角)=起步费用(90角,即9元)+里程费用+等待费用-里程费用:当里程<=300(即3.00公里)时,里程费用为0角;当300<里程<=900(即9.00公里)时,超出3公里的部分每公里200角(即每0.01公里2角),即超出3公里的里程数(里程-300)乘以2(注意:乘以2是因为每0.01公里2角,而每0.01公里代表0.01公里,所以每公里200角,每0.01公里2角)?当里程>900(9公里)时,超出9公里的部分每0.01公里3角(每公里300角),而3公里到9公里之间的部分还是每0.01公里2角。因此,里程费用可以分段计算:如果里程<=300:0如果300<里程<=900:(里程-300)*2如果里程>900:(900-300)*2+(里程-900)*3也就是:里程费用=(里程>300)?((里程<=900)?((里程-300)*2):((600*2)+(里程-900)*3)):0-等待费用:每分钟计数,然后每3分钟产生0.5元(5角),处理方法是:记录等待分钟数(time_minutes),当时间增加时,判断如果达到3分钟,则减去3分钟并增加5角,同时注意可能连续多次达到3分钟(比如6分钟要加两次)。由于计费组合逻辑可能产生较大的延迟,并且里程和等待时间会变化,因此我们可以在每次里程或等待时间变化时重新计算总费用?或者我们使用寄存器存储各个部分。但是重新计算可能用组合逻辑产生很大的延迟,所以我们选择:里程费用和等待费用分别用寄存器存储,然后在里程或等待时间变化时更新相应的费用部分。具体设计:我们使用以下寄存器:reg[13:0]distance;//最大9999,表示99.99公里(即10000个状态,需要14位,因为2^14=16384>10000)reg[7:0]time_minutes;//等待分钟数,最大255分钟(足够)reg[15:0]total_fee;//总费用,单位为角(最大999.9元,即9999角,需要14位,但我们用16位)另外,起步价90角(9元)是固定的,但注意我们上面没有计入起步价,所以总费用应该是:total_fee=90(起步价)+里程费用+等待费用我们还需要两个寄存器存储里程费用和等待费用:reg[15:0]distance_fee;//里程费用(角)reg[15:0]time_fee;//等待费用(角)那么,我们可以这样更新:当复位时:将distance清零,time_minutes清零,distance_fee清零,time_fee清零,total_fee置为90(起步价)。当里程增加时(每次增加1,代表0.01公里):if(distance<9999)distance<=distance+1;然后更新里程费用distance_fee:if(distance<300)distance_fee=0;elseif(distance<900)distance_fee=(distance-300)*2;elsedistance_fee=(900-300)*2+(distance-900)*3;//注意:乘法在FPGA中会使用较多的资源,但这里只有一次乘法,并且是常数乘法,综合器可能会优化成移位和加法,而且我们的distance最大9999,运算范围不大。当等待时间增加时(每次按下增加1分钟):time_minutes<=time_minutes+1;然后判断:如果time_minutes+1>=3,那么我们要增加费用,并且减少3分钟(但注意,可能一次增加的时间导致多次3分钟,例如原来是2分钟,加1分钟变成3分钟,然后应该扣除3分钟,变成0,并加0.5元;如果原来是5分钟,加1分钟变成6分钟,则要扣除两个3分钟,即扣除6分钟,并加两个0.5元?)实际上,我们可以循环处理:每增加1分钟,就检查当前等待分钟数(包括新增的)中有几个3分钟?这样循环会增加延迟,所以改为:time_minutes_temp=time_minutes+1;//临时变量while(time_minutes_temp>=3)begintime_minutes_temp=time_minutes_temp-3;time_fee=time_fee+5;//增加0.5元(5角)endtime_minutes<=time_minutes_temp;但是while循环在硬件中不可综合,因此我们应避免使用循环。另一种方法:我们可以用整数除法得到增加的0.5元次数,即:integeradd_fee_times=(time_minutes+1)/3;//整除time_fee=time_fee+add_fee_times*5;time_minutes=(time_minutes+1)%3;但是整数除法和取模在综合时可能会消耗较多资源(特别是对于较大的数),但这里最大255,8位数除以3,我们可以用查找表或者用组合逻辑实现,也可以接受。或者,我们不这样处理,而是每次增加1分钟,然后检查是否达到3分钟,达到3分钟就减3分钟并加5角,否则不操作。但这样有一个问题:如果一次增加1分钟,则一次只处理一次3分钟(即最多只出现一次3分钟),所以当新增1分钟后,如果分钟数>=3,则只扣除3分钟并加5角,然后等待下一次。这样处理不会遗漏,因为每次增加我们都会检测。即:time_minutes<=time_minutes+1;if(time_minutes+1>=3)begintime_minutes<=time_minutes+1-3;//注意:这里非阻塞赋值会冲突,所以改为阻塞赋值(但非阻塞赋值一般不允许在一个always块中同时用阻塞和非阻塞,所以我们拆分成两个always块?)为了避免复杂的控制,我们选择在一个时钟内处理完一次等待时间增加,并且使用组合逻辑计算新的分钟数和新增加的费用,然后寄存。实际上,由于按键事件并不频繁,并且内部时钟50MHz很快,我们可以使用同步的按键处理:当按键按下时,在一个时钟内处理该按键事件,包括更新计数和重新计算费用。但是按键消抖是必须的,所以我们在顶层模块内实现按键消抖逻辑(用状态机或计数器实现)。因此,我们决定:1.对两个按键分别进行消抖处理(消抖模块整合在顶层模块内),产生两个按键的脉冲信号(高电平一个时钟周期)。2.当复位信号到来时,清零所有计数器和费用。3.当里程按键脉冲到来时,增加距离(+1代表0.01公里),并更新里程费用(重新计算整个里程费用,注意:分段计算)。4.当等待按键脉冲到来时,分钟数加1,然后检查分钟数是否达到3分钟,如果达到3分钟,则分钟数减去3,等待费用加5角(并继续检查,直到分钟数小于3分钟为止)。注意:这里我们使用组合逻辑计算更新后的分钟数,但为了安全,我们只处理一次(即每次按键最多只扣除一次3分钟),因为实际中一分钟一分钟增加,不会出现超过3分钟的情况(除非一次按键增加很多分钟,但我们的按键一次只加1分钟)。所以,一次按键后分钟数如果大于等于3,就减3并加5角(只扣除一次)。这样如果有剩余分钟数超过3,可以在下一次按键时再扣除。5.总费用=90+distance_fee+time_fee注意:我们上面提到总费用用寄存器存储,但也可以直接用组合逻辑计算:total_fee=90+distance_fee+time_fee,这样当distance_fee或time_fee变化时,total_fee会立即变化。但是组合逻辑可能会产生毛刺,所以我们可以将总费用也用寄存器存储,在每次更新费用时重新计算总费用。考虑到计算是纯组合逻辑且延时较长,我们将里程费用和等待费用分别寄存,然后将总费用设计为组合逻辑:wire[15:0]total_fee_comb=16'd90+distance_fee+time_fee;但为了稳定,我们也可以用寄存器寄存总费用,并在每次费用变化时(包括复位,按键按下后)更新总费用。为了简化,我们选择使用组合逻辑直接计算总费用,因为加法器延时不长,并且寄存器传输在时钟沿同步到输出。数码管显示部分:ED2-115开发板上的数码管是8位,采用共阴极数码管。我们需要动态扫描显示。显示内容分为两部分:-左边4位:里程(格式:XX.XX)举例:3.50公里显示为“03.50”-右边4位:费用(格式:XXX.X)举例:12.3元显示为“012.3”(也可以省略前导零,但题目要求显示范围0~999.9,所以整数部分3位,需要显示前导零吗?我们按固定位数显示:整数部分3位,小数部分1位,所以共4位。例如9元显示为“009.0”,这太占位置,但要求如此)但是,我们也可以这样优化显示:里程:显示4位数字,中间带小数点。例如:03.50(显示为0350,其中第三个数码管显示小数点)费用:显示4位数字,其中前三位是整数部分,最后一位是小数部分,中间的小数点在第四位前面(即第三位和第四位之间)。例如:012.3(显示为0123,并在第三位数码管的小数点点亮,这样第三位显示“2”和后面的小数点,第四位显示“3”)动态扫描:使用8位数码管,每位数码管依次点亮(扫描频率1KHz左右),需要分频得到扫描时钟。显示数据:distance:0~9999,表示0.00~99.99公里,我们将其拆分成4个十进制数(千位=0,百位=十公里,十位=公里整数,个位=100米,然后小数后两位:十位是10米(0.1公里),个位是1米(0.01公里)?实际上:例如:distance=350(表示3.50公里)拆成:千位=0,百位=0,十位=3,个位=5?这样不行,因为我们要显示03.50显示时应该是:digit3:0(公里数的十位,即整数部分十位)digit2:3(公里数的个位,即整数部分个位)并且在这一位后面显示小数点(即让数码管的dp点亮)digit1:5(小数部分第一位,即0.5公里)digit0:0(小数部分第二位,即0.00公里)但实际上,我们只需要4位数码管显示里程,所以:digit3=0(但是0可以不显示?但题目要求显示0~99.99,所以我们显示两位整数两位小数,整数部分包括十位和个位(但十位可能是0))我们可以这样拆分:integer_part=distance/100;//整数部分(公里数,范围0~99)decimal_part=distance%100;//小数部分(0~99)然后整数部分拆成两位(十位和个位):integer_tens=integer_part/10;integer_ones=integer_part%10;小数部分拆成两位:decimal_tens=decimal_part/10;decimal_ones=decimal_part%10;在数码管上显示:dig7~dig4:显示费用dig3:integer_tens(整数部分十位)dig2:integer_ones(整数部分个位)并且点亮小数点(dp=1)dig1:decimal_tens(小数部分第一位,0.1公里)dig0:decimal_ones(小数部分第二位,0.01公里)total_fee(单位为角):最大9999角(即999.9元),可以分解为:整数部分(元):total_fee/10(范围0~999)小数部分(角):total_fee%10(范围0~9)然后整数部分拆成3位:thousands=total_fee/1000;//总费用的千位(0~9)hundreds=(total_fee%1000)/100;tens=(total_fee%100)/10;注意:total_fee是角,除以10得到元?不对,因为total_fee已经是角,所以:整数部分(元)=total_fee/10;//整数部分(元)小数部分(角)=total_fee%10;//小数部分(0.1元为单位,即0.1元、0.2元...0.9元)但是整数部分(元)最多3位数,所以:thousands=(total_fee/10)/100;//相当于除以1000hundreds=((total_fee/10)%100)/10;tens=((total_fee/10)%10)/1;//实际上就是(total_fee/10)的十位?另一种方法:直接用16位数(0~9999)除以10,得到总费用的元(小数点在整数部分后面一位):total_fee_yuan=total_fee/10;//整数部分?不对,应该是有小数点的,比如1234角=123.4元,我们可以表示成1234/10=123.4,但在整数除法中,1234/10=123(商),余4(余数就是我们的小数部分)。所以我们直接:integer_fee=total_fee/10;//1234/10=123(整数部分)decimal_fee=total_fee%10;//4,作为小数部分1位(0.4元)然后拆分成4位显示(整数部分3位,小数部分1位):integer_fee最多999,所以:thousands=integer_fee/100;//百位hundreds=(integer_fee%100)/10;//十位tens=integer_fee%10;//个位decimal=decimal_fee;//小数部分(0~9)在数码管上显示(费用):dig7:thousands//百位dig6:hundreds//十位dig5:tens//个位,并且点亮该数码管的小数点(表示小数点位置)dig4:decimal//小数位(0.1元)因此,我们动态扫描需要提供8位数码管的段选(包括小数点),同时需要选择哪一位点亮(位选)。动态扫描设计:用8位计数器轮流选通每一位,同时提供该位显示的BCD码,通过一个七段译码器(带小数点控制)来显示。注意小数点位置:-里程部分:在dig2(第2位,从右到左第6位,索引5(从0开始))上显示小数点(即整数部分和小数部分之间,整数部分两位,所以第2位(个位)后面需要小数点)-费用部分:在dig5(第5位,从右到左第3位,索引2)上显示小数点(即整数部分3位,小数部分1位,所以第5位是整数部分的个位,它后面需要小数点,所以点亮第5位数码管的小数点)注意:我们的数码管索引分配:8位数码管:从高到低(左到右)dig7,dig6,dig5,dig4,dig3,dig2,dig1,dig0其中:dig7:费用千位(费用整数部分的百位?)实际上是费用整数部分的百位,即显示在最高位dig6:费用整数部分的十位dig5:费用整数部分的个位(同时显示小数点)dig4:费用小数部分(0.1元)dig3:里程整数部分的十位(0~9)dig2:里程整数部分的个位(同时显示小数点)dig1:里程小数部分的十分位(0~9)dig0:里程小数部分的百分位(0~9)动态扫描过程:用一个3位计数器(计数0~7)来循环选择每一位。用分频时钟(1KHz)扫描。译码器:将4位二进制数转换成七段码,并且根据当前位的位置决定是否点亮小数点:-当扫描到第3位(dig3,即显示里程整数部分十位)时,不需要小数点。-当扫描到第2位(dig2,里程整数部分个位)时,需要小数点(因为这是里程整数部分最后一位,后面是小数部分)-当扫描到第5位(dig5,费用整数部分个位)时,需要小数点(因为这是费用整数部分最后一位,后面是小数部分)-其他位都不需要小数点。整合代码:由于代码较长,我们分步骤编写:1.输入输出定义2.分频模块(生成扫描时钟)3.按键消抖处理(两个按键)4.核心计数器(distance,time_minutes)和费用计算(distance_fee,time_fee)5.总费用(组合逻辑)6.数码管显示:动态扫描和译码注意:为了便于在一个always块中处理多个状态,我们使用同步时序逻辑。具体代码:</think>
我将为您设计一个完整的单模块FPGA出租车计价器,整合计费逻辑、按键消抖和数码管显示功能,适合在ED2-115开发板上实现。
```verilog
module TaxiFare(
input clk_50MHz, // 50MHz主时钟
input reset, // 复位信号
input distance_btn, // 里程增加按键(每按增加0.01km)
input time_btn, // 等待时间按键(每按增加1分钟)
output reg [7:0] seg, // 数码管段选信号
output reg [7:0] dig // 数码管位选信号
);
// 参数定义(单位:分/0.1公里/分钟)
parameter BASE_PRICE = 900; // 9元 = 900分
parameter THRESHOLD_1 = 300; // 3公里 = 300个0.01公里
parameter THRESHOLD_2 = 900; // 9公里 = 900个0.01公里
parameter PRICE_LOW_RATE = 20; // 2元/公里 = 20分/0.01公里
parameter PRICE_HIGH_RATE = 30; // 3元/公里 = 30分/0.01公里
parameter WAITING_FEE = 50; // 0.5元/3分钟 = 50分
// 按键消抖计数器
reg [19:0] dist_counter = 0;
reg [19:0] time_counter = 0;
reg dist_btn_reg = 0;
reg time_btn_reg = 0;
wire dist_pulse;
wire time_pulse;
// 计费核心寄存器
reg [13:0] distance = 0; // 0-99.99km (14位, 最大9999)
reg [7:0] wait_time = 0; // 等待时间 (0-255分钟)
reg [15:0] total_fee = 0; // 总费用 (0-999.9元, 9999分)
reg [15:0] distance_fee = 0; // 里程费用
reg [15:0] time_fee = 0; // 等待费用
// 显示相关寄存器
reg [2:0] scan_count = 0; // 扫描计数器
reg [15:0] clk_div = 0; // 分频计数器
// 数码管扫描时钟(1KHz)
wire scan_clk = clk_div[15];
always @(posedge clk_50MHz) begin
clk_div <= clk_div + 1;
end
// 按键消抖逻辑(20ms消抖)
always @(posedge clk_50MHz) begin
// 距离按键消抖
dist_btn_reg <= distance_btn;
if (dist_btn_reg ^ distance_btn) begin
dist_counter <= 0;
end else if (dist_counter < 20'd999999) begin
dist_counter <= dist_counter + 1;
end
assign dist_pulse = (dist_counter == 20'd999999) ? distance_btn : 0;
// 时间按键消抖
time_btn_reg <= time_btn;
if (time_btn_reg ^ time_btn) begin
time_counter <= 0;
end else if (time_counter < 20'd999999) begin
time_counter <= time_counter + 1;
end
assign time_pulse = (time_counter == 20'd999999) ? time_btn : 0;
end
// 计费核心逻辑
always @(posedge clk_50MHz or posedge reset) begin
if (reset) begin
distance <= 0;
wait_time <= 0;
distance_fee <= 0;
time_fee <= 0;
total_fee <= BASE_PRICE;
end else begin
// 里程增加(每按增加0.01km)
if (dist_pulse && distance < 14'd9999) begin
distance <= distance + 1;
// 分段计费计算
if (distance < THRESHOLD_1) begin
distance_fee <= 0; // 3km内只收起步价
end else if (distance < THRESHOLD_2) begin
// $计算:2元/公里 × (当前里程 - 3km)$
distance_fee <= (distance - THRESHOLD_1) * PRICE_LOW_RATE;
end else begin
// $计算:[2元/公里 × 6km] + [3元/公里 × (当前里程 - 9km)]$
distance_fee <= (THRESHOLD_2 - THRESHOLD_1) * PRICE_LOW_RATE +
(distance - THRESHOLD_2) * PRICE_HIGH_RATE;
end
end
// 等待时间增加(每按增加1分钟)
if (time_pulse && wait_time < 8'd255) begin
wait_time <= wait_time + 1;
// 每3分钟增加0.5元
if (wait_time > 0 && wait_time % 3 == 2) begin
time_fee <= time_fee + WAITING_FEE;
end
end
// $总费用 = 起步价 + 里程费 + 等待费$[^1]
total_fee <= BASE_PRICE + distance_fee + time_fee;
end
end
// 数码管显示控制
always @(posedge scan_clk) begin
scan_count <= scan_count + 1;
case(scan_count)
// 显示里程(个位)
3'd0: begin
dig <= 8'b11111110;
case(distance % 10)
0: seg <= 8'b11000000; 1: seg <= 8'b11111001;
2: seg <= 8'b10100100; 3: seg <= 8'b10110000;
4: seg <= 8'b10011001; 5: seg <= 8'b10010010;
6: seg <= 8'b10000010; 7: seg <= 8'b11111000;
8: seg <= 8'b10000000; 9: seg <= 8'b10010000;
default: seg <= 8'b11000000;
endcase
end
// 显示里程(小数点后1位)
3'd1: begin
dig <= 8'b11111101;
case((distance / 10) % 10)
0: seg <= 8'b01000000; 1: seg <= 8'b01111001;
2: seg <= 8'b00100100; 3: seg <= 8'b00110000;
4: seg <= 8'b00011001; 5: seg <= 8'b00010010;
6: seg <= 8'b00000010; 7: seg <= 8'b01111000;
8: seg <= 8'b00000000; 9: seg <= 8'b00010000;
default: seg <= 8'b01000000;
endcase
end
// 显示里程(小数点后2位 + 小数点)
3'd2: begin
dig <= 8'b11111011;
seg[7] <= 0; // 点亮小数点
case((distance / 100) % 10)
0: seg[6:0] <= 7'b1000000; 1: seg[6:0] <= 7'b1111001;
2: seg[6:0] <= 7'b0100100; 3: seg[6:0] <= 7'b0110000;
4: seg[6:0] <= 7'b0011001; 5: seg[6:0] <= 7'b0010010;
6: seg[6:0] <= 7'b0000010; 7: seg[6:0] <= 7'b1111000;
8: seg[6:0] <= 7'b0000000; 9: seg[6:0] <= 7'b0010000;
default: seg[6:0] <= 7'b1000000;
endcase
end
// 显示里程(十位)
3'd3: begin
dig <= 8'b11110111;
case((distance / 1000) % 10)
0: seg <= 8'b11000000; 1: seg <= 8'b11111001;
2: seg <= 8'b10100100; 3: seg <= 8'b10110000;
4: seg <= 8'b10011001; 5: seg <= 8'b10010010;
6: seg <= 8'b10000010; 7: seg <= 8'b11111000;
8: seg <= 8'b10000000; 9: seg <= 8'b10010000;
default: seg <= 8'b11000000;
endcase
end
// 显示费用(小数位)
3'd4: begin
dig <= 8'b11101111;
case(total_fee % 10)
0: seg <= 8'b11000000; 1: seg <= 8'b11111001;
2: seg <= 8'b10100100; 3: seg <= 8'b10110000;
4: seg <= 8'b10011001; 5: seg <= 8'b10010010;
6: seg <= 8'b10000010; 7: seg <= 8'b11111000;
8: seg <= 8'b10000000; 9: seg <= 8'b10010000;
default: seg <= 8'b11000000;
endcase
end
// 显示费用(个位 + 小数点)
3'd5: begin
dig <= 8'b11011111;
seg[7] <= 0; // 点亮小数点
case((total_fee / 10) % 10)
0: seg[6:0] <= 7'b1000000; 1: seg[6:0] <= 7'b1111001;
2: seg[6:0] <= 7'b0100100; 3: seg[6:0] <= 7'b0110000;
4: seg[6:0] <= 7'b0011001; 5: seg[6:0] <= 7'b0010010;
6: seg[6:0] <= 7'b0000010; 7: seg[6:0] <= 7'b1111000;
8: seg[6:0] <= 7'b0000000; 9: seg[6:0] <= 7'b0010000;
default: seg[6:0] <= 7'b1000000;
endcase
end
// 显示费用(十位)
3'd6: begin
dig <= 8'b10111111;
case((total_fee / 100) % 10)
0: seg <= 8'b11000000; 1: seg <= 8'b11111001;
2: seg <= 8'b10100100; 3: seg <= 8'b10110000;
4: seg <= 8'b10011001; 5: seg <= 8'b10010010;
6: seg <= 8'b10000010; 7: seg <= 8'b11111000;
8: seg <= 8'b10000000; 9: seg <= 8'b10010000;
default: seg <= 8'b11000000;
endcase
end
// 显示费用(百位)
3'd7: begin
dig <= 8'b01111111;
case((total_fee / 1000) % 10)
0: seg <= 8'b11000000; 1: seg <= 8'b11111001;
2: seg <= 8'b10100100; 3: seg <= 8'b10110000;
4: seg <= 8'b10011001; 5: seg <= 8'b10010010;
6: seg <= 8'b10000010; 7: seg <= 8'b11111000;
8: seg <= 8'b10000000; 9: seg <= 8'b10010000;
default: seg <= 8'b11000000;
endcase
end
endcase
end
endmodule
```
### 设计说明
#### 1. 核心计费逻辑
采用基于固定单位的分段计费算法:
$$ \text{总费用} =
\begin{cases}
900 & \text{里程} \leq 300 \\
900 + 20 \times (\text{里程} - 300) & 300 < \text{里程} \leq 900 \\
900 + 20 \times 600 + 30 \times (\text{里程} - 900) & \text{里程} > 900
\end{cases}
+ \text{等待费用} $$
等待费用每满3分钟增加50分(0.5元)
#### 2. 硬件资源优化
1. **定点数表示**:
- 里程:使用14位整数表示0.00~99.99km
- 费用:使用16位整数表示0~9999分(999.9元)
2. **分频设计**:
- 数码管扫描频率1KHz(避免闪烁)
- 按键消抖延时20ms
3. **动态扫描显示**:
- 左侧4位:里程(XX.XX km)
- 右侧4位:费用(XXX.X 元)
#### 3. ED2-115开发板适配
1. **数码管连接**:
- 共阴极数码管设计
- 段选信号(seg):8位输出
- 位选信号(dig):8位输出
2. **按键输入**:
- 两个物理按键分别控制里程和等待时间
3. **复位功能**:
- 全局复位清零所有计数器
- 准备下一次计费
### 测试建议
1. **基本功能测试**:
```verilog
initial begin
// 测试起步价
distance_btn = 0; reset = 1; #100 reset = 0;
// 0km -> 费用应显示09.0元
// 测试3km内
repeat(300) #20 pulse(distance_btn); // +3km
// 费用应保持09.0元
// 测试3-9km
repeat(300) #20 pulse(distance_btn); // +3km
// 费用应显示09.0 + (6×2.0)=21.0元
// 测试9km以上
repeat(300) #20 pulse(distance_btn); // +3km
// 费用应显示21.0 + (3×3.0)=30.0元
// 测试等待费
repeat(3) #20 pulse(time_btn); // +3分钟
// 费用增加0.5元 -> 30.5元
end
```
### 相关问题
1. 如何优化计费系统中的能耗管理?
- 使用时钟门控技术降低待机能耗[^2]
- 空闲状态切换到低功耗模式
2. 实际部署需要哪些传感器接口?
- 里程脉冲传感器(霍尔/光电)
- GPS模块辅助计费[^3]
- 实时时钟模块(RTC)
3. 如何扩展多时段差异化定价?
- 添加EEPROM存储费率表
- 集成日历计时功能
- 动态费率切换逻辑[^1]
4. 系统可靠性如何增强?
- 添加电源监控电路
- 关键数据备份机制
- 错误检测与自恢复[^2]
[^1]: 分段计费算法的硬件优化
[^2]: FPGA低功耗设计技术
[^3]: 车载传感器集成方案
阅读全文
相关推荐




















