记录一下做这个EDA课设的过程,并总结一些学习收获。刚开始做的时候,老师就说:你们做这个课设呢,重要的不是结果,而是做的过程。我煞有其事的在笔记本上记录了这句话。OK,不废话了,开始学习吧!
一、设计要求
1.能发出1、2、3、4、5、6、7、1八个音;
2.用按键作为键盘;
3.用LED指示灯显示节拍;
4.C调到B调对应频率为
调 | 频率(Hz) |
---|---|
Ċ | 261.63*2 |
B | 493.88 |
A | 440.00 |
G | 392.00 |
F | 349.23 |
E | 329.63 |
D | 293.66 |
C | 261.63 |
5. 数码管显示那部分是我自己加的,没有设计要求,也算是一种拓展吧。
- 另外,需要设计用的软件的话,可以去后面这个百度网盘链接里下载使用,里面有安装包和安装说明。链接:https://pan.baidu.com/s/1cxaotp5YxtLt7juJ8EUqIQ
提取码:eda6
里面还有老师讲解的视频,认真看一看对整个课设的整体认识有很大帮助。对于讲解的视频,建议先看软件部分,再看硬件部分。
对了,还有一些相关文档。
二、设计过程
说实话,刚开始的时候,我脑子一片空白,不知道该怎么做,于是做了个计划:
- 第一天:熟悉软件的运用,搜集相关资料;
- 第二天:根据查找的资料,理清楚由哪些模块构成,以及各个模块之间的关系;
- 第三天:开始做模块一:音调发生模块,并理解其中的大部分VHDL语言;
- 第四天:开始做模块二:分频器模块,并理解其中的大部分VHDL语言;
- 第五天:原计划是做模块三,但是分频器模块还不是很懂,就继续理解模块二;
- 第六天:开始做模块三:显示器模块,并理解其中的大部分VHDL语言;
- 第七天:把三个模块整合,进行仿真;
- 某N天:在板子上进行烧录,观察结果并纠错,直到符合设计要求。
本来准备仿真完成之后就开始在板子上烧录的,但是板子数量不够,所以仿真完成后过了五天,我才拿到板子开始下载烧录。接下来看一看具体的设计过程吧。
第一天
用 quartus || 9.1 进行设计,首先得熟悉这个软件怎么用,我根据老师课上讲的例子以及指导书里的例子来练手,练了差不多一上午,聪明的孩子应该不用这么久,哈哈哈。
然后下午我就去找各种资料了。百度、CSDN等,还有图书馆,根据要做的八音电子琴,搜集各种资料,把觉得能用得到的资料保存或者收藏。
第二天
我把搜集到的资料都看了一遍,并理清需要的模块:1,音调发生模块,我取名为GetTone;2,分频器模块,我取名为FreqDivider;3,显示器模块,我取名为Disp。下面是整个系统的结构图:
这个已经分配好了管脚,我用的板子是EPM570T100C5。建立工程的时候一定要根据自己用的板子来选择。边上有PIN_的就是指管脚,可以先忽略,管脚最好仿真成功之后再分配。
每一个方图的左上角就是它们的名字。
VHDL硬件描述语言是一种“自上而下”的设计语言,通俗点说就是先有大局再考虑局部,从系统再到构成系统的每一个具体的模块。
GetTone模块:根据按键输入,控制Disp模块和FreqDivider模块这两个模块的输出。
Disp模块:根据GetTone模块给的信号,做相应的输出,控制数码管输出的数字和LED灯的亮灭。
FreqDivider模块:根据GetTone模块给的信号,做相应的输出,控制蜂鸣器的发声。
第三天
开始设计GetTone模块了。二话不说,先贴出VHDL代码:
LIBRARY IEEE ;
USE IEEE.STD_LOGIC_1164.ALL ;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY GetTone IS
PORT ( index: IN STD_LOGIC_VECTOR (6 DOWNTO 0 );
code: out STD_LOGIC_VECTOR (3 DOWNTO 0);
tone1: OUT INTEGER RANGE 0 TO 4000 );
END GetTone;
ARCHITECTURE behavioral OF GetTone IS
BEGIN
search: PROCESS(index)
BEGIN
CASE index Is
WHEN "1111110"=>tone1<=3822;code<="0000"; --1
WHEN "1111101"=>tone1<=3405;code<="0001"; --2
WHEN "1111011"=>tone1<=3034;code<="0010"; --3
WHEN "1110111"=>tone1<=2863;code<="0011"; --4
WHEN "1101111"=>tone1<=2551;code<="0100"; --5
WHEN "1011111"=>tone1<=2273;code<="0101"; --6
WHEN "1110110"=>tone1<=2025;code<="0110"; --7
WHEN "1101101"=>tone1<=1911;code<="0111"; --8
WHEN OTHERS =>tone1<=0;code<="1111";
END CASE;
END PROCESS;
END behavioral;
还是做一点简单说明吧,具体每一句的意思一定要自己花时间理解清楚,只有这样当你最后检错纠错时才能知道自己错在哪里以及怎么改正错误。
- index ,按键输入,我用了7个按键,所以有七位二进制数,0有效。index的值要根据自己所用按键数的个数以及有效位(0有效还是1有效)来设计,不一定非得跟这个一样;
- code,输出,用来控制Disp模块的,Disp模块中的code1=code;
- tone1,输出,分频预置数,用来控制FreqDivider模块的,FreqDivider模块中的tone2=tone1;
- case那部分,会根据不同的index输出对应的code和tone1。
这里说一下分频预置数tone1是怎么来的。分频遵循这样一个原则:分得越多(tone1越小),周期越大,频率越小。所以分得越少(tone1越大),频率越大。我用的板子只有一个固定的时钟频率50MHz,我在分频器模块先分成1MHz,再根据分频预置数动态分频。可以用这样一个公式来理解:tone1=1MHz/频率,比如C调的频率是261.63Hz,1MHz/261.63 取整就是3822,C调对应的tone1=3822。
调 | 频率 | Tone1分频预置数 |
---|---|---|
Ċ(高音) | 523.26 | 1911 |
B | 493.88 | 2025 |
A | 440.00 | 2273 |
G | 392.00 | 2551 |
F | 349.23 | 2863 |
E | 329.63 | 3034 |
D | 293.66 | 3405 |
C | 261.63 | 3822 |
第四天+第五天
开始设计FreqDivider模块,VHDL代码如下:
LIBRARY IEEE ;
USE IEEE.STD_LOGIC_1164.ALL ;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY FreqDivider IS
PORT (clk1:IN STD_LOGIC;
tone2:IN INTEGER RANGE 0 TO 4000;
spks: OUT STD_LOGIC);
END FreqDivider;
ARCHITECTURE behavioral OF FreqDivider IS
SIGNAL preclk, fullspks:STD_LOGIC;
BEGIN
pusel1:PROCESS (clk1)
VARIABLE count:INTEGER RANGE 0 TO 49; --50MHz->1MHz
BEGIN
IF clk1'EVENT AND clk1='1' THEN
IF count<49 THEN count := count+1;
ELSE count := 0;
END IF;
IF count<25 THEN preclk <='0';
ELSE preclk <= '1';
END IF;
END IF;
END PROCESS pusel1;
conspks:PROCESS (preclk, tone2) --relevant Freq->fullspks
VARIABLE count1:INTEGER RANGE 0 TO 4000;
BEGIN
IF preclk'EVENT AND preclk='1' THEN
IF count1<tone2 THEN
count1:=count1+1;
fullspks<='1';
ELSE
count1:=0;
fullspks<='0';
END IF;
END IF;
END PROCESS;
disspks:PROCESS(fullspks) --fullspks->spks
VARIABLE count2:STD_LOGIC:='0';
BEGIN
IF fullspks'EVENT AND fullspks='1' THEN
count2:=NOT count2;
IF count2='1' THEN
spks<='1';
ELSE
spks<='0';
END IF;
END IF;
END PROCESS;
END behavioral;
这部分是整个系统中最复杂的,因为我们的VHDL课只上了一点,所以我花了两天时间去理解每一句话,还专门去找教这门课的老师请教。
第六天
该设计Disp模块了,VHDL代码如下:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY Disp IS
PORT(code1:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
cs : OUT STD_LOGIC;
LED7S:OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
LED1: OUT STD_LOGIC;
LED2: OUT STD_LOGIC;
LED3: OUT STD_LOGIC;
LED4: OUT STD_LOGIC;
LED5: OUT STD_LOGIC;
LED6: OUT STD_LOGIC;
LED7: OUT STD_LOGIC;
LED8: OUT STD_LOGIC);
END ;
ARCHITECTURE one OF Disp IS
BEGIN
PROCESS(code1)
BEGIN
CASE code1(3 DOWNTO 0) IS --abcdefg
WHEN "0000" =>LED1<='0';LED2<='1';LED3<='1';LED4<='1';LED5<='1';LED6<='1';LED7<='1';LED8<='1';LED7S<="1001111";cs<='0';--1
WHEN "0001" =>LED1<='1';LED2<='0';LED3<='1';LED4<='1';LED5<='1';LED6<='1';LED7<='1';LED8<='1';LED7S<="0010010";cs<='0';--2
WHEN "0010" =>LED1<='1';LED2<='1';LED3<='0';LED4<='1';LED5<='1';LED6<='1';LED7<='1';LED8<='1';LED7S<="0000110";cs<='0';--3
WHEN "0011" =>LED1<='1';LED2<='1';LED3<='1';LED4<='0';LED5<='1';LED6<='1';LED7<='1';LED8<='1';LED7S<="1001100";cs<='0';--4
WHEN "0100" =>LED1<='1';LED2<='1';LED3<='1';LED4<='1';LED5<='0';LED6<='1';LED7<='1';LED8<='1';LED7S<="0100100";cs<='0';--5
WHEN "0101" =>LED1<='1';LED2<='1';LED3<='1';LED4<='1';LED5<='1';LED6<='0';LED7<='1';LED8<='1';LED7S<="0100000";cs<='0';--6
WHEN "0110" =>LED1<='1';LED2<='1';LED3<='1';LED4<='1';LED5<='1';LED6<='1';LED7<='0';LED8<='1';LED7S<="0001111";cs<='0';--7
WHEN "0111" =>LED1<='1';LED2<='1';LED3<='1';LED4<='1';LED5<='1';LED6<='1';LED7<='1';LED8<='0';LED7S<="0000000";cs<='0';--8
WHEN "1111" =>LED1<='1';LED2<='1';LED3<='1';LED4<='1';LED5<='1';LED6<='1';LED7<='1';LED8<='1';LED7S<="0000001";cs<='0';--8
WHEN others =>LED1<='1';LED2<='1';LED3<='1';LED4<='1';LED5<='1';LED6<='1';LED7<='1';LED8<='1';LED7S<="0000001";cs<='0';
END CASE;
END PROCESS;
END;
这个模块还是挺简单的:
- code1=code;
- cs,用来控制一个数码管是否工作,低电平(0)有效,因为板子上有多个数码管,而我只需要一个,那我选一个取用就好了;
- LED7S,控制数码管要显示的数字,低电平(0)有效;
- LED1-8,分别控制八个LED灯的亮和灭,低电平(0)有效;
调 | LED7S[6…0]–abcdefg | LED1-8 |
---|---|---|
Ċ(高音) | 0000000 | LED8亮 |
B | 0001111 | LED7亮 |
A | 0100000 | LED6亮 |
G | 0100100 | LED5亮 |
F | 1001100 | LED4亮 |
E | 0000110 | LED3亮 |
D | 0010010 | LED2亮 |
C | 1001111 | LED1亮 |
第七天
把上面三个模块整合一下吧!
- 先分别给每个模块生成符号;
- 新建原理图;
- 双击原理图空白页面,在project中选择需要的模块;
- 把三个模块按照预先设计好的系统结构,连接起来。温馨提示:注意输入输出处总线的命名,总线命名不对的话,编译或者仿真就会报错;
- 进行仿真,如果出错的话就检错纠错,直到仿真成功;
- 仿真成功后,根据使用的板子进行管脚分配;
- 最后画出来应该和下面这张图一样(管脚可以不一样,但一定要有):
某N天
终于到烧录这一步了。烧录?就是连接板子和电脑,开始在板子上验证自己的设计是否能实际实现。操作过程请看链接里的烧录文档。链接:https://pan.baidu.com/s/1cxaotp5YxtLt7juJ8EUqIQ
提取码:eda6
验证的过程中,如果老天保佑一下子就验证成功了,那么恭喜你;如果出错了,那么请一定要有耐心、细心和爱心,千万别一气之下把板子给砸了,相信吧,忧郁的日子总会过去。
三、设计结果
第三次用这张图了:
本来应该还有个板子验证结果的,但是验收答辩后就把板子交回去了,也忘记录下来了,那就这样吧,用心体会一下就好,最好自己动手做出来体会,哈哈哈。
这张系统结构图用了多次,其实也是有理由的:做模块设计的时候,一定要牢记最后要实现的系统。
四、收获
收获不少:
- 解决问题的方法就在那里,只是我不知道。比如上面贴出的代码,有些地方老出错,需要改,百思不得其解,最后发现要改的地方其实特别简单。
- 相信自己的力量。遇到问题应该首先问自己,然后上网查。除了那些固定化的操作性的问题,老师和同学大多数时候都不能帮你解决问题,只能听一听。
- 功夫不负有心人。出错了,我就一直想办法改,对它“不离不弃”,甚至吃饭睡觉的时候我都在想。有个比较顽强的错误,改了无数次,它依然“不动如山”,最后在某一天吃饭的时候突然灵机一动想到个办法,终于成功解决。
- 边做课设边做笔记。这点帮了我很多,可以时时翻看自己的设计思路,做笔记记录问题和自己尝试过的解决办法,那么在检错纠错的时候不至于茫然失措,还可以看看自己的笔记,最重要的是 不会被那些 “冥顽不灵” 的问题给气昏头,能保持理性思考。
PS: 第一次写博客,哈哈哈哈,见笑了。