Verilog实现智能电梯控制器.doc
2007-2008学年度第二学期电子技术基础课程设计-智能电梯控制器目 录0 序- 3 -1 原理与系统设计- 4 -1.1 思想来源:- 4 -1.2 付诸实施:- 4 -2 模块设计- 6 -2.1 模块示意图和输入输出描述- 6 -2.2 模块设计过程- 8 -2.3 波形仿真- 8 -3智能电梯综合后的整体电路图- 14 -4 基于SD卡的数码音乐播放器程序设计与注释- 14 -5 带有LCD驱动的智能电梯控制器源码与注释- 19 -5.1 分频模块- 19 -5.2 智能电梯主控制器模块- 20 -5.3 数码管译码模块- 28 -5.4 数码管时间译码模块- 29 -5.5 LCD仲裁器- 30 -5.6 LCD驱动模块- 30 -5.7 LCD控制模块- 32 -5.8 LCD显示模块- 34 -6 智能电梯控制器管脚分配- 40 -7 心得体会与建议_- 41 -0 序本学期初我们有为期一个月的电子技术课程设计,由电信系的曾喻江老师辅导。到第二周时,我选了基于SD卡的数码音乐播放器和智能电梯控制器两个课设题目,当时是满怀信心的,不过实际是,我没有更多的时间去同时做好两个任务。首先说一下基于SD卡的数码音乐播放器,我主要做了一个工作,就是了解并且熟悉的NIOS软核处理器的设计的基本流程,其实我在大二的时候一直想找一段时间去学习一下NIOS嵌入式系统的开发方面的知识,只不过由于其他的诸多原因一直没有着手。而这次曾老师提供DE的板子为我学习NIOS提供的基础平台。除了看了许多有关SOPC和NIOS软核处理器设计方面的书外,我也编写了一些SD卡数码音乐播放方面的C语言程序(见“基于SD卡的数码音乐播放器程序设计与注释”),可以完成播放一首英文歌曲并且可以在LCD上基本同步显示歌词。不过方法可能有些不好。然后我讲一下智能电梯的编写。智能电梯的编写的过程也不是一帆风顺的。而且我试过好多种方法去实现电梯的状态的转移。起初我想到的肯定是有限状态机。不过由于开始我想到只有六个请求(分别为16楼),后来在老师的启发下和东十二楼的电梯实际运行情况我发现,6个按钮肯定是不够的,所以我又加了5个向上的请求按钮和5个向下的请求按钮,这样总共就有16个按钮了,由于当时我没有想到用信号并置的方法,所以需要分析的情况实在是太多了,我也没有信心了。不过问题始终都是要得到解决的,后来我在我们寝室的一个同学的参考书上看到了一个用VHDL语言编写的智能电梯控制器的程序,不过很不完整,它给我的最大的启发就是“信号并置的算法”,我才发现这样一来的话,我的工作量就大大减少了。当时我不仅采用“信号并置的算法”外,还采纳了它的“以楼层为电梯的状态转移的依据”的思想,这确实是一个不错的方法,不过当时我一直没有任何进展,一是它是用VHDL语言编写的,而且我对这个语言不是很熟悉所以不是特别理解。后来竟然干起了把VHDL语言翻译成verilog语言的工作,这样没有任何含金量的工作让我浪费了不少时间。现在想起来,我才发现我竟然迷失了自己,我原先自己的算法已经被丢失了,留下了的仅仅是一些他人的程序。“以楼层为电梯的状态转移的依据”的编程方法让我没有得到任何进展,我放弃的这种处理多种状态的方法,继而转向了我原先的“有限状态机”的方法。使用三段式的有限状态机的方法也花了很多时间去修正和改善。实现了基本的功能,当时一遇到比较复杂的情况时(比喻同时有几个人在请求或者是同时有两个在不同楼层的请求时电梯该如何运行呢,这些特殊情况我在当时一直没有找到合适和有效的方法去解决。)。在这里我想感谢EI HUST bripengandre ,这位编程高手给我们展示了应该怎样用有限状态机去处理比较复杂的输入和状态转移的情况。我的程序中难免会有一些他的原创,这一点请读者原谅,由他的启发,我才发现我们应该学会利用强大的逻辑处理技巧去分析问题和解决问题。不同的状态归根结底是不同的二进制数的变化。而且一系列的惯性思维让我们有时候是作茧自缚,比如一说到楼层的变化,我们首先会想到加1或减1这样的算术运算,但是有时一个简单的右移和左移也能轻松的达到目的。现在再想起来我觉得用“有限状态机(finite state machine)”去实现多状态的电梯是一个不可超越的方法了。希望以后还有这样的机会去做一个硬件课程设计,锻炼编程能力。1 原理与系统设计1.1 思想来源:平时我们上课或者是上自习都去过东十二楼或者去过科技楼、南一楼,免不了坐坐电梯,对它的基本工作原理我们有知道多少了,这次我们要制作一个智能电梯控制器,必须对它的工作原理有十分清晰的了解。我们设计的智能电梯控制器应该可以实时接受各楼层的上下请求信号及电梯内部的停靠请求,然后根据这些请求实现对电梯正确的控制:1、除了顶层和底层外,各楼层均设有上下请求开关,顶层和底层分别设有下降和上升请求开关,这一点应该不难理解;电梯内设有乘客到达层次的请求开关。2、电梯每1s上升或下降一层3、电梯到达有停站请求的楼层后,经过1s后电梯门打开,开门指示灯亮,开门5s后电梯指示灯灭,电梯继续运行,直至运行完最后一个请求后停靠在当前层。以上是我们所应实现的基本功能。我在序言中也讲到了,“应用有限状态机”实现电梯的实时控制是最好不过的方法了,通过我的分析以及参考图书馆的有关书籍,也少不了参照一些网上的程序,最后总结出了电梯正常运行的七个状态:上升、下降、上升的过程中途停止、下降的过程中途停止、开门、关门、等待状态。电梯在上述七个状态间的转移是通过三段式状态机来实现的,各状态间的转移大体与生活中的电梯运转一致,有如下的基本原则:1、方向为第一优先准则,这就是曾老师给我们的技术指标。电梯在运转时先响应同方向上的请求,只有当同方向上的请求响应完后,才能转而响应不同方向上的请求。2、初始化状态为1楼等待门是关闭的。这个就不用多解释了。除了我对电梯的运行规律做出如上的分析外,我的另一个选择开发智能电梯控制器的原因是我想锻炼一下自己的逻辑思维和分析复杂问题的能力。1.2 付诸实施:题目既然选定了,我就要开始收集资料了。最开始我根据DE2板子所能提供的资源,把楼层数设为6层。从我查阅的书籍中我总结了两点是我可以借鉴的。首先,是怎样处理数量繁多的电梯输入信号,如果采用分情况讨论的话,程序一路写完,复杂度肯定是不堪设想,而且我还不敢保证是否分析到所有的情况了。always (call_up_1 or call_up_2 or call_up_3 or call_up_4 or call_up_5) up_all=1'b0, call_up_5, call_up_4, call_up_3, call_up_2, call_up_1; /将各下降请求信号实时地合并(1楼为底层,无下降请求,考虑到通用性,将第1位填零) always (call_down_2 or call_down_3 or call_down_4 or call_down_5 or call_down_6) down_all=call_down_6, call_down_5, call_down_4, call_down_3, call_down_2, 1'b0;/将各停靠请求信号实时地合并 always (request_1 or request_2 or request_3 or request_4 or request_5 or request_6) request_all=request_6, request_5, request_4, request_3, request_2, request_1;以上这一段程序就是我采用的信号并置处理很多输入信号的一种行之有效的方法。其次,我从资料中学习到的方法就是如何把信号并置的方法和有限状态机联系起来。这一点从下面的参数定义中可以窥见一二。 parameter WAIT=7'b0000001, UP=7'b0000010, DOWN=7'b0000100, UPSTOP=7'b0001000 , DOWNSTOP=7'b0010000, OPENDOOR=7'b0100000, CLOSEDOOR=7'b1000000;/定义楼层的符号常量 parameterFLOOR1=6'b000001,FLOOR2=6'b000010,FLOOR3=6'b000100, FLOOR4=6'b001000, FLOOR5=6'b010000, FLOOR6=6'b100000; parameter TRUE=1'b1, FALSE=1'b0;/定义门打开和门关闭的符号常量 parameter OPEN=1'b1, CLOSED=1'b0;/定义电梯上升,下降和静止的符号常量 parameter UPFLAG=2'b01,DNFLAG=2'b10,STATIC=2'b00;这里采用了七个状态实现了有限状态机。WAITUPUPSTOPDOWNOPENDOORDOWNSTOPCLOSEDOOR以上是我分析的两种基本的电梯状态转换图:黑线:WAIT TO UP TO UPSTOP TO OPENDOOR TO CLOSEDOOR TO WAIT粉红线:WAIT TO DOWN TO DOWNSTOP TO OPENDOOR TO CLOSEDOOR TO WAIT 由于状态转换的输入条件实在太多,在此我不画出,读者可以自行在程序(附有详细注释)中领会。我的程序中采用的是标准的MEALY型状态状态机。而且是老师建议的三段式的结构来写的。不过我有一点不明白的就是为什么在我的QUARTUS7.1中运用RTL VIEW不能显示出这个三段式的有限状态机。不过我会继续努力一下的,目前我智能用图形框来表示这个状态的流程了。2 模块设计2.1 模块示意图和输入输出描述(1) 电梯主控制器模块elevator_controller:端口声明:Input Port:call_up_1, call_up_2, call_up_3, call_up_4, call_up_5分别为1-5楼的上行请求信号,call_down_2, call_down_3, call_down_4, call_down_5, call_down_6则分别为2-6楼的下行请求信号request_1, request_2, request_3, request_4, request_5, request_6则分别为电梯内部的停靠1-6楼的请求上述各端口均为有请求时,输入为高电平,否则为低电平;clk分别为状态转移时钟,reset为复位信号Output Port:PosOut输出当前电梯所在的楼层,DoorFlag为开门标志,UpDnFlag为电梯上下标志LiftState输出当前电梯的状态.PosOut取值可为6'b000001,6'b000010,6'b000100,6'001000,6'b010000,6'b100000分别代表电梯处在1,2,3,4,5,6楼。这样编码的话,有利于后面的比较判断。DoorFlag取值可为1'b0,1'b1,分别代表当前门是关闭和当前门是打开的。UpDnFlag取值可为2'b00,2'b01,2'b10,分别代表当前电梯是上升的,下降的和静止的。LiftState7'b0000001,7'b0000010,7'b0000100,7'b0001000,7'b0010000,7'b0100000,7'b1000000,分别电梯处于等待模式、上升模式、下降模式、上升停止,下降停止、开门和关门等7个状态。(2)分频模块frequence_div:端口说明:Input ports:cp_50M;output ports:cp_1;(3)电梯状态仲裁器arbitrator:端口说明:Input ports:elevator_state;count_in;output ports:output open_enable,stop_enable,up_enable,down_enable,close_enable;(4)LCD驱动模块DE2_Default:端口说明:Input ports:input open_enable,stop_enable,up_enable,down_enable,close_enable;inputCLOCK_50;/50 MHzinput KEY;output ports:inout7:0LCD_DATA;/LCD Data bus 8 bitsoutputLCD_ON;/LCD Power ON/OFFoutputLCD_BLON;/LCD Back Light ON/OFFoutputLCD_RW;/LCD Read/Write Select, 0 = Write, 1 = ReadoutputLCD_EN;/LCD EnableoutputLCD_RS;/LCD Command/DataSelect, 0= Command, 1 = Data2.2 模块设计过程现在我简要的说明一下我的模块的设计过程:(1)电梯主控制器模块elevator_controller:此段智能电梯控制器由三个重要部分组成的。(a)信号并置部分,完成对5路向上请求、5路向下请求、6路内部请求的信号并置,化繁为简。(b)三段式有限状态机部分。在有请求的情况下,电梯控制器还要根据电梯的当前状态和当前的楼层去判断电梯的下一步该如何运作。(c)计数器部分。完成电梯的开门、关门的时间管理。(2)分频模块frequence_div:这段分频器完成对50Mhz的1分频操作。采用传统的“一半就翻转”的计数技巧。(3)电梯状态仲裁器arbitrator:完成电梯信号到LCD控制的信号转换。其中也采用了“电梯主控制器”中的信号并置的思想。这一点可以在我的程序中十分清楚的看到,在此我不再赘述。(4)LCD驱动模块DE2_Default:这个模块我是采用“ 拿来主义”的。因为是DE2板子提供的源程序,所以编写起来还算比较轻松。就只加了一个“ 根据不同的输入产生不同的输出 ”的模块。中途还遇到了字符型液晶不能更新的问题,不过在同学的帮助下,最终还是解决了。(5)数码管译码模块:(6)数码管时间译码模块:以上两个模块一起讲比较合适,因为它们都是采用了同样的译码原则,只不过条件不一样而已。我们可以针对不同的输入根据自己的意愿把它译成同样的数码显示。 以上的模块设计过程说的比较的简约。主要是大概的介绍了我的各个模块的基本设计原理,希望读者能从程序中仔细体会这种原理。2.3 波形仿真(1)当电梯处于初始状态时,电梯在高层有向下的请求时:(2)LCD的显示由于数据太多,所以单独显示如下:首先显示:“ it is static. ”随后会显示:以上的字符发送到液晶显示模块之后,可以显示“ door is rising. ”随后会显示:可以显示“ it is static. ”以上的字符发送到液晶显示模块之后,可以显示“ door is opening. ”随后会显示:以上的字符发送到液晶显示模块之后,可以显示“ it is closing. ”最后会显示下列字符,电梯重新回到初始等待状态:以上可以显示“ it is static. ” 说明:由于LCD显示波形会占用比较多的空间,所以以上我仅以在高层有向下的请求为例来说明,电梯的状态完全可以通过LCD来正确地显示出来。鉴于此,我的下面的波形将不展示LCD显示部分的波形图。(3)当电梯停在6楼处于等待状态时,在1楼和2楼同时有向上的请求时:(4)当电梯停在1楼处于等待状态时,在6楼和5楼同时有向下的请求时:(5)当电梯停在6楼时,有在5楼的向下的请求时,电梯应该先到五楼,电梯内部请求到1楼,如果电梯在下降的过程中,有在2楼的向上的请求时,电梯应该先相应内部请求,然后相应外部请求:(6)电梯的强制运行按钮forbid: 说明:由于电梯在实际运行的过程中,会遇到的情况种类很多,所以我在此不会一一例举,在上面,我以几种的典型的情况展示了我设计的智能电梯控制器的运行状态的正确转换。如果读者想要试试其它的几种情况,可以自己运行一下我的程序(附下或已打包)。3智能电梯综合后的整体电路图4 基于SD卡的数码音乐播放器程序设计与注释#include "basic_io.h"#include "LCD.h"#include "SD_Card.h"#include "stdio.h"int main(void) UINT16 i=0,Tmp1=0,Tmp2=0,k=0,count; float stamp38=10,15,20,26, 32,37,44,48, 49, 55,60,67,71, 72,78,84,89, 95,102,106,107, 112,118,125,148, 153,158,164,171, 175,176,181,187, 194,199,204,210, 217;/ 这个数组我是根据“千千静听”软件中所/提供的歌词时间戳来记录的,就是指不同/的时间点对应于不同的歌词。其中的标准/参照时间我是根据首次放歌经验校准的。 UINT32 j=720; BYTE Buffer512=0;/设置缓存 while(SD_card_init()/SD初始化 usleep(500000);/延时 LCD_Test();/LCD显示 for(count=0;count<38;count+) stampcount=(stampcount)/(10)*3748; while(1) SD_read_lba(Buffer,j,1); while(i<512) if(!IORD(AUDIO_0_BASE,0) Tmp1=(Bufferi+1<<8)|Bufferi; IOWR(AUDIO_0_BASE,0,Tmp1); i+=2; if(k=1000) / usleep(50000); LCD_Init(); LCD_Line2(); LCD_Show_Text("huangboxue"); /显示作者 if(k=2000) /usleep(50000); / LCD_Init(); LCD_Line2(); LCD_Show_Text(" "); LCD_Line2(); LCD_Show_Text("and duanbing"); /显示合作者 if(k=(UINT16)stamp0) /usleep(50000); LCD_Init(); LCD_Show_Text("Hoobastank "); LCD_Line2(); LCD_Show_Text("The Reason"); /显示歌名和歌手 if(k=(UINT16)stamp1) /usleep(50000); LCD_Init(); LCD_Show_Text("I'm not a "); LCD_Line2(); LCD_Show_Text("perfect person"); /显示歌词 if(k=(UINT16)stamp2) /usleep(50000); LCD_Init(); LCD_Show_Text("As many things I"); LCD_Line2(); LCD_Show_Text("wish I didn't do"); /显示歌词 if(k=(UINT16)stamp3) /usleep(50000); LCD_Init(); LCD_Show_Text("But I continue"); LCD_Line2(); LCD_Show_Text("learning"); /显示歌词 if(k=(UINT16)stamp4) /usleep(50000); LCD_Init(); LCD_Show_Text("I never meant to"); LCD_Line2(); LCD_Show_Text("do those things"); /显示歌词 if(k=(UINT16)stamp4+1000) LCD_Init(); LCD_Show_Text("to you"); /显示歌词 if(k=(UINT16)stamp5) /usleep(50000); LCD_Init(); LCD_Show_Text("And so I have to"); LCD_Line2(); LCD_Show_Text("say before I go"); /显示歌词 if(k=(UINT16)stamp6) /usleep(50000); LCD_Init(); LCD_Show_Text("That I just "); LCD_Line2(); LCD_Show_Text("want you to know"); /显示歌词 if(k=(UINT16)stamp7) /usleep(50000); LCD_Init(); LCD_Show_Text(" "); LCD_Line2(); LCD_Show_Text(" "); /显示歌词 if(k=(UINT16)stamp8) /usleep(50000); LCD_Init(); LCD_Show_Text("I've found a"); LCD_Line2(); LCD_Show_Text("reason for me"); /显示歌词 if(k=(UINT16)stamp9) /usleep(50000); LCD_Init(); LCD_Show_Text("To change who "); LCD_Line2(); LCD_Show_Text("I used to be"); /显示歌词 if(k=(UINT16)stamp10) /usleep(50000); LCD_Init(); LCD_Show_Text("A reason to "); LCD_Line2(); LCD_Show_Text("start over new"); /显示歌词 if(k=(UINT16)stamp11) /usleep(50000); LCD_Init(); LCD_Show_Text("and the reason"); LCD_Line2(); LCD_Show_Text(" is you"); /显示歌词 if(k=(UINT16)stamp12) /usleep(50000); LCD_Init(); LCD_Show_Text(" "); LCD_Line2(); LCD_Show_Text(" "); /显示歌词 if(k=(UINT16)stamp13) /usleep(50000); LCD_Init(); LCD_Show_Text("I'm sorry that"); LCD_Line2(); LCD_Show_Text("I hurt you"); /显示歌词 if(k=(UINT16)stamp14) /usleep(50000); LCD_Init(); LCD_Show_Text("It's something I "); LCD_Line2(); LCD_Show_Text("must live with"); /显示歌词 if(k=(UINT16)stamp14+1000) LCD_Init(); LCD_Show_Text("everyday"); /显示歌词 if(k=(UINT16)stamp15) /usleep(50000); LCD_Init(); LCD_Show_Text("And all the pain"); LCD_Line2(); LCD_Show_Text("I put you"); /显示歌词 if(k=(UINT16)stamp15+1000) LCD_Init(); LCD_Show_Text(" through"); /显示歌词 if(k=(UINT16)stamp16) /usleep(50000); LCD_Init(); LCD_Show_Text("I wish that I "); LCD_Line2(); LCD_Show_Text("could take it"); /显示歌词 if(k=(UINT16)stamp16+1000) LCD_Init(); LCD_Show_Text("all away"); /显示歌词 if(k=(UINT16)stamp17) /usleep(50000); LCD_Init(); LCD_Show_Text("And be the one "); LCD_Line2(); LCD_Show_Text("who catches all"); /显示歌词 if(k=(UINT16)stamp17+1000) LCD_Init(); LCD_Show_Text(" your tears"); /显示歌词 if(k=(UINT16)stamp18) /usleep(50000); LCD_Init(); LCD_Show_Text("Thats why i need"); LCD_Line2(); LCD_Show_Text("you to hear"); if(k=(UINT16)stamp19) /usleep(50000); LCD_Init(); LCD_Show_Text(" "); LCD_Line2(); LCD_Show_Text(" "); /显示歌词 if(k=(UINT16)stamp20) /usleep(50000); LCD_Init(); LCD_Show_Text("I've found a "); LCD_Line2(); LCD_Show_Text("resaon for me"); /显示歌词 if(k=(UINT16)stamp21) /usleep(50000); LCD_Init(); LCD_Show_Text("To change who"); LCD_Line2(); LCD_Show_Text("I used to be"); /显示歌词 if(k=(UINT16)stamp22) /usleep(50000); LCD_Init(); LCD_Show_Text("A reason to "); LCD_Line2(); LCD_Show_Text("start over new"); /显示歌词 if(k=(UINT16)stamp23) /usleep(50000); LCD_Init(); LCD_Show_Text("and the reason"); LCD_Line2(); LCD_Show_Text(" is You "); /显示歌词 if(k=(UINT16)stamp24) /usleep(50000); LCD_Init(); LCD_Show_Text(" "); LCD_Line2(); LCD_Show_Text(" "); /显示歌词 if(k=(UINT16)stamp25) /usleep(50000); LCD_Init(); LCD_Show_Text("I'm not a "); LCD_Line2(); LCD_Show_Text("perfect person"); /显示歌词 if(k=(UINT16)stamp26) /usleep(50000); LCD_Init(); LCD_Show_Text("I never meant"); LCD_Line2(); LCD_Show_Text("to do those"); /显示歌词 if(k=(UINT16)stamp26+1000) LCD_Init(); LCD_Show_Text("things to you"); /显示歌词 if(k=(UINT16)stamp27) /usleep(50000); LCD_Init(); LCD_Show_Text("And so I have to"); LCD_Line2(); LCD_Show_Text("say before I go"); /显示歌词 if(k=(UINT16)stamp28) /usleep(50000); LCD_Init(); LCD_Show_Text("That I just "); LCD_Line2(); LCD_Show_Text("want you to know"); if(k=(UINT16)stamp29) /usleep(50000); LCD_Init(); LCD_Show_Text(" "); LCD_Line2(); LCD_Show_Text(" "); /显示歌词 if(k=(UINT16)stamp30) /usleep(50000); LCD_Init(); LCD_Show_Text("I've found a");