第五章 任意波形发生器的软件设计
65
TestPnp.c负责处理添加设备例程和即插即用请求处理例子程;Loader.c实现了将数据下载至CY7C68013芯片RAM中的供应商自定义请求,由Cypress公司提供;Tagtest.c为本设计开发的固件程序,使用hex2c工具生成。hex2c是一个Win32控制台应用程序,功能是将Keil C51语言编译连接所得到的Intel十六进制记录(.hex文件)转换成C代码,用法为:hex2c
有了这四个文件,就可以对驱动程序进行编译了。在开发设备驱动的过程中,使用了VC++和DDK。VC++是包含标准编译工具(编译程序和连接程序)的集成开发环境,通过正确设置,其可用来建立内核模式的设备驱动程序;DDK是微软公司提供的驱动开发工具包,它是开发设备驱动程序所必须的软件组件,对不同的Windows版本有不同的DDK,最好不要混用。关于VC++开发驱动程序的编译环境的设置可参考相关书籍[32][33],在此不做介绍。
5.3.2 USB设备的访问
在Windows下,用户的Win32应用程序不能直接访问硬件设备,而只能通过设备驱动程序间接的对设备进行操作。在设计中,是通过Windows提供的3个标准API函数来访问USB设备的:
(1)通过CreateFile来打开USB设备,得到设备的句柄;
(2)通过DeviceIOControl向USB驱动程序发送IO控制代码,从而控制USB设备执行相应的操作;
(3)通过CloseHandle来关闭设备。
需要注意的是,CreateFile要和CloseHandle一一对应,打开一次就应该关闭一次。
5.4 用户应用程序
本设计使用LabWindows/CVI 8.0在上位机开发了一个虚拟仪器操作界面,实际上也就是生成任意波形数据的操作界面,通过在操作界面上可以选择输出波形的种类和参数,也可以手动绘制任意波形,同时可以实现对任意波形发生器的上位机操作。
66
基于FPGA的任意波形发生器的设计与实现
5.4.1 开发平台LabWindows/CVI
LabWindows/CVI(C for Virtual Instrument)是美国NI(National Instrument)公司开发的32位面向计算机测控领域的虚拟仪器软件开发平台,可以在多系统下运行。它以ANSIC为核心,将功能强大、使用灵活的C语言与用于数据采集控制、分析处理和显示输出的测控专用工具有机结合起来,它的集成开发环境、交互式编程方法、丰富的功能面板和库函数大大增强了C语言的功能,为熟悉C语言的开发设计人员编写检测系统、自动控制系统、数据采集系统等应用软件提供了一个理想的开发环境[35]。
5.4.2 任意波形发生器软件界面
设计的任意波形发生器软件界面如图5.3所示。
图5.3 任意波形发生器软件界面
在LabWindows/CVI 中设计的操作界面,实际上是在用户计算机屏幕上定义的一个相当于物理仪器面板的软件面板,它由各种控件(如开关、按钮、指示灯等)构成。用户面板上的这些控件运用了Windows编程的事件驱动机制,用户选
第五章 任意波形发生器的软件设计
67
中这些控件就可以产生一系列接口事件,并调用相应的回调函数来处理这些事件。 图5.3所示的任意波形发生器界面主要由4个部分组成:
(1)波形选择部分:通过旋转输出波形、调制波形和载波波形三个旋转钮可以选择任意波形发生器的输出波形种类,包括常见波形和用户自定义波形。在输出是方波的情况下,可以设置其占空比;如果用户选择的是自定义波形,则可以从文件读取波形文件或者手工绘制波形。
(2)波形控制部分:这个部分的控件比较多,包括设置波形是否调制及调制的类型、输出波形的频率、波形初始相位、输出增益、直流偏置、滤波器选择等。
(3)波形窗口部分:这部分用于显示波形(一个周期)和作为手动绘制任意波形的区域。当选择是常见波形时,波形窗口按生成的波形各点的幅度数据显示波形;当选择的是自定义波形时,按下“从文件读取”按钮可以读取波形文件并将其显示在波形窗口,便于用户了解打开的是哪类波形文件;当用户选择“手工绘制”波形时,波形窗口中会出现光标,用户通过拖动此光标可以在其中绘制任意波形。
(4)输出控制部分:通过按下“启动”按钮可以通过USB接口将波形数据下载至波形发生器硬件系统的RAM中,并讲界面设置的各项参数配置到硬件系统,然后启动输出,这时候输出指示灯会亮起;通过按下“停止”按钮,则停止任意波形发生器的输出,同时为降低系统功耗,将DAC设置为休眠状态,输出指示灯熄灭;通过按下“退出”按钮则关闭任意波形发生器硬件系统,同时退出软件界面。
任意波形发生器软件界面会对各项参数的设置进行监视,当设置值超出指定范围时,界面会弹出警告窗口,同时将参数设回默认值。同时,为了避免操作时错误点击按钮或出现错误设置情况,同时了为了使每一项工作的的相对独立性,在设计操作界面时,充分使用了“禁止—使能”机制。比如在波形输出时不允许切换波形;在选择常规波形时不允许在波形窗口绘制波形等。
5.4.3 波形数据的产生
因为波形数据最终是要存放到硬件系统的波形RAM中,因此波形数据的生成必须考虑到波形RAM的设计。在第四章波形存储器的设计中我们将波形RAM的存储深度设计为4096个单元,每个单元的字长是10bits,因此波形一个周期的采样点数为4096,同时,由于选用的DAC芯片支持直接二进制码和二进制补码两种输入格式,因此波形点的幅度量化既可以量化成0~1023之间的无符号整数,也可以量化成-512~511之间的有符号整数。设计中选择了量化成-512~511之间的有符号整数。波形数据产生的流程如图5.4所示。
68
基于FPGA的任意波形发生器的设计与实现 开始选择波形种类Y 常规波形?N 打开文件?YY生成波形幅度数据结束 结束绘制?N重新绘制N开始手工绘制 图5.4 波形数据产生流程图
其中常规波形数据的产生可以通过调用LabWindows/CVI的信号产生函数来产生,如SineWave、SquareWave、Triangle、SawtoothWave、WhiteNoise几个函数分别可以用来产生正弦波、方波、三角波、锯齿波、白噪声,这些函数的调用都非常简单,在此不做介绍,下面主要讲述一下手工绘制波形的程序设计。
用鼠标绘制波形时,将波形窗口作为绘图区,实现此功能要通过调用LabWindows/CVI中的定时控件Timer。编程时要先设定Timer的定时时间间隔,即调用定时控件回调函数的频率,其最小间隔为1ms,本设计将其设置为1ms。手工绘图过程中,可以拖动绘图区的光标进行绘图,也可以通过点击绘图区域将光标在各点切换进行绘图。与此同时,定时控件的回调函数每1ms触发一次,每执行一次该函数采集一个离散点,获取触发时刻光标出的坐标,这样就能得到一系列的离散点,相邻离散点之间的离散间隔是不等的,跟光标的移动速度有关。由于得到的只是一些离散点,还有许多波形点没有赋值,这样就要在相邻离散点之间采用插值的方法计算那些空缺点的幅值。考虑到定时控件回调函数的触发时间很短(1ms),这样相邻离散点间的间距比较小,于是可以采用线性插值法,设计结果证明,采用线性插值法计算出来的波形是比较光滑的。图5.5是手工绘制的一个任意波形。
关于线性插值法,我们设计如下:先记录连续两次调用定时控件回调函数时光标的坐标,假设前一次调用时光标位置在(x1,y1),后一次调用时光标位置在由于x1、先将他们转化为整型,设分别为int_x1、(x2,y2),x2都为双精度值,int_x2,如果int_x2?int_x1,则不进行插值,否则进行线性插值,需要插入的点数为
num?int_x2?int_x1?1,每两点之间的幅度增量为(y2?y1)/(int_x2?int_x1),最
第五章 任意波形发生器的软件设计
69
图5.5 手工绘制的任意波形
后将插值计算结果存入临时数组。下面是定时控件回调函数的部分代码: …
GetGraphCursor (panelHandle, PANEL_GRAPH, 1, &x2, &y2); //获取光标坐标 int_x2=(int)x2; //数据格式转化 int_x1=(int)x1; if (int_x2>int_x1) {
PlotLine(panelHandle,PANEL_GRAPH,x1,y1,x2,y2,VAL_BLUE); //连接两点 num= int_x2- int_x1+1; //计算插值点数 for(i=1;i temp_data[int_x1+i]=((y2-y1)/(int_x2-int_x1))*i+y1; //将数据存入临时数组 x1=x2; //保留坐标点,作为下一次函数调用使用 y1=y2; } … 无论是调用LabWindows/CVI的信号产生函数来生成波形数据,还是手工绘制产生的波形数据,最后都必须转换成整数类型,以满足数据传输和RAM存储的需要。这里有个问题需要注意:设计中选择了将波形数据量化成-512~511之间的有符号整数,使用了短整型(short int)来存储,即用了16位(2字节)来存储数据。计算机用最高的第16位来表示符号,用剩下的低15位表示数据,而DAC的字长为10位,用最高第10位来表示符号,剩下的低9位表示数据,因此是否需要考虑数据转换的问题?事实上,我们可以直接截取计算机16位数据的低10位来作为DAC的输入,整数在计算机中是以二进制补码的方式存储的,这对于非负整数不存在问题,因为非负整数的补码和原码相同,对于0~511之间的整数,用16位表示只比用10位表示在高位多了6个“0”,我们完全可以舍弃这6个“0”;