数字系统设计学习之出租车计价器设计

前言

数字系统设计的大作业来了,用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元/公里;

测试结果1

在这里插入图片描述
计算过程:
等待时间 花费:42秒/6 = 0.7元
前3公里 花费:8元
后2.8公里 花费: 2.8*2=5.6元
一共花费 0.7 + 8 + 5.6 = 14.3元

测试结果2

在这里插入图片描述
计算过程:
等待时间 花费: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在不同分支对同一量赋值

这个问题不知道算不算是问题,但是我在做板载测试的时候确实遇到了,一个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;

  • 14
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值