计算机接口技术初探

2017-08-18 10:58:17 FPGAVideoAudio

上学期选了一门课,叫《微型计算机系统接口技术》,很久没有上过这么有意思的课了,感觉有必要记录一下。事实上前两篇文章也是我在学这门课的学习记录。

课程简介

课程的目标是:在一个FPGA开发板(如下图)上,对FPGA进行编程,来实现一个或多个计算机接口。前提:需要对数字电路有一定的了解。

需要解释的几点:

  • FPGA是一种可编程的硬件。
  • 硬件编程几乎等价于设计电路。
  • 计算机所有的接口本质上就是若干根导线。
  • 设计接口就是设计接口上信息传输的协议,即导线上电信号的变化方式。
  • 实现接口就是按照设计好的协议,通过对硬件编程,实现导线上电信号的变化。

课程使用的课本是《微型计算机系统接口技术实验教程:基于FPGA(第二版)》,书作者也就是这门课的任课教师。

课本上的实验包含了开发板上所有的接口,主要包括:USB、RJ45、RS232、PCI-e X1、3.5mm Audio、VGA等,还可以在其上实现八段数码管、LCD、LED灯的控制等等。

实验采用VHDL硬件描述语言来进行逻辑设计,使用Xilinx公司的开发平台Xilinx ISE Design Suite 14.4进行综合、仿真与实现。

实验使用的是清华大学EDK-SP6ADSP-TXMFI001微机接口技术实验平台,它基于Xilinx公司的Spartan-6家族的FPGA芯片,主板芯片采用XC6S45LXT,系统时钟为100MHz。对于音频接口来说,本实验还使用了AK4520芯片做数模转换,而对于VGA接口来说,本实验使用了AD7125做数模转换。

关于上课

课程有两个任课教师和一个实验指导教师,教学占四分之一,实验占四分之三。两个任课教师年龄都比较大了,所以讲课效果其实不太理想,听是不太能听懂的,所以还是要靠自学啊。虽然老师讲课效果不太理想,但能看出来三个老师都还是比较懂这个课程的,而且都挺热心的。

对于教学部分来说,主要目的是对这门课有个大概的了解以及学习在FPGA上编程的相关软件的使用。

准备工作

这门课程以实验为主,课程要求是以个人或小组的形式,挑选其中一个实验来完成。我本来是想做RJ45以太网口的实验的,后来因为这个实验最终完成后也只能看到ping通,所以没选它。音频和视频实验的反馈更强,但由于两个实验的工作量都比较小,我最终决定自己设计一个课本上没有的、音视频接口组合实验并独立完成。

由于之前没有做过硬件编程,所以一切都要从头学起,第一步就是学习硬件编程语言。由于老师上课使用的是VHDL,所以我没有选择Verilog。

经再三考虑,我决定先系统地学一遍VHDL,使用的教材是《VHDL数字电路设计教程》,花了一个月的时间把这本书学习了一遍,总体感觉这本书不错,适合初学也适合自学。不过书中也有一些小错误,但基本上自己都能发现并改正。

这本书有个比较好的地方就是它还会教你一些硬件编程的方法,例如状态机,就像讲C语言的书大多都会讲一下链表一样。事实上了解状态机的设计对完成实验起了巨大帮助。

实验原理与设计

由于我是自己设计音视频组合实验,因此我就要对音频和视频两个实验都有清晰的认识和了解,在完全理解两个实验的基础上才能做实验,而理解两个实验的前提是理解两个接口。

3.5mm音频实验

3.5mm音频口的本质其实是模拟信号,由前文《声音的产生与声音的数字化》可以知道,音频口上传输的电信号,本质上是对声波信号的模拟。如果我们是输出模拟信号的话,编程具有非常大的复杂度。具体到本实验,开发板提供了一个叫做AK4520的芯片,来帮我们做数模转换的工作。也就是说,AK4520芯片的输出端通过导线接到了3.5mm音频口上,我们只需按照AK4520芯片需要的数字信号的格式,控制芯片输入端信号的内容即可。这样就能以数字信号的形式,实现3.5mm音频口上的声音信号输出了。

