目录
友情提示
一、电路功能描述
本电路主要实现了如下一些功能:
1. 电子琴演奏功能:
将相应按键按下的动作转化为键值;
不同键盘模式的切换:矩阵键盘、PS2键盘;
2. 音频驱动(放大)功能:
输出不同频率的方波驱动3.5mm耳机口放音;
3. 音调显示功能:
将相应音调显示为对应LED的高电平点亮,并通过数码管显示对应音节;
4. 曲谱播放功能:
将相应的内置歌曲自动播放出来。
二、方案设计
1. 总体设计方案
采用verilog编程,程序流程图如下;
RTL ANALYSIS 电路模块原理图:
2. 对原理框图或程序流程图做出简单解释
首先是PS2KEYBOARD,也就是左上角那个模块提供键值,下面clock control提供时钟分频,然后上面的键盘,再通过那个模块例化,就是那个RTL_MUX,这个可以选择键值,然后再根据那个自动播放的使能开关,就是和自动播放并通过使能开关,选择合适的键值,再传给右中间那个例化模块,就是RTL_ X MUX,然后一方面这个键值呢,传给灯的那个模块,例化模块决定灯怎么亮。
然后键值传给输出音乐输出模块,也就是这个audio_port。键值传给audio_port之后,再通过一边通过SD滤波器,使音乐放大,一边就是输出不同频率的方波,来控制这个音色的音调。上面那个同理,用矩阵键盘来获取相应的位宽为五的键值,然后输出。
3. 各模块介绍
3.1 音频驱动模块
原理内容较长,主要区分为“音调”和“音长”两个模块,通过表1和表2两个模块进行细节展示,详见附录I。
3.2 LED显示模块
(1)功能描述:
根据发出的不同的音调的声音点亮不同的LED
(2)管脚描述:
该模块较为简单,故内置在顶层模块里了,没有模块化
(3)实现说明:
用case语句根据接收到的键值查表点亮LED
3.3 数码管显示模块
(1)功能描述:
根据发出的不同音调,显示不同的数据,具体见下表:
音调 | 显示 | 音调 | 显示 | 音调 | 显示 |
低音1 | A1 | 中音1 | B1 | 高音1 | C1 |
低音2 | A2 | 中音2 | B2 | 高音2 | C2 |
低音3 | A3 | 中音3 | B3 | 高音3 | C3 |
低音4 | A4 | 中音4 | B4 | 高音4 | C4 |
低音5 | A5 | 中音5 | B5 | 高音5 | C5 |
低音6 | A6 | 中音6 | B6 | 高音6 | C6 |
低音7 | A7 | 中音7 | B7 | 高音7 | C7 |
表3 数码管显示对照表
(2) 管脚描述:
管脚名称 | 输入/输出 | 功能描述 |
clk | 输入 | 100MHZ时钟输入 |
reset | 输入 | 系统复位键 |
key_value | 输入 | 输出音调对应的键值传入(五位) |
an | 输出 | 数码管位选(两位) |
sseg | 输出 | 数码管段选(八位) |
表4 数码管模块端口说明表
(3) 实现说明:
我们通过设置一个多位宽(如20位;[19:0])的寄存器,每次系统时钟脉冲给该寄存器的值加一,然后检索寄存器的最高二位,(有四种结果,00,01,10,11),且由于当寄存器装满后,会溢出并归零。实现循环,因此,寄存器的高2位是在00,01,10,11之间循环的,
循环频率是100MHz/2**18=381.4Hz。这相当于2ms切换一次数码管显示,利用视觉暂留现象实现了四个数码管同时亮的结果。
3.4 矩阵键盘模块
(1) 功能描述:
用4*4的薄膜矩阵键盘实现系统交互,不同的按键代表不同的音调,按下按键,喇叭发出相应的音调声,松开按键声音消失。
(2) 管脚描述:
管脚名称 | 输入/输出 | 功能描述 |
clk_100 | 输入 | 100HZ时钟输入口 |
col | 输入 | 4位键盘列信号输入口,默认上拉 |
row_scan | 输出 | 4位键盘行扫描信号输出口,reg型 |
key_value | 输出 | 4位寄存器类型键值输出口 |
表5 矩阵键盘模块端口说明表
(3) 实现说明:
键盘列信号默认上拉为1,行扫描信号按每行依次置0的顺序扫描,扫描频率为100HZ。当有按键按下时,行扫描置0信号到达那行时,按键对应列信号会被拉低置0,由此触发扫描暂停信号,我们将此时的行列信号读取后译码为响应的键值输出;当按键松开时,列信号又恢复成上拉模式,行扫描继续进行,此时的行列信号被译码成无效的键值。
3.5 PS2机械键盘模块
(1) 功能描述:
实现与电脑键盘的对接,即将矩阵键盘切换为电脑键盘来进行演奏
(2) 管脚描述:
管脚名称 | 输入/输出 | 功能描述 |
rst | 输入 | 复位键,外接板上复位键 |
ps2_clk | 输入 | 键盘自带时钟输入口 |
ps2_data | 输入 | 键盘数据线输入口 |
sys_clk | 输入 | 板载100MHZ时钟输入口 |
key_out | 输出 | 5位寄存器类型键值输出口 |
表6 ps2键盘模块端口说明表
(3) 实现说明:
根据ps2协议,用板载超高频时钟捕获键盘时钟的下降沿,此时捕获键盘数据线上的数据。键盘每次发送数据有11位,其中2到9位为有效数据,每次捕获有效数据进行判断,是否为有效键值,是则转化为放音模块识别的有效键值,否则转化为无效键值。松开按键时,进行断码F0的识别,否则认为按键未松开。
简易的消抖,数据信号的收发开源资料甚多,且原理基本一致(如上所述)这种信息传递,信息打包功能似乎和蓝牙的UART,VGA的数据传送类似。移植代码后,知晓了每个按键都需要设置成有效键值,在有效的基础上再根据获取的键值执行音调的切换的设计思路。这样可以大大减少意外的乱码带来的问题。
3.6 时钟分频模块
(1) 功能描述:
该分频模块将板载100MHZ主频分为100HZ、16HZ/4HZ,主要为后续矩阵键盘模块、曲谱产生模块以及最后的放音驱动分频模块提供模块时钟。
(2) 管脚描述:
管脚名称 | 输入/输出 | 功能描述 |
sys_clk | 输入 | 100MHZ板载时钟输入口 |
sel | 输入 | 3位选择信号口,接拨码开关 |
clk_100 | 输出 | 100HZ时钟输出信号口 |
clk_select | 输出 | 根据曲谱节拍输出相应的频率 |
表7 时钟分频模块端口说明表
(3) 实现说明:
本模块通过计数器到达预先设定好的值后反馈置零的方法来对主频进行分频,同时通过sel接外部的拨码开关选择不同曲谱时的基本节拍。比如sel为100时歌曲选择为千本樱,clk_select节拍频率输出为16HZ;而sel为010时歌曲选择为天空之城,clk_select节拍频率输出为4HZ。
3.7 曲谱生成模块
(1) 功能描述:
将对应歌曲的谱子转化成一连串对应的键值,根据对应节拍的时钟不断地发送给最后的放音模块,实现歌曲的播放。
(2) 管脚描述:
管脚名称 | 输入/输出 | 功能描述 |
clk_16 | 输入 | 节拍时钟输入口,根据歌曲,频率不一定相同 |
en | 输入 | 自动放音使能,接外部拨码开关 |
songs_num | 输入 | 3位歌曲选择输入口,接外部拨码开关 |
tone | 输出 | 5位输出寄存器,保存曲谱当前对应的键值 |
表8 曲谱生成模块端口说明表
(3) 实现说明:
用case语句查表法,将曲谱对应的键值表内置在代码里
三、总结
回顾整个设计过程,可谓是起起落落。首先是基础乐理知识的整理和运用,这部分带来的感觉其实是,外强中干的。乐理转化基本没啥太大的困难,但最后总结出是:“一个音,一个频率”的规律,就基本完成了。
至于数字系统的设计,最让我们头疼,花费时间最多的还是两个键盘的连接。(Verilog代码方面因为开源资料很多,实际就没遇到太大的困难。大树底下好乘凉)
在连接方面,EGO1开发板的引脚口似乎有BUG,有不对应的关系,似乎是关于开口镜像对称了引脚名。导致一直未能实现矩阵键盘和EGO1开发板的连接;更有甚者,可能是矩阵键盘连接线的问题,矩阵键盘与EGO1开发板排针的连接很难稳定,如果不是人手工按住,很容易出现某个音的断连,失声情况,影响体验。
为更好地弹奏,我们决定使用PS2键盘。我们先用机械键盘进行尝试,通过指示灯D29的闪烁判断是否信号已经接受。通过观察,我们认为信号应该是已经被接受了,但并没有相应的功能执行,能想到的问题就只有两种,代码逻辑问题,以及机械键盘不支持PS2协议。
我们决定做两手准备,一方面购置了PS2键盘以及转换器;一方面对代码逻辑进行排查,优化逻辑结构;最后终于实现了弹奏功能。此次小系统的设计还有一个很关键的地方就是模块例化。如C语言函数调用一般,通过模块例化将各个.v文件编写的功能模块调用起来,实现整体的功能,这一点研究起来也费了很多周折,因为我们以前的设计都是在一个.v文件中执行所有功能的,但我们相信日后的研究学习中,模块例化是绕不开的一个坎儿。最后就是其他的基础功能实现,主要是数码管的显示,以及LED的控制。数码管显示我们直接套用以前模仿74LS248译码器的代码,实现了16进制数转归到数码管的显示,LED控制则是直接将16个灯直接受接受到的16进制信号联系。
额外的功能方面,为更好的显示效果,我们把信号通过VGA使得电脑显示屏展现出来,但由于代码编写的问题,一直未能在显示器中展示图像,最后暂时放弃了思路。另外,通过铱元素公司提供的蓝牙控制例程,我们实现了手机蓝牙控制数码管显示数字,LED的亮灭;但如何将蓝牙控制例程与我们的弹琴工程连接,这或许是一个可以日后探索的方向。
一些不足的方面,主要体现在按键的问题上,描述起来大概是1:切换音调时,出现了卡音和失音现象,我们怀疑的地方有几个,要么是按键消抖处理不够规范;要么是按键的逻辑没考虑完整,没考虑多个按键同时按下的情况。这一点的改进思路可以是取消按键弹起,声音输出关闭功能,但这会使得尾音难以处理,所以暂时没有解决。
由于没有建立键盘所有按键与音调输出的逻辑关系,当我们不小心按到了未经编写的按键时(也就是输入了无效键值),会导致其他有效键值的正常功能失效,解决方法是加大工程量,把所有的无效键值都归在error处,就可以实现弹奏的稳定性。
最后,我们未能实现仿真处理。因为编写仿真文件我们暂时来不及学习,所以我们只能一次次地生产比特流,下载,根据显示结果寻找,猜测BUG的出现之处,无疑带来了很大的麻烦,学习如何编写仿真文件,使我们之后深入学习的一个重要方向。
至此,整个小系统设计完成。这是一次很大程度的跨越,如模块的一到多使用,如各色Verilog语言的运用,对我们在FPGA开发之路上助力甚多。我们也有遗憾和不足,可在之后的学习中加以学习和完善。
数码管和LED显示模块
数码管的显示设计几乎和前面做的计数器数码管显示一致,未遇到太多困难。数字积木教材上有很详细的代码可以给我们参考,是很不错的学习资料。
矩阵键盘模块
矩阵键盘极易出现接触不良的现象,解决办法是采用公对母的杜邦线。但这样会显得臃肿和繁杂,于是我们并未把矩阵键盘作为最后的演示选择,仅仅弹奏一曲小星星做铺垫。
PS2机械键盘模块
PS2键盘弹奏是花费时间最多的地方,我们先用机械键盘试水,发现一直弹奏失败,不发声音。后总结有2个原因,该机械键盘不支持PS2协议;接触不良。于是我们购置了简朴的PS2接口键盘,并通过转换器与EGO1开发板连接,通过D29的微小闪烁判断数据接受成功,最后实现了弹奏功能。
时钟分频模块
时钟分频极为基础而重要的部分,其本质上就是IP核中divider的简单实现。我们的思路是设置一个装载值,比如,50M,每次系统时钟(P17,100MHz)电平变化时计数,计数值达到预装载值即翻转输出信号,达到分频效果。这部分的处理很顺利,因为之前就有设计的基础,也加深了我们对时钟分频的理解。
实例化处理
模块例化类似于函数调用,是将不同模块整合的重要方法。为简便处理,本实验我们采取的是位置关联,而非更严格精准的名称关联。但模块例化的改变并不频繁,加上由于整个系统的设计并不复杂,或许位置关联更为有效。
按键消抖处理(可日后实现)
实际的拨动开关和按键开关都是机械式的设备,开关动作来回抖动多次后才能稳定下来,这个过程会使得信号产生抖动。
按键消抖的严谨处理是采用数电课程学习的(有限)状态机。原则上,当输入信号稳定20ms以后才改变去抖动以后的输出值,而状态机可以实现这个这样的功能。严格的流程如下:
编写代码的核心思路是:
1:假定系统的起始态是zero(one)态,当sw变为1(0)时,系统转换为wait1_1态。
2:当处于wait1_1态时,有限状态机处于等待状态并将m_tick置为有效电平态。若sw变为0则表示1值所持续的时间过短有限状态机返回zero态。
3:这个动作在wait1_2态和wait1_3态也将再重复2次。
当开关弹回也是同理。
这部分由于时间关系,我们并没有完全实现,所以在实际演奏的时候会出现不适感,间断感,消抖处理不充分难逃其咎,比如,一个音没按完,就按下了另一个音,或者连点单一音调的时候,都可能出现,突然失音的尴尬情况,这个也有可能不仅仅是消抖处理不加的缘故,也有可能是内部逻辑不完善,但确实挺难找出来原因的。