数字电路综合实验设计
简易出租车计价器的设计与实现
学院: 电子工程学院
班级:
学号:
姓名:
班内序号: 04
摘要
本文介绍了利用Quartus II综合性PLD/FPGA开发软件,在MAXII数字逻辑实验开发板上实现简易出租车计价器功能的设计与实现方法。本方案采用自上而下的设计理念,将整体电路按照功能划分为分频、计数、控制、数码管显示电路、点阵显示电路等若干模块,模块内用VHDL语言完成逻辑设计,模块间用原理图进行连接,使整体可实现计费、计时等功能。
关键字:可编程器件 模块化设计 出租车计价器 VHDL语言
一、设计任务要求
Ø 设计一台出租车计价器,不同情况下具有不同的收费标准。
基本要求:
1.行驶公里:用时钟2秒钟表示出租车匀速行驶1公里。在行车5公里以内,按起步价13元收费,超过5公里部分,以每公里2元收费。燃油附加费每运次1元。
2.途中等待:用按键控制中途等待,等待少于(包括)5秒不收费,超过5秒后没等待3秒钟加收1元。
3.用数码管分时显示计费金额、行驶里程和等候时间。字母A表示当前处于显示计费金额状态,字母B表示当前处于显示行驶里程状态,字母C表示当前处于显示等候时间状态。
4.用按键控制出租车空驶、载客状态。
提高要求:
1.用点阵滚动显示收费单据。
2.具有夜间模式,基本单价加收20%的费用。出租车收费以元为单位,元以下四舍五入。
3.出租车行驶速度可调可控。
4.多人乘车,分段计价。
5.自拟其他功能。
二、设计思路与结构框图
1.设计思路
由结构框图可以分析得出,该系统的的主体是计数控制器。该系统由外部控制载客控制信号和等待控制信号,以时钟信号的翻转为计数依据,完成对时间、里程和费用的计数,并将结果通过数码管译码电路显示出来。
该系统的控制信号可由拨码或按键输入,时钟由开发板内部时钟分频得出,输出有点阵输出和数码管输出。因此,可将系统分为分频器、计数控制器、数码管译码和显示以及点阵显示四部分。并以此得出系统的逻辑框图如下:
2.控制器部分的状态转移图
Ø 该控制器一共有三个基本状态:
空驶状态、载客状态和等待状态。
Ø 分别由v、和w来进行控制。
图3 状态转移图
三、分块电路和总体电路设计
(一)总体电路设计
图4 总体电路设计
Ø 整体电路由数码管译码电路、转换电路、点阵译码电路、技术控制电路、分频电路五部分构成。
Ø 分频电路将开发板内部的50MHz时钟分为500hz(供给数码管和点阵)、1Hz(控制器计时)、0.5Hz(行驶路程计费)、0.3Hz(等待时间计费)以及用于提高分频效率的其他若按频率。
Ø 计数控制电路由四部分构成,整体完成行驶距离的计数、等待时间的计数以及费用的计数。输出为三组十位二进制数。
Ø 转换电路有转换数据类型和在数码管上分时显示两个功能。首先将输入的十位二进制数转换为4位十进制数,并且利用除法和取余数的运算提取出个位、十位、百位、千位,并转换为四位二进制BCD码。其次利用0.5hz的时钟,将行驶里程、等待时间、计费金额以2秒为周期依次out1~4,供给数码管以便分时显示。
Ø 数码管译码电路以500HZ实现动态扫描,并将转换电路输出的5组BCD码依次译码,完成显示。
Ø 点阵译码器根据载客控制信号,分别显示“O”、“X”静态图案。
(二)分块电路设计
1.2 关键代码
ENTITY div_100 IS
PORT(
clk100 : IN STD_LOGIC; --输入时钟
clear: IN STD_LOGIC;
clk1: OUT STD_LOGIC); --输出时钟
END div_100;
ARCHITECTURE a100 OF div_100 IS
SIGNAL tmp100: INTEGER RANGE 0 TO 99; --计数信号
BEGIN
p1:PROCESS(clear,clk100) --p1进行100进制的计数
BEGIN
IF clear ='0' THEN
tmp100<=0;
ELSIF clk100'event AND clk100='1' THEN
IF tmp100=99 THEN
tmp100<=0;
ELSE
tmp100<=tmp100+1;
END IF;
END IF;
END PROCESS p1;
p2:PROCESS(clk100) --p2输出占空比为50%的新时钟
BEGIN
IF clk100'event AND clk100='1' THEN
IF tmp100>49 THEN
clk1<='1';
ELSE
clk1<='0';
END IF;
END IF;
END PROCESS p2;
END a100;
1.3 仿真波形
2. 计数控制器
2.1 电路模块
计数控制器由四部分组成,输入为三个不同频率的时钟、载客控制信号v和等待控制信号w。control1完成里程的计数cd和行驶费用cm1,control2完成等待时间ct的计数和等待开始计费(ct>5s)信号outt,control3接收到outt后输出为等待时间的计费cm2,control4为总费用(cm1+cm2+燃油附加费1元)。
设计关键思想是将费用、行驶里程、等待时间三个计数过程分开处理,并且将行驶计费和等待计费也分开处理。通过载客信号v、等待信号w分别触发不用的进程完成计数。因为行驶过程中每两秒1公里,每公里两元,等待过程中每三秒1元,因此分别用1hz、0.5hz、0.33hz的时钟完成计时、计费、记里程。
2.2 模块control1
2.2.1 电路模块
Ø 行驶里程计数cd和行驶计费cm1
2.2.2 关键代码
ENTITY control1 IS
PORT(
clk2: IN STD_LOGIC; --输入是0.5Hz的时钟
v: IN STD_LOGIC; --载客控制输入信号
w: IN STD_LOGIC; --等待信号
cm1: OUT STD_LOGIC_VECTOR(9 DOWNTO 0); --里程计费
cd: OUT STD_LOGIC_VECTOR(9 DOWNTO 0) ); --行驶距离
END control1 ;
ARCHITECTURE c OF control1 IS
SIGNAL temp1: STD_LOGIC_VECTOR(9 DOWNTO 0) ;
SIGNAL temp_cm: STD_LOGIC;
BEGIN
p1:PROCESS(clk2,v,w) --p1里程计数
BEGIN
IF (clk2'event and clk2='1') THEN
IF v='0' THEN --v=0时重新计数
temp1<="0000000000" ;
ELSIF w='1' THEN --开始等待时里程保持不变
temp1<=temp1;
ELSE temp1<=temp1 +1;
END IF;
END IF;
END PROCESS p1;
p2:PROCESS(clk2,temp1)
BEGIN
IF (clk2'event and clk2='1') THEN
IF temp1<"0000000110" THEN --小于5公里起步价13元
cm1<="0000001101";
cd<=temp1;
ELSE cm1<=temp1+temp1+"0000000011"; --大于5公里时的费用
cd<=temp1;
END IF;
END IF;
END PROCESS p2;
END c;
2.3 模块control2
2.3.1电路模块
Ø 等待时间计数ct和等待开始计费信号outt(ct>5s)
2.3.2关键代码
ENTITY control2 IS
PORT(
clk1: IN STD_LOGIC; --输入是1Hz的时钟
v: IN STD_LOGIC; --载客控制输入信号
w: IN STD_LOGIC; --等待信号
outt: out STD_LOGIC; --若大于5秒,输出1
ct: out STD_LOGIC_VECTOR(9 DOWNTO 0));--等待时间输出
END control2 ;
ARCHITECTURE c OF control2 IS
SIGNAL tempt: STD_LOGIC_VECTOR(9 DOWNTO 0) ;
SIGNAL temp1: INTEGER RANGE 0 TO 999;
BEGIN
p3:PROCESS(clk1,v,w) --累加等待时间
BEGIN
IF (clk1'event and clk1='1') THEN
IF v='0' THEN
tempt<="0000000000" ; --v=0清零重置
temp1<=0;
outt<='0';
ELSIF w='0' THEN --行驶时保持状态
tempt<=tempt;
temp1<=temp1;
ELSE tempt<=tempt +1; --等待时技术开始
temp1<=temp1 +1;
IF temp1>4 THEN --大于5秒时outt=1
outt<='1';
ELSE outt<='0';
END IF;
END IF;
END IF;
END PROCESS p3;
p4:PROCESS(clk1,tempt) --将信号赋给输出ct
BEGIN
ct<=tempt;
END PROCESS p4;
END c;
2.4 模块control3
2.4.1电路模块
Ø 当输入outt为1时,开始计数
Ø cm2为等待时间计费
2.4.2关键代码
ENTITY control3 IS
PORT(
clk3: IN STD_LOGIC; --输入是0.3Hz的时钟
outt: IN STD_LOGIC; --为1时,等待时间大于5s
v: IN STD_LOGIC; --载客控制输入信号
w: IN STD_LOGIC; --等待信号
cm2: out STD_LOGIC_VECTOR(9 DOWNTO 0));--等待计费
END control3 ;
ARCHITECTURE c OF control3 IS
SIGNAL tempm: STD_LOGIC_VECTOR(9 DOWNTO 0) ;
BEGIN
p5:PROCESS(clk3,outt,v,w)
BEGIN
IF outt='1'THEN --大于5秒时开始计费
IF (clk3'event and clk3='1') THEN
IF v='0' THEN
tempm<="0000000000"; --乘客下车等待时间重置
ELSIF w='0' THEN --行驶时保持状态
tempm<=tempm;
ELSE tempm<=tempm+1 ; --等待时开始计费
END IF;
END IF;
END IF;
END PROCESS p5;
p6: PROCESS(clk3,tempm)
BEGIN
IF V='0' THEN
cm2<="0000000000";
ELSE
cm2<=tempm; --将信号赋给输出:等待计费cm2
END IF;
END PROCESS p6;
END c;
2.5 control4
2.5.1 电路模块
Ø 将等待计费cm2和行驶计费cm1相加,并加上燃油费1元,得到总费用cm
2.5.2 关键代码
ENTITY control4 IS
PORT(
clk2: IN STD_LOGIC; --输入是0.5Hz的时钟
v: IN STD_LOGIC; --载客控制输入信号
cm1: in STD_LOGIC_VECTOR(9 DOWNTO 0);--行驶费用
cm2: in STD_LOGIC_VECTOR(9 DOWNTO 0); --等待费用
cm: out STD_LOGIC_VECTOR(9 DOWNTO 0));--总费用
END control4 ;
ARCHITECTURE c OF control4 IS
BEGIN
p5:PROCESS(clk2,v)
BEGIN
IF (clk2'event and clk2='1') THEN
IF v='0' THEN
cm <="0000000000";
ELSE cm<=cm1+cm2+1;
END IF;
END IF;
END PROCESS p5;
END c;
2.6控制计数部分的总仿真波形
3.转换器
3.1 电路模块
Ø 由三个输入:行驶距离cd、等待时间ct、计费金额cm
Ø 输出out1-5为四位二进制BCD码
Ø 实现功能:将十位二进制数按照位数转为为BCD码
3.2 关键代码
ENTITY transform IS
PORT(
clk: IN STD_LOGIC; --0.5hz的时钟
v: IN STD_LOGIC;
cd: in STD_LOGIC_VECTOR(9 DOWNTO 0);--行驶距离
ct: in STD_LOGIC_VECTOR(9 DOWNTO 0); --等待时间
cm: in STD_LOGIC_VECTOR(9 DOWNTO 0) ;--总费用
out1: out std_logic_vector(3 downto 0) ;--输出个位
out2: out std_logic_vector(3 downto 0); --输出十位
out3: out std_logic_vector(3 downto 0); --输出百位
out4: out std_logic_vector(3 downto 0); --输出千位
out5: out std_logic_vector(3 downto 0)); --输出ABC
END transform ;
ARCHITECTURE c OF transform IS
signal cc : integer range 0 to 1023 ;
signal t : std_logic_vector(1 downto 0) ;
signal q1,q2,q3:integer range 0 to 1000;
BEGIN
p1:PROCESS(clk,v) --p1进行3进制的计数
BEGIN
IF (clk'event and clk='1') THEN
IF v='0' THEN
t<="00";
elsif t="00" then
t<="01";
elsif t="01" then
t<="10";
elsif t="10" then
t<="00";
else t<="00";
END IF;
END IF;
END PROCESS p1;
p2:process(clk,t)
BEGIN
if (clk'event and clk='1') then
CASE t is --实现分时显示
when "00" => cc <=CONV_INTEGER(cm);out5<="1010" ;--计费,显示A
when "01" => cc <=CONV_INTEGER(cd);out5<="1011"; --计里程,显示b
when "10" => cc <=CONV_INTEGER(ct);out5<="1100"; --计时,显示c
when others => cc<=cc; --将十位2进制转为十进制
END CASE;
q1<=cc/10; --除法结果为整数部分
q2<=q1/10;
q3<=q2/10;
out1<=conv_std_logic_vector(cc rem 10,4); --取得个位
out2<=conv_std_logic_vector(q1 rem 10,4); --取得十位
out3<=conv_std_logic_vector(q2 rem 10,4); --取得百位
out4<=conv_std_logic_vector(q3 rem 10,4); --取得千位
end if;
end process p2;
END c;
4.数码管译码电路
4.1电路模块
4.2关键代码
ENTITY shumaguan IS
PORT(
clk1: IN STD_LOGIC; ---500hz数码管扫描时钟
out5,out4,out3,out2,out1: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
g:OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
cat:OUT STD_LOGIC_VECTOR(5 DOWNTO 0));
END shumaguan;
ARCHITECTURE a OF shumaguan IS
SIGNAL tmpg:STD_LOGIC_VECTOR(6 DOWNTO 0);
SIGNAL tmpn:STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL tmpc:INTEGER RANGE 0 TO 5;
BEGIN
p1:PROCESS(clk1) --六进制计数
BEGIN
if(clk1'event and clk1='1')then
if tmpc = 5 then
tmpc<=0;
else
tmpc<=tmpc+1;
END IF;
END IF;
END PROCESS p1;
p2:PROCESS(clk1) --数码管选通轮流显示
BEGIN
if(clk1'event and clk1='1')then --将个位、十位等赋给各个数码管
case tmpc is
WHEN 0 => tmpn<=out1;cat<="111110";
WHEN 1 => tmpn<=out2;cat<="111101";
WHEN 2 => tmpn<=out3;cat<="111011";
WHEN 3 => tmpn<=out4;cat<="110111";
WHEN 5 => tmpn<=out5;cat<="011111"; --显示abc
WHEN OTHERS=> tmpn<="1101";cat<="000000";
END CASE;
END IF;
END PROCESS p2;
p3:PROCESS(tmpn) --译码电路
BEGIN
CASE tmpn IS
WHEN"0000"=> tmpg<="1111110";--0
WHEN"0001"=> tmpg<="0110000";--1
WHEN"0010"=> tmpg<="1101101";--2
WHEN"0011"=> tmpg<="1111001";--3
WHEN"0100"=> tmpg<="0110011";--4
WHEN"0101"=> tmpg<="1011011";--5
WHEN"0110"=> tmpg<="1011111";--6
WHEN"0111"=> tmpg<="1110000";--7
WHEN"1000"=> tmpg<="1111111";--8
WHEN"1001"=> tmpg<="1111011";--9
WHEN"1010"=> tmpg<="1110111";--A
WHEN"1011"=> tmpg<="0011111";--B
WHEN"1100"=> tmpg<="1001110";--C
WHEN"1101"=> tmpg<="0000000";--blank
WHEN OTHERS=> tmpg<="ZZZZZZZ";
END CASE;
END PROCESS p3;
g<=tmpg;
END a;
5.点阵译码电路
5.1电路模块
5.2关键代码
ENTITY dz is
PORT(
clk500: IN STD_LOGIC;
v : IN STD_LOGIC;
row : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); --行
col : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); --列 END dz;
ARCHITECTURE a OF dz IS
SIGNAL tmp_row:STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL tmp_col:STD_LOGIC_VECTOR(7 DOWNTO 0);
begin
process(v,clk500)
begin
if (clk500'event and clk500='1')then
if v = '0'then
case tmp_col is
WHEN"11111110"=> tmp_col<="01111111";tmp_row<="00111100";
WHEN"01111111"=> tmp_col<="10111111";tmp_row<="01000010";
WHEN"10111111"=> tmp_col<="11011111";tmp_row<="10000001"; WHEN"11011111"=> tmp_col<="11101111";tmp_row<="10000001";
WHEN"11101111"=> tmp_col<="11110111";tmp_row<="10000001";
WHEN"11110111"=> tmp_col<="11111011";tmp_row<="10000001"; WHEN"11111011"=> tmp_col<="11111101";tmp_row<="01000010"; WHEN"11111101"=> tmp_col<="11111110";tmp_row<="00111100";
WHEN OTHERS=> tmp_col<="01111111";tmp_row<="00000000";
end case; --当未载客时,点阵显示“O”
else
case tmp_col is
WHEN"11111110"=> tmp_col<="01111111";tmp_row<="10000001";
WHEN"01111111"=> tmp_col<="10111111";tmp_row<="01000010";
WHEN"10111111"=> tmp_col<="11011111";tmp_row<="00100100"; WHEN"11011111"=> tmp_col<="11101111";tmp_row<="00011000"; WHEN"11101111"=> tmp_col<="11110111";tmp_row<="00011000"; WHEN"11110111"=> tmp_col<="11111011";tmp_row<="00100100"; WHEN"11111011"=> tmp_col<="11111101";tmp_row<="01000010"; WHEN"11111101"=> tmp_col<="11111110";tmp_row<="10000001"; WHEN OTHERS=> tmp_col<="01111111";tmp_row<="00000000";
end case; --载客时点阵显示“X”
end if;
end if;
end process ;
row<=tmp_col;
col<=tmp_row;
end a;
5.3仿真波形
四、实现功能说明
1,实现功能
Ø 实现计时、计里程、计费功能
Ø 数码管可分时显示行驶距离、费用、等待时间
Ø 点阵显示载客和空载状态
2.器材的资源占用情况
Ø 由图11可得:本电路一共用了576个逻辑元件,占总逻辑元件数的45%,总管脚数37,占总管脚的32%。
Ø 由图12看到,除了transform模块之外,各模块占用资源均不大。transform模块因为用到了除法、取余数等运算。因此占用资源较多,应在后期进行优化,降低资源利用率。
3.仿真波形图(见电路分析部分)
4.必要的测试方法
Ø 控制计数器整体仿真
Ø 控制器与转换器整体仿真
Ø 数码管赋值检查是否显示
Ø 故障时中间接输出,并且管脚设置为LED灯,看是否正常传输
五、故障与问题分析
1)计数显示不正确,有延迟
解决办法:在控制计数器内部,将组合逻辑部分和计数部分分到不同的进程,避免在时序进程出现大量运算,造成延迟。
2)计数时采用十位二进制计数,无法逐位读取,赋给各个数码管
解决办法1:先将二进制转为十进制,再利用除法和取余数运算得到各位数
值(改动小,但运算量大,占用资源多)
2:计数时改为逐位计数,后一位满十后向前一位进1(改动大,但
运算量减少)
3)仿真时计数器正常工作,但下载到板子时一直显示为0,无法确定是计数器的问题还是数码管的问题
解决办法:将计数器的一个输出作为LED的输入,发现LED不亮,说明控制器有问题。(后来经过检查发现是原理图连接有误)
六、总结和结论
这次数字电路综合实验是一次将理论、实验与现实应用结合在一起的实验。当第一次课拿到题目时,我感觉无从下手,几乎没有思路。但当三周后我基本完成了实验任务时,却又觉得整个电路结构无比清晰,甚至知道我的方案哪里做得好、哪里做得不好。在这三周的时间里,我觉得我有如下的收获:
1.基础知识与动手操作并重
由于我觉得自己基础不好,又加上是上学期的内容,因此在拿到题目后我并没有立即动手做,而是完整地阅读了一编实验教材,并且认真做了笔记。在之后的设计中,无论是对于具体语句还是设计思想,都有了成竹在胸的感觉,甚至还可以帮助同学们解决一些小的知识点,做一些小的改进。但光看书也是不够的,在编程的过程中,一开始仍有一些生涩,随着时间的推移,我的进度也逐渐加快。因此,这说明,在做具体的项目时一定首先要打好基础,不能过于着急动手,当然也不能一味地抠细节,忽视实验的重要性。
2. 整体的设计比具体的实现更重要
在老师的讲课过程中,不止一遍强调数字电路的模块的划分、上层的设计要比具体的编程语句更重要。一开始我并没有意识到这一问题,在做逻辑框图时细节和准确性都不够。因为我习惯一边开始做,一边想下一步怎么做。但在老师检查过程中我的框图被指出错误之后,我又重新做了逻辑框图。这一次,我感觉到在我认真思考后,我对整个电路的结构有了清晰地认识,特别是将电路划分为每一个细小的模块后,模块的具体实现就变得很简单,甚至很多都是现成的。这让我明白上层的设计、模块的划分才是最有创造性、最有技术含量的部分,我们作为设计师,应当将重点放在设计思想上,而不是具体的一个个语句。只有这样,我们的工作才是最具有价值的,而不是简单的模仿和重复。
3. 硬件编程语言与普通编程语言是有区别的
硬件编程语言侧重于10的逻辑实现,任何一段程序有一部分电路与之对应。在编程的过程中,要养成做硬件的思维习惯。比如这次,计数部分用了10位二进制来表示。但后来的数码管显示需要提取出各位的数值。不得已我用了除法和取余运算,造成的结果是用了大量的逻辑元件。但是我看到我的同学在计数时就将各位单独进行运算,比我节省了大量的元器件。这就是硬件语言的思维,在编程过程中应该尽量避免数据类型的转换、乘除法等,造成资源的浪费。
4. 测试是编程的重要环节
因为我们做比较大型的项目不多,而这次实验,因为用了大量的模块化设计,合理、准确的测试就很重要了。因为整体仿真的运算量太大,而且一旦出错很难发现什么地方出了问题,因此这就要求我们事先要对每一个模块都要进行仿真、测试。而且最好还要对相连的模块进行整体仿真,避免出现问题。而且在仿真、测试的过程中,选择合理的方法也很重要,设置适当的条件、选择合适的输入、输出等会让测试事半功倍。
总之,这次实验让我收获巨大。当我实现了生活中可能用到的一个功能时,不仅让我收获了成就感与信心,还让我对生活中各种数字电路的实现产生了浓厚的兴趣。看到生活中的电子产品时,有时候就会不自觉地思考这是怎么实现的?可能要分为哪些模块?这些模块有哪些功能,彼此又有什么联系?这让我对进一步了解数字电路的应用提供了强大的推动力。
七、完整源程序
电路原理图、源程序代码见电路分析部分