第7章 波形发生器
对于大学自动化和电子信息等专业的学生,肯定会在高等数学、信号处理、电力电子技术等课程中学到很多信号波形,经常涉及的信号波形有正弦波、方波、锯齿波等。在信号处理中,经常会涉及正弦与余弦的叠加或乘积,很多学生对于这些处理后的波形究竟什么样很难绘制,工科毕业的读者对此也深有感触。本章就使用VB来设计一个波形发生器,来绘制各种基本波形和复杂波形。
7.1 需求分析
通常,波形发生器程序只涉及正弦波、方波、锯齿波3种基本波形,而且不能设置相位。本章设计的波形发生器包括以下功能。
(1)可以绘制方波和锯齿波。
(2)可以绘制正弦波,并且能够设置波形的幅值、频率和相位。
(3)可以绘制正弦波与余弦波的叠加,并能设置两种波形的幅值、频率和相位。
(4)可以绘制正弦波与余弦波的乘积,并能设置两种波形的幅值、频率和相位。
(5)可以绘制二极管整流波形,并且能够设置波形的幅值、频率和相位。
(6)可以绘制网格以显示波形曲线的值。
(7)可以设置波形曲线的颜色和线宽。
(8)可以在状态栏显示鼠标在绘图区中的坐标位置。图7-1为波形发生器界面。
图7-1 波形发生器界面
7.2 技术要点
波形发生器的波形绘制区域使用Rectangle函数确定绘图区域在窗体上的位置和尺寸大小,使用FillRect函数填充用于绘图的矩形区域。
在绘制网格线时,使用CreatePen函数设置画笔为虚线。根据CheckBox控件的Value属性,来确定是否需要绘制网格线。
本章设计的波形发生器可以绘制方波和锯齿波,正弦波和二极管整流波形及其波形族,叠加波形和乘积波形及其波形族。因此,该波形发生器可以绘制各种曲线及其组合曲线。这些曲线可以通过幅值、频率和相位的设置获得。这些波形的绘制主要通过绘制直线或使用直线逼近曲线来实现的。
方波和锯齿波在绘制的时候具有特殊性,即该曲线的主要由直线组成。因此,绘制该曲线前,确定曲线中所有直线的端点即可,而直线部分不必再细分。
正弦波及其衍生波形是光滑的曲线。所以,该曲线的绘制思路与方波和锯齿波不同。绘制正弦波及其衍生波形时,需要使用足够多的直线来逼近曲线,否则曲线可能会产生失真。本章使用10000条直线来绘制该曲线。
绘制直线可以使用Line方法或Line控件数组实现。但是,如果使用Line方法来绘制10000多条直线,其绘制效率很低,程序会存在短暂停滞,使用Line控件数组也不可行。因此,本章使用Polyline函数绘制曲线,使用该函数前,只要确定各端点的坐标即可,最重要的是使用该函数绘制曲线的效率极高。
此外,绘图区中纵坐标数值等的显示,都是使用TextOut函数输出的。该函数的使用比较简单,只要确定输出文本的位置即可实现。
7.3 系统结构
创建一个新的工程,添加1个Form窗体和1个Modele模块,根据图7-1所示的波形发生器界面向Form窗体上添加各控件。工程的对象及其属性值如表7-1所示。
表7-1 工程的对象及其属性值
在窗体中还使用到了Label控件数组,只用于显示各文本框的含义,而在程序中并没有涉及其他应用。因此,本章并没有将其列在表7-1中,读者可以根据图7-1对其Caption属性进行相应设置即可。
7.4 实现过程
根据波形发生器程序的功能,可以将该程序分为变量声明和程序初始化、绘图区域设置、绘制方波、绘制锯齿波、绘制正弦波、绘制叠加波形、获取坐标、选择波形曲线等部分。其余程序代码相对比较简单,或者程序设计思路比较相似的程序代码请参考光盘中的源代码。
7.4.1 变量声明和程序初始化
本章设计的波形发生器可以绘制正弦等波形,因此需要声明一些频率、幅值和相位等变量。此外,还需要声明一些API函数实现曲线的绘制等,这些API函数及其相关变量的声明参考光盘中的源程序。程序代码如程序清单7-1所示。
程序清单7-1变量声明和程序初始化
1. Private intWaveFlag As Integer 2. Private intFrequency1 As Long 3. Private intFrequency2 As Long
4. Private sngRange1 As Single 5. Private sngRange2 As Single 6. Private sngPhase1 As Single 7. Private sngPhase2 As Single 8. 9. Private intLineWidth As Long 10. Private lngWaveColor As Long 11. 12. Private Sub Form_Load() 13. SetWindowPos Me.hwnd,-1,0,0,0,0,SWP_NOMOVE Or SWP_NOSIZE 14. optWaveType.Item(2).Value=True 15. stbStatus.Panels.Add 16. stbStatus.Panels(1).AutoSize=sbrSpring 17. txtFrequency.Item(0).Text=10 18. txtRange.Item(0).Text=1 19. txtLineWidth.Text=1 20. End Sub
程序说明:第1行声明波形类型的变量,用于记录当前的波形类型,本章设计的波形发生器涉及到两个正弦和余弦的叠加或乘积。因此,第2~7行分别声明两组频率、幅值和相位变量,其中,第一组的频率和幅值变量就默认为是方波和锯齿波的频率和幅值。第9行声明波形曲线的线宽变量,第10行声明波形曲线的颜色变量,第12~20行在程序加载的时候初始化变量和控件,第12行使用SetWindowPos函数将波形发生器窗口始终置前。
7.4.2 绘图区域设置
在绘制波形曲线之前,需要确定绘图区域,包括绘图区的尺寸大小、输出坐标值和绘制网格线等,绘图区域设置程序代码放置在mOdule模块中。程序代码如程序清单7-2所示。
程序清单7-2绘图区域设置
1. Public Function DrawSet() 2. Dim i As Long 3. Dim hdc As Long 4. Dim Piont As POINTAPI 5. Dim rct As RECT 6. Dim lngPen As Long 7. 8. With rct 9. .Left=40 10. .Top=20 11. .Right=ClientWidth+40 12. .Bottom=ClientHeight+20 13. End With 14. 15. For i=5 To-5 Step-1 16. TextOut frmMain.hdc,25,(ClientHeight+30)/2-i*40,Str(i),2 17. Next 18. TextOut frmMain.hdc,40,5,"单位:V",6
19. 20. hdc=GetDC(frmMain.hwnd) 21. Rectangle hdc,40,20,ClientWidth+40,ClientHeight+20 22. FillRect hdc,rct,5 23. MoveToEx hdc,40,ClientHeight/2+20,Piont 24. LineTo hdc,ClientWidth+40,ClientHeight/2+20 25. 26. lngPen=CreatePen(PS_DOT,1,RGB(20,20,20)) 27. SelectObject frmMain.hdc,lngPen 28. If frmMain.chkDrawGrid.Value=1 Then 29. For i=20 To ClientHeight+20 Step 20 30. MoveToEx hdc,40,i,Piont 31. LineTo hdc,ClientWidth+40,i 32. Next 33. 34. For i=40 To ClientWidth+40 Step 20 35. MoveToEx hdc,i,20,Piont 36. LineTo hdc,i,ClientHeight+20 37. Next 38. End If 39. End Function
程序说明:第8~13行确定绘图区域的位置和尺寸大小,第15~18行输出纵坐标的数值和单位,第20~23行在窗体上绘制出上述确定的矩形绘图区域,第24行绘制横坐标轴,第26~27行设置画笔类型为虚线,用于绘制网格线,第29~32行绘制横向网格。第34~37行绘制纵向网格。
7.4.3 绘制方波
方波是最基本的波形之一。方波的一个周期可以用4个点将其分为3段,分别确定各个周期的4 个点即可确定整条方波曲线。方波程序代码放置在mOdule模块中,其余波形绘制代码与方波相同。程序代码如程序清单7-3所示。
程序清单7-3绘制方波
1. Public Function SquareWave(ByVal Frequrncy As Long,ByVal Range As Single,_ 2. ByVal Width As Long,ByVal Color As Long) 3. Dim i As Long 4. Dim sngHalfCycle As Long 5. Dim lngRativeRange As Long 6. 7. lngHalfCycle=ClientWidth/(2*Frequrncy) 8. lngRativeRange=Range*ClientHeight/10 9. 10. For i=0 To(4*Frequrncy-4)Step 4 11. Points(i).X=40+i*ClientWidth/(4*Frequrncy) 12. Points(i).Y=ClientHeight/2+20-lngRativeRange 13. Points(i+1).X=Points(i).X+lngHalfCycle
14. Points(i+1).Y=ClientHeight/2+20-lngRativeRange 15. Points(i+2).X=Points(i).X+lngHalfCycle 16. Points(i+2).Y=ClientHeight/2+20+lngRativeRange 17. Points(i+3).X=Points(i+2).X+lngHalfCycle 18. Points(i+3).Y=ClientHeight/2+20+lngRativeRange 19. Next i 20. 21. LineSet Width,Color 22. Polyline frmMain.hdc,Points(0),4*Frequrncy 23. SelectObject frmMain.hdc,lngDefaltPen 24. TextOut frmMain.hdc,ClientWidth+45,ClientHeight+10,_ 25. "频率="&Frequrncy&"Hz",10 26. End Function
程序说明:第7行将整个横轴分为两倍频率等分,第8行计算单位幅值在坐标系统中的相对高度,第10~19行将每个周期分为4等分进行分别赋值,第21行设置波形曲线的线宽和颜色,第22行绘制方波曲线,第24行在横轴最右端输出频率值。
7.4.4 绘制锯齿波
锯齿波的绘制思路与方波的绘制相似,也是分段确定曲线各端点的坐标值。程序代码如程序清单7-4所示。
程序清单7-4绘制锯齿波
1. Public Sub SawToothWave(ByVal Frequrncy As Long,ByVal Range As Single,_ 2. ByVal Width As Long,ByVal Color As Long) 3. Dim i As Long 4. Dim sngHalfCycle As Long 5. Dim lngRativeRange As Long 6. 7. sngHalfCycle=ClientWidth/(4*Frequrncy) 8. lngRativeRange=Range*ClientHeight/10 9. 10. For i=0 To 4*Frequrncy 12. Points(i).X=i*sngHalfCycle+40 13. If i Mod 4=1 Then 14. Points(i).Y=ClientHeight/2+20-lngRativeRange 15. End If 16. If i Mod 4=3 Then 17. Points(i).Y=ClientHeight/2+20+lngRativeRange 18. End If 19. If i Mod 2=0 Then 20. Points(i).Y=ClientHeight/2+20 21. End If 22. Next 23. 24. LineSet Width,Color
25. Polyline frmMain.hdc,Points(0),4*Frequrncy+1 26. SelectObject frmMain.hdc,lngDefaltPen 27. TextOut frmMain.hdc,ClientWidth+45,ClientHeight+10,_ 28. "频率="&Frequrncy&"Hz",10 29. End Sub
程序说明:第10~22行使用循环语句给各端点赋值,第14行为锯齿波负半周的端点值。第17行为锯齿波正半周的端点值,第20行为锯齿波在横轴上的端点值,第24~28行绘制曲线并输出相应频率。
7.4.5 绘制正弦波
绘制正弦波需要确定其频率、幅值、相位等参数,根据相位设置的不同可以获得包括余弦波形在内的一组波形曲线。正弦波曲线的绘制思路与方波和锯齿波都不同,方波和锯齿波主要是由直线构成,所以可以分段给端点赋值,而正弦波只能将整条曲线分得足够小才能光滑地将其绘制出来。程序代码如程序清单7-5所示。
程序清单7-5绘制正弦波
1. Public Function SinWave(ByVal Frequrncy As Long,ByVal Range As Single,_ 2. ByVal sngPhase As Single,ByVal lngPoints As Long,_ 3. ByVal Width As Long,ByVal Color As Long) 4. Dim i As Long 5. 6. For i=0 To lngPoints 7. Points(i).X=i*ClientWidth/lngPoints+40 8. Points(i).Y=ClientHeight-Range*ClientHeight*_ 9. (Sin(2*pi*i*Frequrncy/lngPoints-sngPhase))_ 10. /10+20-ClientHeight/2 11. Next 12. 13. LineSet Width,Color 14. Polyline frmMain.hdc,Points(0),lngPoints+1 15. DeleteObject lngLinePen 16. SelectObject frmMain.hdc,lngDefaltPen 17. TextOut frmMain.hdc,ClientWidth+45,ClientHeight+10,_ 18. "频率="&Frequrncy&"Hz",10 19. End Function
程序说明:第6~11行将正弦波曲线分成10000点,并使用循环语句给各点赋值。第13~18行绘制曲线并输出相应频率。
7.4.6 绘制叠加波形
叠加波形的绘制和正弦波绘制的方法一样,也是将整条曲线分成10000段,再将其光滑地绘制出来。程序代码如程序清单7-6所示。
程序清单7-6绘制叠加波形
1. Public Function SinAddWave(ByVal Frequrncy1 As Long,ByVal Range1 As Single,_ 2. ByVal sngPhase1 As Single,ByVal Frequrncy2 As Long,_ 3. ByVal Range2 As Single,ByVal sngPhase2 As Single,_ 4. ByVal lngPoints As Long,ByVal Width As Long,_ 5. ByVal Color As Long) 6. Dim i As Long 7. 8. For i=0 To lngPoints 9. Points(i).X=i*ClientWidth/lngPoints+40 10. Points(i).Y=ClientHeight-ClientHeight*(Range1*_ 11. (Sin(2*pi*i*Frequrncy1/lngPoints-sngPhase1))+_ 12. Range2*(Cos(2*pi*i*Frequrncy2/lngPoints-sngPhase2)))_ 13. /10+20-ClientHeight/2 14. Next 15. 16. LineSet Width,Color 17. Polyline frmMain.hdc,Points(0),lngPoints+1 18. DeleteObject lngLinePen 19. SelectObject frmMain.hdc,lngDefaltPen 20. End Function
程序说明:第1~5行为叠加波形函数的参数。叠加波形由正弦和余弦波形组成,因此有两组频率、幅值、相位参数。第10~13行是计算每个点的纵坐标值,只要将两个函数值相加即可。第16~19行绘制叠加波形曲线。
7.4.7 获取坐标
本章设计的波形发生器可以使用鼠标获取坐标系统中任意点的坐标值,因此也可以获取曲线上的各坐标值。程序代码如程序清单7-7所示。
程序清单7-7获取坐标
1. Private Sub Form_MouseMove(Button As Integer,Shift As Integer,_ 2. X As Single,Y As Single) 3. Dim Point As POINTAPI 4. Dim sngX As Single 5. Dim sngY As Single 6. 7. GetCursorPos Point 8. ScreenToClient frmMain.hwnd,Point 9. sngX=Point.X-40 10. sngY=220-Point.Y 11. 12. If sngX<0 Or sngX>600 Or sngY>200 Or sngY<-200 Then 13. stbStatus.Panels(1).Text="无法取得当前坐标" 14. Exit Sub
15. End If 16. 17. sngX=sngX/600*intFrequency1 18. sngY=sngY/400 19. stbStatus.Panels(1).Text="当前坐标:x="&sngX&" y="&sngY 20. End Sub
程序说明:本段程序在窗体的MouseMove事件中实现鼠标获取任意点的坐标值。第7~8行使用GetCursorPos函数获取鼠标在窗体坐标系统中的坐标值。第12~15行判断鼠标的当前坐标是否在绘图区域,如果不在就不显示当前的坐标值。第17~18行将鼠标的协调坐标值转换为真实的曲线坐标系统的坐标值。第19行在状态栏将鼠标当前的坐标值显示出来。
7.4.8 选择波形曲线
本章设计的波形发生器可以绘制多种波形曲线,这些波形曲线可以通过OptionButton控件进行选择。程序代码如程序清单7-8所示。
程序清单7-8选择波形曲线
1. Private Sub optWaveType_Click(Index As Integer) 2. If InputValid=False Then 3. Exit Sub 4. End If 5. 6. Me.Cls 7. DrawSet 8. 9. Select Case Index 10. Case 0 11. intWaveFlag=1 12. SquareWave intFrequency1,sngRange1,intLineWidth,lngWaveColor 13. Case 1 14. intWaveFlag=2 15. SawToothWave intFrequency1,sngRange1,intLineWidth,lngWaveColor 16. Case 2 17. intWaveFlag=3 18. SinWave intFrequency1,sngRange1,sngPhase1,10000,_ 19. intLineWidth,lngWaveColor 20. Case 3 21. intWaveFlag=4 22. SinAddWave intFrequency1,sngRange1,sngPhase1,_ 23. intFrequency2,sngRange2,sngPhase2,_ 24. 10000,intLineWidth,lngWaveColor 25. Case 4 26. intWaveFlag=5 27. SinMultiplyWave intFrequency1,sngRange1,sngPhase1,_ 28. intFrequency2,sngRange2,sngPhase2,_ 29. 10000,intLineWidth,lngWaveColor
30. Case 5 31. intWaveFlag=6 32. CommutateWave intFrequency1,sngRange1,sngPhase1,10000,_ 33. intLineWidth,lngWaveColor 34. End Select 35. End Sub
程序说明:本段程序使用OptionButton控件数组进行波形曲线的选择。第2~4 行使用InputValid函数判断输入参数的有效性,InputValid函数的代码可以参考光盘中的源代码。第6行清除绘图区域准备绘制新的波形曲线,第7行调用DrawSet过程进行绘图区域设置,第9~34行使用Select选择语句绘制各种波形曲线。