数字系统设计的大作业来了,用VHDL语言在实验板上实现一个出租车计价设计,有一些难度,大概花了几天时间,不过好在最后搞出来了,同时总结一下遇到的问题,为了排版整洁,源代码就放在最后放出了。
温馨提示一下,一定要看老师发的关于实验板的说明文档和教材,很多东西说明文档里都有的!
本博客原创,转载请注明!!!
本源代码原创,转载和使用请注明!!!
白嫖容易,希望同学能独立完成!!!
如果有一些童鞋毫无思路可以参考一下我的实现思路,但是最后一定要自己去实践、去完成本次实验设计!!!
可能看懂代码花的时间比自己敲用的时间还长
实验板:FLEX10k系列的EPF10K20TC144-4
软件:QuartusII 9
如果对QuartusII 9下载程序过程不熟悉的童鞋,可以看一下我之前的博客:数字系统设计学习之QuartusII9下载程序
废话不多说,开始吧!
本文原创,创作不易,转载请注明!!!
本文链接
个人博客:https://ronglin.fun/?p=262
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/116535972
出租车计价器设计(平台实现)★★
完成简易出租车计价器设计,选做停车等待计价功能。
基本功能:
1)起步8元/3公里,此后2元/公里;
2)里程指示信号为每前进50米一个高电平脉冲,上升沿有效;显示行驶公里数,精确到0.1公里。(模拟时速40KM/h)
4)前进里程开始之前显示价钱,精确到0.1元;
5)用两个按键分别表示开始行程和结束行程。
选做功能:
1) 增加一个停车等待/恢复行程按钮,用2个数码管显示等待时间,精确到0.1分钟。
2) 等候费1元/分钟,计价精度为0.1元。
根据实验需求,整个程序的运行过程可以分为三个状态,分别是停止状态、运行状态和等待状态,停止状态用来显示本次行驶的相关数据;运行状态,模拟车辆运行,里程指示以时钟脉冲为标志,即来一次时钟上升沿,行驶里程数增加一次。同时根据计费准则计费,计费规则为,前3公里8元钱,此后2元/公里;等待状态下,以时钟信号为标志增加等待时间,显示等待时间,同时根据等待时间计费,计费规则为1分钟1元钱,即6秒1毛钱。
分析完题目就可以开始画思维图了:
思路设计如上图,一共设计了三个状态分别是停止状态、运行状态和等待状态,停止状态不做任何计算,只是显示本次行驶的“等待时间”、“行驶公里数”和“费用”,当用户按下开始按钮之后,程序切换到运行状态,当cp到来时增加公里数,同时费用增加,当用户按下等待按钮时,程序切换到等待状态,当cp到来时增加等待时间,计费累计。一目了然。
之后开始头秃敲代码,为了排版整洁,源代码放在博客最后。
因为用到了数码管,输出的最后结果是数码管的译码值,不直观,所以仿真图就不放出来了,直接看板载结果。
计费规则:
等待时间换算,1元/分钟(每6秒1毛钱)
前3公里8元钱,
此后2元/公里;
计算过程:
等待时间 花费:42秒/6 = 0.7元
前3公里 花费:8元
后2.8公里 花费: 2.8*2=5.6元
一共花费 0.7 + 8 + 5.6 = 14.3元
计算过程:
等待时间 花费:1分12秒 = 72秒,72秒/6 = 1.2元
前3公里 花费:8元
后0.5公里 花费: 0.5*2=1元
一共花费1.2 + 8 + 1 = 10.2元
本次设计采取的主要是有限状态机的思想,虽说是状态机,但和书上的比却不是正宗的VHDL模式的状态机,而是类比了嵌入式STM32类单片机的状态机设计思想,非要说的话,算是Moore类状态机,用一个process控制状态切换,这个进程主要监听两个按钮的变化,根据用户的输入改变状态;第二个process就是每一个状态下,当时钟信号到来时应该执行什么操作;还有一(两)个process是用来控制数码管,让其显示自己需要的内容。最开始设计数码管的显示,也是根据当前的状态输出值,但最后经过比较 不同状态需要输出的内容时,由于数码管数量有限,不同状态下能输出的值的数量有限,最后统一,不论哪种状态,都只显示“等待时间”、“行驶公里数”、“价钱”。其中还有一个小瑕疵就是,本来想用1Hz作为标准时钟,但是1Hz的频率太慢,不利于调试,最后还是调高了频率。
我的测试重点主要在于用户的错误操作,对于基础功能,经过多次计算和测试是没有问题的,所以重点放在了错误操作上:
1.在停止状态,点击等待状态的按键。
在停止状态,如果启动等待状态,是没有任何反应的,数码管仍然显示“等待时间”、“行驶公里数”、“价钱”且数值不变。符合日常生活。
2.在等待状态,点击停止状态的按键。
点击之后,等待时间不再读秒,立刻停止,此时再次拨动等待状态的按键,数码管没有任何反应。符合日常逻辑。
3.在等待状态,点击开始状态的按键。
点击开始状态的按键,没有任何反应。
4. 在等待状态的按键未关闭的状态下,直接点击开始
会直接进入等待状态,“等待时间”读秒,同时计费,不论是否超过了3公里。符合设计逻辑。
由于数码管的限制,行驶公里数只能显示两位,所以最高只能显示9.9公里,但是这只是显示问题,公里数只会显示个位和小数位,费用不会受到影响,等待时间同理,等待时间只显示分钟和秒数,最高只能显示到9分59秒,如果超过了10分钟,只会从0开始读秒,但是费用不会受到影响。不过因为数码管数量限制,只能最高显示99.9元,如果超过99.9元,费用就会清空。
最大的问题还是在于频率的问题,因为1Hz太慢,不利于调试查看最后结果,就稍微加快了一下频率,用的100Hz时钟,然后开关是110,最后的时钟频率就是100/(2^6) = 1.5625Hz,如果非要改进的话,需要再定义一个分频器。按照目前的时钟频率,模拟的出租车时速为562.5km/h。起飞 尽管如此,需要看到3公里之后的价格情况仍需等待一段时间,故我特意加快了时钟频率(车速)来更方便的调试。
我的设计思路基于有限状态机,最初定义了4个状态,停止,行驶3公里内,行驶超3公里,等待。但经过分析,最后确定的状态只有,停止和行驶两种状态,为了使代码更整洁,等待状态融进了行驶状态中,但是基本的状态框架已经设计好,可以在其上增加其他状态,或者将我调快的时钟频率调回成符合日常的车速,这些改动不需要对代码代码有很大改动,这是状态机设计思路的好处,同时代码的可读性也更强。
重点来了,主要是遇到的问题的一些解决方法,非要说的话,也不算是解决方案,而且避开这些问题,因为理论部分实在是不扎实,很多东西并不清楚是什么原因。
遇到的第一个问题就是,逻辑单元不够用。这个错误主要是因为代码太复杂,综合的时候需要大量的逻辑单元,而手上的芯片只有1152个逻辑单元,所以编译的时候都通不过。落实到源代码中就是定义了一个整数范围为0-99999的信号量,当这个信号量参与运算尤其是乘除模的时候,需要耗费大量的逻辑单元,最后导致不可用。
解决方法:
首先尝试的的一个方法就是把这个整型量换算为二进制数,最后需要用数码管显示的时候再用函数转化。最开始这个方法是可以解决问题的,但随着功能的增加,需要对这个信号量进行大量的运算,结果还是超了。
最后解决方法是,定义多个信号量,将上边的整型按位拆分,例如最大需要999,就定义3个长度为4的位向量(0000-1010),最后显示的时候,依次处理,不过这种方法的难度在于,每一次运算都要做进位运算,当个位满10的时候就要进位到十位,每一位都要判断,虽然代码麻烦一些,但是可省下很多逻辑单元。
这个问题应该算是VHDL语言的特性了,给一个信号量赋值的时候,不会有效,整个进程执行到最后一条语句时进程接下来挂起时,数据才发生带入,当设计很简单的电路时,延迟一个时钟周期貌似没什么问题,但是设计很难的项目时,必须要严谨的对待每一个时钟周期。
解决方法:
我用的方法就是,在process中定义一个变量,初始值为信号量的值,在进程中对信号的赋值是立刻实现的,然后对这个变量进行操作,就行了,最后还要记得在进程的结束处将变量的值赋给信号量存储起来。
这个问题不知道算不算是问题,但是我在做板载测试的时候确实遇到了,一个case语句,在不同的分支中,如果都有对同一个量的赋值运算,这个量的最后值可能不合乎逻辑。不论是信号量还是变量。
解决方法:
我对EDA的内部逻辑并不是很懂,可能是因为并行性?如果对于软件设计来说,case语句中不同分支是相互独立的,绝不会相互影响的,但是在VHDL中有些小问题。我的解决方法就是将case换成if,最后就可以通过了。
对于本学科,数字系统设计的一些感想,因为我之前已经入门了嵌入式开发,深知硬件设计的难度,尤其是课本前几章的复杂的理论体系中,我学的有些迷茫。不过当拿到硬件之后,我就开始慢慢的了解EDA,用板子实现一个个常用功能非常有成就感,但是学习过程中,我觉得最大的难点在于,现在网络上对于EDA的资料太少,当quartus报错时,很多报错都查不到相关原因,只能自己一步一步修改代码,一句一句代码的改来确定报错原因。其次的难点在于硬件设计和软件设计还是有很大的区别,虽然VHDL像高级语言一样,有很多的方便的用法,但是它始终还是硬件。硬件的学习真是一个比较漫长且难熬的过程,不过幸好在不断的改错下完成了大作业。=w=
硬件资源:FLEX10k系列的EPF10K20TC144-4
软件资源:QuartusII 9
参考资料:EDA-I便携式数字系统实验与设计平台说明书.pdf和EDA技术与VHDL设计(第二版)徐志军、王金明、严廷辉等编著。
所需资源:
本次实验设计输入端口共需要5个:2个时钟信号、2个输入按键、1个输入开关;输出端口为2类,1个是数码管的选择位矢量,1个是数码管的显示数据。所以共需要资源为:8个数码管的片选sel(0…7)(pin_119—pin_135),数码管显示digital(7…0)(pin_136—pin_144),两个时钟信号(clk_digital:pin_55、clk_run:pin_122),2个输入按键(key_begin:pin_43、key_stop:pin_33),1个输入开关(key_wait:pin_44)
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
ENTITY taxi is --配置实体
port(
clk_digital : in std_logic;
clk_run : in std_logic;
key_begin: in std_logic;
key_stop: in std_logic;
key_wait:in std_logic;
sel : out std_logic_vector(7 downto 0);
digital : out std_logic_vector(7 downto 0)
);
end ;
architecture cost of taxi is --配置结构体
TYPE digital_ARRAY is array (7 downto 0) of std_logic_vector(7 downto 0); --digital个LED灯
signal digitals : digital_ARRAY;
signal digital_index : integer range 0 to 7;
TYPE digital_codes is array (0 to 9) of std_logic_vector(7 downto 0); --数码管译码数组
signal digital_code : digital_codes;
signal digital_code_point : digital_codes;
signal flag : std_logic := '0';
signal state : integer range 0 to 1 := 0; --0:stop; 1:run;
signal km_0 : std_logic_vector(3 downto 0):= "0000";
signal km_1 : std_logic_vector(3 downto 0):= "0000";
signal free_0 : std_logic_vector(4 downto 0) :="00000";
signal free_1 : std_logic_vector(4 downto 0) :="01000";
signal free_2 : std_logic_vector(4 downto 0) :="00000";
signal flag_free : std_logic := '0';
signal wait_time_min : std_logic_vector(3 downto 0) := "0000";
signal wait_time_sec_0 : std_logic_vector(3 downto 0):= "0000";
signal wait_time_sec_1 : std_logic_vector(3 downto 0):= "0000";
signal counter_6 : integer range 0 to 6 :=0;
begin
--数码管译码数组
--digitals(index) <= digital_code(x_index)
digital_code(0) <= "11111100";
digital_code(1) <= "01100000";
digital_code(2) <= "11011010";
digital_code(3) <= "11110010";
digital_code(4) <= "01100110";
digital_code(5) <= "10110110";
digital_code(6) <= "10111110";
digital_code(7) <= "11100000";
digital_code(8) <= "11111110";
digital_code(9) <= "11110110";
digital_code_point(0) <= "11111101";
digital_code_point(1) <= "01100001";
digital_code_point(2) <= "11011011";
digital_code_point(3) <= "11110011";
digital_code_point(4) <= "01100111";
digital_code_point(5) <= "10110111";
digital_code_point(6) <= "10111111";
digital_code_point(7) <= "11100001";
digital_code_point(8) <= "11111111";
digital_code_point(9) <= "11110111";
digital_show:process(clk_digital) --动态扫描8个管
begin
if clk_digital'event and clk_digital = '1' then
digital <= digitals(digital_index);
sel <= NOT ( conv_std_logic_vector(2**digital_index,8) ); --构造位向量,选中哪一个数码管
if digital_index = 7 then
digital_index <= 0;
else
digital_index <= digital_index + 1 ;
end if;
end if;
end process;
--结果的展示效果
--先用3管显示等待时间
--再用2管显示已经行驶的公里数
--最后用3管显示价格
show:process(clk_digital)
begin
digitals(7) <= digital_code_point( conv_integer(wait_time_min) );
digitals(6) <= digital_code(conv_integer(wait_time_sec_1));
digitals(5) <= digital_code(conv_integer(wait_time_sec_0));
digitals(4) <= digital_code_point(conv_integer(km_1));
digitals(3) <= digital_code(conv_integer(km_0));
digitals(2) <= digital_code( conv_integer(free_2) );
digitals(1) <= digital_code_point( conv_integer(free_1) );
digitals(0) <= digital_code(conv_integer(free_0));
end process;
process(key_begin,key_stop) --状态转化的条件
begin
if key_stop ='0' then
state <= 0;
else
if key_begin = '0' then
state <= 1;
end if;
end if;
end process;
process(clk_run,state)
--用变量解决信号赋值的延迟问题
variable km_0_tmp : std_logic_vector(3 downto 0) := km_0;
variable km_1_tmp : std_logic_vector(3 downto 0) := km_1;
variable free_0_tmp : std_logic_vector(4 downto 0) := free_0;
variable free_1_tmp : std_logic_vector(4 downto 0) := free_1;
variable free_2_tmp : std_logic_vector(4 downto 0) := free_2;
variable flag_free_tmp : std_logic := flag_free;
variable wait_time_min_tmp : std_logic_vector(3 downto 0) := wait_time_min;
variable wait_time_sec_0_tmp : std_logic_vector(3 downto 0) := wait_time_sec_0;
variable wait_time_sec_1_tmp : std_logic_vector(3 downto 0) := wait_time_sec_1;
variable counter_6_tmp : integer range 0 to 6 := counter_6;
begin
case state is
when 0 =>--stop
flag <= '0';
when 1 =>--run
if flag = '0' then --每一次出发前清空上一次的结果
km_0_tmp := "0000";
km_1_tmp := "0000";
free_0_tmp := "00000";
free_1_tmp := "01000";
free_2_tmp := "00000";
flag_free_tmp := '0';
wait_time_min_tmp := "0000";
wait_time_sec_0_tmp := "0000";
wait_time_sec_1_tmp := "0000";
flag <= '1';
else
if clk_run'event and clk_run = '1' then --统一时钟以免出现数据被错误修改
if key_wait = '1' then --在等待状态下
wait_time_sec_0_tmp := wait_time_sec_0_tmp + "0001";
--以下代码用于处理进位
if wait_time_sec_0_tmp >= "1010" then
wait_time_sec_1_tmp := wait_time_sec_1_tmp + "0001";
wait_time_sec_0_tmp := wait_time_sec_0_tmp - "1010";
end if;
if wait_time_sec_1_tmp >= "0110" then
wait_time_min_tmp := wait_time_min_tmp + "0001";
wait_time_sec_1_tmp := wait_time_sec_1_tmp - "0110";
end if;
if wait_time_min_tmp >= "1010" then
wait_time_min_tmp := "0000";
end if;
counter_6_tmp := counter_6_tmp + 1; --计数器,每等待6s费用增加0.1
if counter_6_tmp >= 6 then
counter_6_tmp := 0;
free_0_tmp := free_0_tmp + "00001";
if free_0_tmp >= "01010" then
free_1_tmp := free_1_tmp + "00001";
free_0_tmp := free_0_tmp - "01010";
end if;
if free_1_tmp >= "01010" then
free_2_tmp := free_2_tmp + "00001";
free_1_tmp := free_1_tmp - "01010";
end if;
if free_2_tmp >= "01010" then
free_2_tmp := "00000";
end if;
end if;
else --在运行状态下
km_0_tmp := km_0_tmp + "0001" ;
--以下代码用于处理满10进位
if km_0_tmp >= "1010" then
km_1_tmp := km_1_tmp + "0001";
km_0_tmp := "0000";
end if;
if km_1_tmp >= "0011" and km_0_tmp > "0000" then
flag_free_tmp := '1';
end if;
--如果超过9.9公里,清空公里显示但是计费仍存在
if km_1_tmp >= "1010" then
km_1_tmp := km_1_tmp - "1010";
end if;
--超过3公里开始计费
if flag_free_tmp = '1' then
free_0_tmp := free_0_tmp + "00010";
if free_0_tmp >= "01010" then
free_1_tmp := free_1_tmp + "00001";
free_0_tmp := free_0_tmp - "01010";
end if;
if free_1_tmp >= "01010" then
free_2_tmp := free_2_tmp + "00001";
free_1_tmp := free_1_tmp - "01010";
end if;
if free_2_tmp >= "01010" then
free_2_tmp := "00000";
end if;
end if;
end if;--运行/等待状态
end if;--时钟上升沿
end if; --清空上一次的结果
when others =>
flag <= '0';
end case;
--将变量的值赋给信号
km_0 <= km_0_tmp;
km_1 <= km_1_tmp;
flag_free <= flag_free_tmp;
wait_time_min <= wait_time_min_tmp;
wait_time_sec_0 <= wait_time_sec_0_tmp;
wait_time_sec_1 <= wait_time_sec_1_tmp;
counter_6 <= counter_6_tmp;
free_0 <= free_0_tmp;
free_1 <= free_1_tmp;
free_2 <= free_2_tmp;
end process;
end cost;