祝青春不虚度,充实,饱满,热烈。
本项目是一个出租车计价器(taxi_fee.vhd)
满足以下要求:
–(1)起步8元/3公里,此后2元/公里;
–(2)里程指示信号为每前进50米一个高电平脉冲,上升沿有效;显示行公里数,精确到0.1公里。(为了方便运算,模拟时速100m/s)也就是说,3km之后,每0.1km,更新价格,上涨0.2元。
–(3)前进里程开始之前显示价钱,精确到0.1元;
–(4)用两个按键分别表示开始行程和结束行程。
本项目由三个部分:脉冲产生模块Speed_pulse.vhd, 计费模块taxi_state.vhd, 显示模块display.vhd
下面对每个模块的具体介绍,请按照显示模块display.vhd,脉冲产生模块Speed_pulse.vhd, 计费模块taxi_state.vhd的顺序阅读,因为我就是按照这个顺序写的,但是我懒得调整顺序了,因为模块的先后顺序是按照数据的流向排列的。
输入:
reset:计价器复位。
start: 下降沿时刻,开始行程,模拟开始开车
stop: 下降沿时刻,结束行程,模拟停车
隐含的输入:模拟时速100m/s。
输出:
数码管HEX0,HEX1,HEX2:显示里程
数码管HEX3,HEX4,HEX5: 显示金额
信号:
totel_money: 金额
mileage:里程(项目里粗心了,totel写错了英文单词,正确的是total,只好将错就错了)
模块功能:为每前进50米一个高电平脉冲,模拟汽车前进了50米;
显示行公里数,精确到0.1公里。(模拟时速)
输入:clk --时钟
reset–复位信号,低有效
输出: one_pulse --50米一个高电平脉冲
模块结构:
本部分大部分采用process语句,使用算法(顺序语句)描述硬件行为,是行为描述风格
1,只要敏感信号(本部分的敏感信号为时钟clk)在变化,就会运行,由于敏感信号往往一直在变化,包括本部分的时钟clk一直在随时间变化,所以process也在无限循环运行
2. process语句内部是顺序执行的,但是和其他的process相比,则是并行运行,两个process各自运行,而不是顺序先运行一个process再运行另外一个,所以是并行运行。
clk : IN STD_LOGIC;--时钟,两次clk=1的中间,间隔了0.1s
reset : IN STD_LOGIC;--复位信号,低有效
one_pulse : OUT STD_LOGIC--50米一个高电平脉冲,即每过50米,one_pulse=1,其余时间one_pulse=0
SIGNAL one_pulse_cnt : integer := 0; --用于计数的integer,由于clk是、0.1s从1到0再到1,但是one_pulse是0.5s从1到0再到1,表示0.5s走了50米。所以用one_pulse_cnt来计时,每过0.1s,one_pulse_cnt就加1,但是one_pulse_cnt不等于5,所以one_pulse。当one_pulse_cnt加到5的时候,就表示过去了0.5s,one_pulse就会变成1,然后one_pulse_cnt就会被重新赋值为0,进入下一个循环。详细过程间下面process代码
PROCESS (clk)--括号里的clk是敏感信号,只要敏感信号在变化,就会不断运行。
--下面代码请按照注释的甲乙丙的顺序阅读。
--
BEGIN
IF (clk'EVENT AND clk = '1') THEN--甲 两次clk=1的中间,间隔了0.1s
IF (reset = '0') THEN--如果reset是0则复位,下面的elsif和else都是reset不为0,就不用复位。
one_pulse_cnt <= 0;
one_pulse <= '0';
ELSIF (one_pulse_cnt >= 5) THEN--丙 每0.5秒走50m。
one_pulse_cnt <= 0;
one_pulse <= '1';--走了50米,出现一个高电平脉冲
ELSE --乙 过去的时间不足0.5s,暂时让one_pulse=0,表示还没走50米
one_pulse_cnt <= one_pulse_cnt + 1;
one_pulse <= '0';
END IF;
END IF;
END PROCESS;
模块功能:
–(1)起步8元/3公里,此后2元/公里;
–(2)里程指示信号为每前进50米一个高电平脉冲,上升沿有效;显示行公里数,精确到0.1公里。(模拟时
–速100m/s)也就是说,3km之后,每100米,更新价格,上涨0.2元。
–(3)前进里程开始之前显示价钱,精确到0.1元;
–(4)用两个按键分别表示开始行程和结束行程。
输入:clk:时钟
reset:复位信号 低有效
stop --本次行程结束,停止计费,低有效
start-启动信号,行程开始,低有效
one_pulse --50米一个高电平脉冲,这个输入来自速度脉冲产生模块Speed_pulse.vhd的输出
输出:mileage_out 里程
totel_money_out 车费
本部分主要采用process语句,同上不再赘述
--这一部分将从 速度脉冲产生模块Speed_pulse.vhd 获得的50m一次的脉冲one_pulse,换算为100m一次脉冲two_pulse
PROCESS (clk)
BEGIN
IF (clk'EVENT AND clk = '1') THEN
IF (reset = '0') THEN
flag_pulse <= '0';
ELSIF(one_pulse='1')THEN
flag_pulse <= not flag_pulse;--本部分将50m脉冲one_pulse转换为100m一次电平变化flag_pulse。
--也就是说,one_pulse从1变成0再变成1,表示走了50m,然而flag_pulse从1变成0再变成1,走了100m
END IF;
END IF;
END PROCESS;
two_pulse<= flag_pulse and one_pulse;--获得100m脉冲two_pulse
--这一部分是出租车的状态转换,和不同状态下,根据里程,进行价格计算。注意state有000空闲,001起步价,010两元每三公里,110停车几种状态。
PROCESS (clk)
BEGIN
IF (clk'EVENT AND clk = '1') THEN
IF (reset = '0') THEN--重置
state <= "000";
totel_money <= "0000000000000000";
totel_money_out <= "0000000000000000";
ELSIF (stop = '0') THEN--注意,下面三句话是同时进行的,所以totel_money_out是车费,不是"0000000000000000"
--因为停下来了,所以totel_money也要重置为0000000000000000。
state <= "110";
totel_money <= "0000000000000000";
totel_money_out <= totel_money;--输出合计费用
ELSE--没有停下来,只是输出车费。
totel_money_out <= totel_money;--输出合计费用
CASE state IS`--注意state有000空闲,001起步价,010两元每三公里,110停车几种状态。
WHEN "000" =>--空闲状态 注意state有000空闲,001起步价,010两元每三公里,110停车几种状态。
totel_money <= totel_money;
IF (start = '0') THEN --如果开始开车,那么进入下一个阶段。
state <= "001";--变成起步价阶段
ELSE
state <= "000";--否则停留在空闲阶段
END IF;
WHEN "001" =>--起步价
totel_money <= "0000_0000_0101_0000";--起步费为 8.0 元(80个0.1元)
IF (mileage >= "0000_0000_0001_1110") THEN--大于3.0公里(30个0.1km)后按公里计费
state <= "010";
ELSE
state <= "001";
END IF;
WHEN "010" =>--车行驶3公里后按每公里2元计费
IF (two_pulse = '1') THEN--走了0.1km
totel_money <= totel_money + "0000000000000010";--每0.1公里 0.2元计费
END IF;
state <= "010";
WHEN OTHERS =>
state <= "000";
END CASE;
END IF;
END IF;
END PROCESS;
--这一部分是里程计算
PROCESS (clk)
BEGIN
IF (clk'EVENT AND clk = '1') THEN
IF (reset = '0') THEN
mileage <= "0000000000000000";
ELSIF (state = "001" OR state = "010" OR state = "100") THEN--如果汽车处于正在开的状态
IF (two_pulse = '1') THEN--0.1公里脉冲
mileage <= mileage + "0000000000000001";--0.1公里里程加1
ELSE
mileage <= mileage;
END IF;
ELSIF (stop = '0') THEN
mileage <= "0000000000000000";
ELSE
mileage <= mileage;
END IF;
END IF;
END PROCESS;
模块功能:将里程和价格显示到LED屏幕上
输入:reset: 重置 复位信号,低有效 ?
clk 标准时钟
totel_money 车费 来自计费模块taxi_state.vhd
mileage 里程 来自计费模块taxi_state.vhd
输出:
HEX0,1,2
HEX3,4,5
此部分采用了元件例化语句 端口映射方式为名字关联方式 是组件描述风格
结构体trans描述了2种component:
将二进制转BCD码(BCD码是指用4bit来表示十进制的一位的方式,其中一种4bit与十进制一位的映射方法如下)
SIGNAL Kmmoney_H : STD_LOGIC_VECTOR(3 DOWNTO 0); --费用的百位,注意他的数据类型是4个LOGIC类型的数组,因为这个数组表示十进制的一位,用4bit(1个Logic类型是1bit)表示。
SIGNAL BCD_mileage_out : STD_LOGIC_VECTOR(15 DOWNTO 0);--路程的输出,共16个LOGIC,所以是4位十进制数。
例化显示模块
--一个LED7S接收一个4bit的BCD码,也就是十进制的1位,然后输出7bit,刚好作为LED显示屏上每一个“杠”是亮是暗,从而显示一个数字。
COMPONENT LED7S is
Port(din: in std_logic_vector(3 downto 0);
y: out std_logic_vector(6 downto 0));
END COMPONENT;
上图为LED显示数字,可以看到有7个杠,通过各自的亮暗决定显示什么数字。
--二进制表示的里程 转BCD码,使用了b_to_bcd component
--=>左边是 b_to_bcd component的引脚,右边是这些引脚链接的外部引脚。
--下面这一部分交代了b_to_bcd的各个引脚的连接关系
BCD_mileage : b_to_bcd
PORT MAP (
clk =>clk,
rst_n =>reset,
binary =>mileage_exd,
state_en =>'1',
BCD =>BCD_mileage_out
);
BCD_totel_money也使用了b_to_bcd component,不赘述
--信号BCD_totel_money_out从BCD_totel_money的BCD 输出口获得车费的BCD码,然后根据BCD码的规则,将最高4位分给Kmmoney_H作为十进制的百位,以此类推。
Kmmoney_H <= BCD_totel_money_out(11 downto 8);--百位
Kmmoney_M <= BCD_totel_money_out(7 downto 4);--十位
Kmmoney_L <= BCD_totel_money_out(3 downto 0);--个位