而仔细研究AK4520芯片后,可以知道,它所需的输入信号其实基本上等同于WAV格式,也就是PCM编码(可参考前文WAV音频格式详解)。具体来说,它是位深为20bits、采样率为48000kHz、2声道的PCM数据的串行输出,只不过虽然位深是20bits,但实际上芯片工作时左右声道各占32bits的时间,剩下12bits是无效数据。时序图如下:

VGA视频实验

VGA也是模拟信号,同理开发板上也集成了AD7125芯片用于数模转换。视频比音频简单不少,按照从上往下、从左往右的扫描方式,依次输出RGB数据内容即可控制屏幕上的输出内容。

实验设计

我的目标是在课本实验的基础上,将音频3.5mm接口与视频VGA接口实验进行结合与修改,完成一个输出自定义乐曲并在显示器上显示音频电平的实验。

因此,首先我需要决定我想输出的乐曲。考虑到FPGA上的ROM大小有限,因此我需要使用最简单的方波、三角波和噪声波来合成乐曲,使用一定方法压缩后存储在ROM中。在播放的同时读取电平值,将电平高低表现在VGA图像上。

其实正确的做法应该是实现方波、三角波和噪声波的信号发生器,这样的话对ROM容量就没什么需求了,但感觉不一定能写出来,因此使用上述方法。

进行实验

谱曲

FamiTracker谱曲软件使用最简单的方波、三角波和噪声波来合成乐曲。在这里,我使用这个软件进行乐曲的谱曲,并通过导出成txt文本文件的方式将曲谱输出,便于让我自己编写C++程序处理曲谱,从而输出波形。

我们常用的音频文件几乎都使用16bits的位深,几乎没有采用20bits位深的,但当我们了解了音频文件的原理之后,这事就容易解决了。对于20bits,我们只输出前16bits,后4bits用0填充,这样就相当于电平值变成了原来的16倍,也就是音量提高了,而不产生其他变化。

这个软件可以做出5个通道的曲子,其中最后一个是PCM采样通道,一般用不到,这里我也并没有使用它。因此,我总共使用的就只有前4个通道,分别是方波(主旋律)、方波(副旋律)、三角波(BASS)、噪声波(鼓点)。利用这几个通道,我们就可以实现一个简单的乐曲的制作。

使用这个软件的主要目的是在谱曲时能够听到输出的音乐的音调和节奏是否正确,虽然这个软件也能够输出wav音频文件,但由于FPGA的ROM容量非常小,根本无法保存这么庞大的文件,因此我们需要使用一些手段进行处理,想办法让ROM能够存下它。这就要求我们自己写程序来输出波形了。

编程实现波形输出与压缩

在了解了声音的产生原理之后,我们需要编写程序来实现波形的输出,在这里我选择C++进行处理。

程序的核心思想是将相同频率、相同波形的一段音频,分割成0.065秒的小块。这是一个音符的时间,我只存储这段时间的采样点,如果需要延长时间则重复这个小块即可。这样我们分析整个乐谱之后,就可以得到这个乐谱中可能用到的所有小块。我们把每个小块集合起来,可以得到一个各种频率和波形的采样点存储表,且每个采样点块是唯一的,不会产生冗余。同时,我们分析曲谱,可以得到每个时间点,每个通道当前需要播放哪个小块,于是我们可以制作出一个地址存储表,用于存储当前需要播放的小块在采样点存储表中的地址。

为了保证程序产生的两张存储表没有错误,我将它们按照之后VHDL程序将要采取的读取方法进行读取,写成了wav格式的文件进行测试,从而可以直观地通过听来判断两张存储表是否正确。

在程序完成后,将两张存储表(二进制格式)进行处理,再存储进FPGA的ROM中,从而让硬件可以直接读取。我们将地址存储表命名为rom1,采样点存储表命名为rom2。

音频状态机设计

整个程序中最重要的就是音频部分的状态机设计了。因为我的整个实验都是自己设计的,因此不能使用实验指导书中的已经设计好的状态机,而需要自己设计。

因此,我通过设计与后续的不断调整,设计出如下状态机:

它的核心思想是,通过rom1读取到当前需要输出的数据在rom2的地址,然后根据这个地址找到在rom2的数据,进行输出。要注意rom2中的数据其实是连续的一整段数据(因为包含了0.065秒的采样点数据),因此只有这一段数据全都输出完了才会进入到rom1的下一个地址进行读取。同理,在这个过程中一定要注意如果rom1读完了,也就是音乐播放完了,那么就要将地址置0,从而实现循环播放乐曲的效果。

另外,状态机中的rstDataCnt、incDataCnt、incKeyIdx、incBlockIdx、shiftOut、rstBlockIdx、rstKeyIdx、getDataSum等信号需要在两段式状态机的第二段中进行具体效果的实现。也就是要实现类似如下代码的功能。

if rstDataCnt='1' then
    dataCnt<=(others => '0');
elsif incDataCnt='1' then

视频部分音频电平显示设计

首先,对于图片的显示非常简单,我们只需要从rom中直接读取图片的二进制数据,按照VGA的扫描方式将数据从rom读取并传入AD7125芯片就可以了。但对于电平值来说,就需要我们做一些处理。

对于色彩,我们可以首先预处理出一张RGB颜色的表格,在程序中将其定义为constant变量即可。例如:constant color : rgbbits:=("000011110000110111111101", ......

我们知道,电平值是一个16bits的数据,因此我们需要将这个数据从音频模块传输出来,被视频模块所使用。由于这个数据量非常大,速度非常快,因此我们需要设置一个count做延迟,即count达到一定的数值之后才取一次16bits电平数据,然后清空count。

当我们得到电平值之后,就可以根据这个值,按比例绘制一列像素点,值越大则一列像素点有颜色的部分越高。

遇到的问题

我遇到的第一个找了很久才解决的BUG,就是大端、小端字节序的问题。按照书上对于rom的用法,我将它应用在了音频二进制数据的存储上,却不能正常工作。其真正原因在于,bmp图像是使用大端表示法进行二进制数据存储的,因此直接将其二进制数据去掉文件头简单处理后即可直接被IP Core使用。但我自己生成的wav数据是就不可以,因为wav格式使用的是小端表示法。因此我需要手动将我想输出的数据转成大端表示法后再输出,这样才能保证读取的数据没有问题。

这第一个问题相对来说还是比较容易解决的,因为它可以在仿真阶段找出问题所在。

而第二个问题就相对复杂,我的状态机完成之后,一直没有产生想要的效果,而且我在仿真下完全看不出来哪里有问题,事实上这个问题产生的原因是,我同时在两个信号的作用下同时对同一个变量赋值,仿真没有找到问题的原因是第二次赋值的操作是我真正想要的,仿真正好也显示的是这一个值。

而我最终找到这一BUG是利用的开发板上的LED灯,一步步进行调试,最终定位到产生问题的状态机的状态上,仔细观察思考之后终于发现问题所在。

结果

实现播放音乐的同时在显示器上显示一张图片和音乐当前的电平值。

感受

学这门课的收获还是很大的,一开始,我对硬件编程是完全不了解的,VHDL更是一点都不会。考虑到正好可以趁这个机会加深自己对硬件的认识,就认真投入了进去学一下。硬件编程跟软件编程其实有很大的差别,最重要的是思维方式的不同。硬件编程其实就是在设计电路,写出来的每一个VHDL语句其实都是对电路的描述,因此在写程序时就要按照设计电路的思维方式去思考。

其实音频和视频这两个实验,既简单又不简单,虽然内容不多,但如果深入研究下去,会学到很多很多东西。

我认为这门课的实验设计得非常好,不论是简单的还是复杂的实验,只要真正动手去做了,就一定会有收获。

演示

如果长时间无法加载评论,请对 *.disqus.com 启用代理!