怎样检测端口

zjp 2000-09-01 05:15:00
我想问一下,在VB中怎样检测端口(串/并口)是否打开,如果某个端口已经打开,怎样“释放”对该端口的占用。非常感谢!
...全文
525 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
Un1 2000-09-06
  • 打赏
  • 举报
回复
你可以使用CreateFile去打开这些断口如果失败,判断err.lastdllerror可以得到此断口是否已经被打开。
0xFFCD 2000-09-01
  • 打赏
  • 举报
回复
VB编程如何控制I/O口
VB没有提供直接读写I/O口的方法。所以只能借助其他语言来编写DLL,然后在VB中调用。如果要在Windows 3.X下读写端口,也可以下载ftp://ftp.winsite.com/pub/pc/win31/programr/vbasic/portz10.zip,它是一个免费的VBX,可以通过读写VBX的属性来实现读写端口的操作,十分方便。
如果你在Windows下使用过C/C++,那么编写这样的DLL可能没有什么困难。在C语言里都包括inp和outp函数。可以把下面这段C语言代码(32位)编译生成DLL,然后在VB中调用。
#include
#include
/*作用:从指定端口读入一个字节
参数:portid端口号
返回值:读入的字节*/
int _stdcall Inport(short portid)
{
return inp(portid);}
/*作用:向指定端口写入一个字节
参数:portid端口号*/
void _stdcall output(short portid,short byte)
{
outp(portid,byte);
}
/*作用:从指定端口读入一个字节
参数:portid端口号
返回值:读入的字节*/
int _stdcall Inportw(short portid)
{
return inpw(portid);}
/*作用:向指定端口写入一个字节
参数:portid端口号*/
void _stdcall Outportw(short portid,short word)
{
outpw(portid,(unsigned short) word);
}
注意:这种方法只能用于Windows 95,不能用于Windows NT。


如何过滤键盘录入
在 VB 的应用得到以前就处理键盘动作, 实现对键盘的全面控制, 可过滤任意的键。 下面的例子过滤了 CTRL+C 键, 并把该键模拟为在 Command1 上单击。
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Const WH_KEYBOARD = 2
Public Const KBH_MASK = &H20000000
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Global hHook As Long
'KeyboardProc 在 VB 应用动作前发生
Public Function KeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If nCode >= 0 Then
'处理你希望过滤的键
If wParam = Asc("C") And (lParam And KBH_MASK) <> 0 Then
If (lParam And &HC0000000) = 0 Then
'模拟在Command1 中单击
Form1.Command1.SetFocus
Call PostMessage(Form1.Command1.hwnd, WM_LBUTTONDOWN, 0, &H20002)
Call PostMessage(Form1.Command1.hwnd, WM_LBUTTONUP, 0, &H20002)
KeyboardProc = 1
Exit Function
End If
End If
End If
KeyboardProc = CallNextHookEx(hHook, nCode, wParam, lParam)
End Function
 
Private Sub Form_Load()
'将 KeyboardProc 连接到中断上
hHook = SetWindowsHookEx(WH_KEYBOARD, AddressOf KeyboardProc, 0&, App.ThreadID)
End Sub
Private Sub Form_Unload(Cancel As Integer)
Call UnhookWindowsHookEx(hHook)
End Sub


打印机技巧
用 API 打开打印对话框
使用通用对话框控件当然可以打开打印对话框, 不过要浪费更多的资源和增加了一个 OCX 部件。 而用 API 会高效。
声明:
Declare Function PRINTDLG Lib "comdlg32.dll" Alias _
"PrintDlgA" (pPrintdlg As PRINTDLG) As Long
Type PRINTDLG
lStructSize As Long
hwndOwner As Long
hDevMode As Long
hDevNames As Long
hdc As Long
flags As Long
nFromPage As Integer
nToPage As Integer
nMinPage As Integer
nMaxPage As Integer
nCopies As Integer
hInstance As Long
lCustData As Long
lpfnPrintHook As Long
lpfnSetupHook As Long
lpPrintTemplateName As String
lpSetupTemplateName As String
hPrintTemplate As Long
hSetupTemplate As Long
End Type
使用:
Private Sub Command1_Click()
Dim p As PRINTDLG
p.lStructSize = Len(p)
p.hwndOwner = Me.hWnd
p.nFromPage = 1
p.nToPage = 1
p.nMinPage = 1
p.nMaxPage = 1
p.nCopies = 1
x = PRINTDLG(p)
Printer.Print Text1.Text
End Sub
在打印字符串时自动换行
感谢 shijia 的代码测试。
其中 len1 为打印的宽度, Str 为打印的文本。
Do While Len(Str) > 0
str1 = Str
Do While len1 > 0 And Printer.TextWidth(str1) > len1
str1 = Left(str1, Len(str1) - 1)
Loop
Printer.Print str1 '打印
If Len(str1) = 0 Then Exit Do '不匹配
Str = Mid(Str, Len(Str1)+1) '截断!
Loop
让打印机只打印一行
在 Win95 下,只有在使用 EndDoc 或 NewPage 时,打印机才开始打印,而且每次都要换页。使用以下的方法,可以只打印一行,并且可以把打印机的控制字符也直接发到打印机。
注意:如果打印机无汉字库,不能输出中文。
Open "PRN" For Output As #1
Print #1, "一行"


VB中DRAGDROP事件与DRAGOVER事件的使用
在WINDOWS中,拖动意味着移动光标到对象上,按住鼠标,接着移动鼠标使对象在屏幕内滑动以重新定位对象。当释放鼠标按钮时,在拖动对象所在控件边界内放开鼠标会触发DragDrop事件。如果放开鼠标时对象不在一个控件的上面,那么对象定位于窗体本身。
如果一个对象在一个控件(或窗体)上被放开,并且在后者的DragDrop事件中没有放任何内容,那么“被放开”的对象将仍然在窗体上原先位置出现,不会出现其他事情。因此,拖动对象的DragDrop事件不是用来存放有关放开对象时执行某些指令的地方。而应将这些指令放到背景的DragDrop事件中或者拖动对象所放开的目标控件中。
一个DragDrop事件提供三条信息--DragDrop(Source as Control,X as Single,Y as Single)Source,是已经放开的图片,图标或控件。X和Y值,是放开事件所在目表对象的水平和垂直方向位置。如:
Sub pictrue1-DragDrop(Source as Control,X as Single,Y as Single)
Pictrue1.DrawWidth=6
picture1.pset(x,y),QBcolor(4)
End Sub
当一个控件拖动到另一个之上时,发生一个DragOver事件,以警告被入侵的控件或窗体发生了拖动。一个DragOver事件提供四条信息:
DragOver(Source as conterol,X as Single,Y as Single,State as Integer)
“Source”为入侵者标志。入侵者在被入侵的控件或窗体内的当前位置由X,Y来确定入侵者的状态,如是否刚刚进入。仍在其内或将要离开等由State变量报告。
通过允许用户拖动一个指针在屏幕上移动,接触窗口内的不同区域引起事情发生。一种常用的编程技术是和一状态栏一同使用。状态栏随程序运行时的情况而改变,报告光标的当前位置,变量如Font等的状态,提供一种有效的帮助功能。


如何判断某一个Drive是否为光碟机?
须 调 用 Windows API 的 GetDriveType ,首先 声 明 以 下 API :
Declare Function GetDriveType Lib "kernel32" Alias _
"GetDriveTypeA" (ByVal nDrive As String) As Long
然后将以上的声明放在.bas的一般模块中,如果放在 Form 之中, 须在Declare之前再加上 Private。 然后使用以下叙述调用 :
ret = GetDriveType ( "D:\")
若传回值 ret 等于 5 , 即表示 "D:\" 为光碟机 , 至于其他传回值的意义则是 :
2:软碟, 3:硬碟, 4:Server端磁碟, 6:RAMDISK。


如何让点阵打印机每次印出一行?
VB 的 Printer 事 件 必 须 调 用 EndDoc 或 NewPage, 才 会 将 列 印的 资 料 输 出 到 打 印 机 , 但 每 印 就 是 一 页 , 我 希 望 每 输 出一 行 资 料 就 立 刻 印 在 点 阵 打 印 机 上 面 , 该 如 何 进 行 呢 ?
文 件 名 称 "PRN" 对 DOS 而 言 , 指 的 是 打 印 机 , 对 Windows 而 言 仍 然 是 适 用 的 , 因 此 先 利 用 以 下 叙 述 开 启 "PRN"(印表 机 ):
Open "PRN" For Output As #1
然 后 再 利 用 以 下 的 Print 叙 述 便 可 以 逐 行 印 出 资 料 :
Print #1, 资 料
注 : 如 果 想 输 出 中 文 , 必 须 使 用 中 文打 印 机 , 因 为 以 上 的 列 印 方 法 并 未 通 过 Windows 的 打 印 机驱 动 程 序 , 所 以 无 法 在 英 文 打 印 机 上 面 输 出 中 文 字 。


如何改变 Windows 预设的打印机?
在 VB 里面, 原本改变预设打印机的方法是:(假设安装有两种打印机(驱动程序))
Set Printer = Printers(0) ' 将预设打印机设定成第一种打印机
Set Printer = Printers(1) ' 将预设打印机设定成第二种打印机
但实际上以上叙述有时候不会成功(原因不详), 为了能够成功地改变预设打印机,以下是调用 Windows API 的方法:(补充说明: 此一解决方案适用于 Windows 95, 至于 NT 设定预设打印机的方法请叁考 98/04/05 的每周小技巧)
1. API 的声明:
Const HWND_BROADCAST = &HFFFF&
Const WM_WININICHANGE = &H1A
Private Declare Function GetProfileString Lib "kernel32" Alias "GetProfileStringA" (ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long) As Long
Private Declare Function WriteProfileString Lib "kernel32" Alias "WriteProfileStringA" (ByVal lpszSection As String, ByVal lpszKeyName As String, ByVal lpszString As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
注:如果以上的声明放在「一般模块」底下, 应在 Const 之前加上 Public 保留字, 并且将 Private 保留字去掉。
2. 程序范例:
PrinterName = "您想设定的打印机名称"
Dim S As String, length As Long, hKey As Long
S = String(80, Chr(0))
length = GetProfileString("devices", PrinterName, "", S, Len(S))
S = Left(S, length)
Call WriteProfileString("windows", "device", PrinterName & "," & S)
Call SendMessage(HWND_BROADCAST, WM_WININICHANGE, &H7FFF&, ByVal "windows")
以笔者的所安装的打印机为例, 含有 "HP LaserJet 4L" 及 "HP LaserJet 5L (Traditional)" 两种, 若想将预设打印机设定成 "HP LaserJet 4L", 则须将以上程序的「PrinterName = "您想设定的打印机名称"」改成:
PrinterName = "HP LaserJet 4L"


如何改变 NT 预设的打印机?
前面介绍的「如何改变 Windows 预设的打印机?」,只适用于 Windows, 对 NT 则没有效用, 其实在 NT 底下想要改变 VB 程序列印的打印机,只要使用:
Set Printer = Printers(0)
Set Printer = Printers(1)
但以上叙述却对 Windows 95 没有效用, 所以才会有 98/03/01 所介绍的「如何改变 Windows 预设的打印机?」。
至于改变 NT 预设打印机的方法, 则是改变登录资料库(Registry)打印机的设定,在登录资料库中纪录预设打印机的 Value 是:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows subkey 的
Device value
以笔者机器的设定为例, 将 "HP LaserJet 4L" 设定为预设打印机,则 Device = "HP LaserJet 4L,winspool,LPT1:",假设笔者想将预设打印机改成 "HP LaserJet 4M"(先决条件是安装有 "HP LaserJet 4M" 的打印机驱动程序), 则方法是把 Device 的 value 改成 "HP LaserJet 4M,winspool,LPT1:"。
至于在程序中改变登录资料库的方法, 则请叁阅 Run!PC 50 期(3 月号)「Windows 的登录资料库」。

资料来源:Visual Basic http://lovevb.2699.com
0xFFCD 2000-09-01
  • 打赏
  • 举报
回复
一、PC串行通信程序机制
常用的PC串行通信程序大多利用BIOSINT14H中断,以查询I/O方式完成异步串行通信。

Windows系统提供中断方式驱动的串行通信驱动程序COMM.DRV。通信程序无需直接对串行端口进行操作,
而是通过驱动程序这一编程接口进行间接操作。

Windows操作系统中,串行通信采用“事件通知”方式,支持数据按块传送。进行通信时,Windows开辟
一个用户定义的输入输出缓冲区,每接收一个字符就产生一个低级硬件中断,串行驱动程序立即取得控
制权,并将字符放入输入数据缓冲区,然后将控制权返还正在运行的应用程序。如果输入数据缓冲区满
了,驱动程序用当前定义的流控制机制通知发送方停止发送数据。发送数据也采用类似的处理方式,应
用程序将需要发送的数据放入输出数据缓冲区,串口每发送一个字符就产生一个低级硬件中断。

二、PC串行口远程通信的硬件配置

串行通信数据链中,计算机一端称为DTE,即数据终端设备;调制解调器一端称为DCE,即数据通信设备。
PC串行通信的硬件连接方式分为零Modem连接和Modem连接。

零Modem连接即直接用调制解调器电缆将两台设备连接起来,Modem连接则通过DCE(Modem)将两台PC连
接起来。

零Modem连接用于距离15m内两台DTE或DCE设备之间的连接。而Modem连接则通过电话网或专线用于长距
离的通信。

TXD和RXD信号线用于计算机和Modem间传输数据信息;RTS从计算机端发送信号到Modem,表示希望把数
据传给Modem,若Modem能接收这个数据,它将发送CTS信号给计算机;DTR从计算机端发送信号到Modem,
表示计算机已作好通信准备,Modem将DTR信号作为初始化自己的信号,然后给计算机回送数据设置就
绪信号DSR;当本地Modem接收到远地Modem的呼叫,则发送RING信号给计算机;Modem应答呼叫后,
发送数据载波检测DCD信号到计算机,表示已建立连接。

Modem的工作状态分为命令状态和在线状态。处于命令状态时,Modem对串口发送的信息进行解释,
可通过PC串口向Modem发送AT命令对其进行初始化、设置和操作。

例如,初始化命令为:“ATQOV1WOSOΚ1&C1&D2&YO&WO”,其含义为:

QO:返回结果码;

V1:以字符形式显示结果码;

WO:不返回协商进程结果码;

SOΚ1:振铃一次,即应答;

&C1:追踪数据载波信号CD;

&D2当DTR从开到关时,Modem挂机进入命令状态;

&YO:Modem上电时重新调出用户方案;

&WO:将以上设置存为动态设置用户方案0;

一旦本地Modem与远地Modem建立连接,Modem即处于在线状态,可进行数据传输。此时,Modem不响应AT命令,
直接传送所收到的数据。

三、Modem连接远程通信软件的编制

通信过程中大部分的工作由串行通信驱动软件承担。

本文软件的运行环境为AST+4/66d,Hayes公司的ACCURA144+FAX144Modem,Windows3.2,VB3.0专业版
。VB3.0专业版中提供了一个MSCOMM.VBX通信控件。它使用户能很方便地通过对控件属性的设置、检测,
访问串行通信驱动程序的大多数特性,而不必考虑其实现细节。该控件为“事件驱动”,只响应“OnComm”
事件,该事件可对已发生的事件或错误进行处理,与CommEvent属性紧密相关。

通信软件、通信驱动软件和Modem之间的通信关系

以下程序实现了通信的基本过程。包括初始化串口和Modem,上位机拨号呼叫下位机,下位机应答并开始
定时传输数据,挂机停止传输等功能。

VB3.0编制的通信软件清单如下:

上位机控件清单:

控件名称 属性

Form1Form1Caption ″上位机通信″

MsComm comm1

CommandButton1CallCaption ″拨号″

CommandButton2hangCaption ″挂机″

CommandButton3ExitCaption ″退出″

下位机控件清单

控件名称 属性

Form1Form1Caption ″下位机通信″

MsComm comm1

CommandButton1hangCaption ″挂机″

CommandButton2ExitCaption ″退出″

Timer1Timer1Interval 3000

Text(i)Text(i)

1.串口初始化、设置程序

在程序初始化时,先对串口初始化。因为使用RTS/CTS握手信号的硬件设备只有RTS信号置为高才
能接收数据,所以需将MSCOMM通信控件的RTSenable属性设为TRUE。由于Modem在处于命令状态时,
通过串口发送的命令以及命令结果均会回显在输入缓冲区中,因此在进入在线状态前,禁止
产生EV-RECEIVE事件,避免数据误处理。

由于Windows是多任务系统,在进行数据收发循环处理时,应调用DOEVENTS事件,将控制权交还
系统,允许Windows执行其他任务。但应注意,在处理OnComm事件时小心使用DOEVENTS,避免系
统又产生OnComm事件,造成嵌套,出现堆栈溢出错误。

Sub Form-Load()

comm1.CommportΚ1′COM1口通信

comm1.SettingsΚ″9600,N,8,1″′波特率9600,无校验,8位数据位1位停止位

comm1.OutBufferSizeΚOUTPACK′设置输出缓冲区大小

comm1.InBufferSizeΚINPACK′设置输入缓冲区大小

comm1.InputLenΚ1′每次从输入缓冲区中取一个字符

comm1.RTSenableΚ-1′RTS信号为高

comm1.RthresholdΚ0′不产生MSCOMM-EV-RECEIVE事件

comm1.PortOpenΚ-1′打开串口

cdΚcomm1.CDHolding′取载波检测信号线原始状态

comm1.outputΚ″ATQOV1WOSOΚ1&C1&D2&YOWO″′初始化Modem

Do DoEventsLoopUntilcomm1.OutBufferCountΚ0

End Sub

2.上位机拨号呼叫下位机

SubCallcom-Click()

Comm1.OutPutΚ″ATDT″&Phonnum$&chr$(13)′向Modem发拨号命令

Do DoEventsLoopUntilcomm1.OutBufferCountΚ0

End Sub

3.下位机Modem应答,连通后开始定时传输数据

下位机检测CD,判断是否连通:

Sub Comm1-OnComm()

Select Casecomm1.CommEvent

Case5 ′MSCOMM-EV-CD

Timer1.EnabledΚ-1′启动定时器

End Select

End Sub

VB传送定长字符串,数据接收处理相对简单。但在发送实时数据时,若将数据转换成定长字符串,
这样会大大降低传输效率。在接收端,通知程序从输入缓冲区中取字符有两种方法:一是用轮询方法
循环判断InBufferCount是否为0;另外就是利用Rthreshold属性触发“OnComm”事件及“CommEvent”属
性中EV-RECEIVE事件。当Rthreshold的值不为0时,允许产生EV-RECEIVE事件。该属性设置和返回在通
信控件设定CommEvent特性为接收和产生OnComm事件之前要接收的字符数。VB传送变长字符串时,接收端
的Rthreshold值要作相应的改变,以确保接收数据及时准确。

下位机定时发送字符串“strin3$ΚFormat$(Len(strin1$),″0000″)
&chr$(8)&strin2$”。strin1$为当前字符串,strin2$为上一字符串。
上位机根据接收strin3$中Len(strin1$),相应改变RthresholdΚLen(strin1$)+4。
这样,通过提前发送字符串长度,改变Rthreshold值可实现发送变长数据。

Windows串行通信驱动程序的事件驱动特性仍有不稳定的缺陷。如InputLenΚ0时,
有可能取出不完整的数据。为保证可靠,采取降低处理速率的方法,保证下位机发
送的数据流完整地进入输入缓冲区。设InputLenΚ1,即每次从缓冲区中取一个字符。
也可采用轮询方法,定时检测输入缓冲区。下位机定时发送数据:

Sub Timer1-Timer()

If comm1.CDHoldingΙΛcdThen

str1$-text1(i).Text

strin2$Κstrin1$′上一字符串

strin1$Κstr1$′当前字符串

strin3$ΚFormat$(Len(strin1$),″0000″)&chr$(8)&strin2$

′传送当前字符串长度,以及上一字符串内容

com m1.OutPutΚstrin3$

Do DoEventsLoopUntilcomm1.OutBufferCountΚ0

Else

Timer1.EnabledΚ0

End If

End Sub

上位机检测CD状态,判断是否连通、并接收处理数据。

Sub Comm1-OnComm()

Select Case comm1.CommEvent

Case5 ′MSCOMM-EV-CD检测CD状态,判断是否连通

comm1.InBufferCountΚ0′清输入缓冲区

comm1.RThresholdΚ4′当输入缓冲区中有五个字符,

Case2′MSCOMM-EV-RECEIVE产生EV-RECEIVE事件,接收处理数据

Do DoEvents

Ifcomm1.InBufferCountThen′循环从缓冲区中取数据

str1$Κstr1$+comm1.Input

Else

Exit Do

End If

Loop

len2Κ1en1′当前字符串长度

len1ΚVa1(Left$(str1$,5))′即将发送的字符串长度

comm1.RThresholdΚlen1+4

strin$ΚRight$(str1$,len2)′数据处理

……

End Select

str1$Κ″″

End Sub

4.根据设置的AT命令“&D2”,当置DTR信号为低时,Modem挂机进入命令状态。也可通过串口
输出““+++ATH”+chr$(13)”字符串挂机。

Sub Hang-click()

If comm1.CDHoldingΙΛcdThen′检测CD,判断是否挂机

comm1.DTRenableΚ0

comm1.DTRenableΚ-1′置DTR信号为高,为下一次通信准备

End If

End Sub

5.退出程序时,一定要关闭串口

Sub Exitcomm()

comm1.PortOpenΚfalse

End

End Sub

四、Windows通信机制和VB3.0通信控件潜在缺陷

在通信软件编制过程中,我们发现Windows下通信机制采用的“事件驱动”在实际运行中不稳定。
在通信过程中随机挂机的情况。这主要是由于Windows本身通信机制和通信线路介质及质量的影响。
为保证数据传输稳定可靠,可采用通信协议如XModem-CRC或YModem。在MSCOMM.VBX通信控件中,
Rthreshold属性值大小与输入缓冲区InBufferSize大小有关,当Rthreshold值较接近InBufferSize值时,
有可能不产生EV-RECEIVE事件,为保证产生该事件,可将InBufferSize值取得较大。

资料来源:Visual Basic http://lovevb.2699.com
0xFFCD 2000-09-01
  • 打赏
  • 举报
回复
利用VB开发通信程序主要的方法有两种:一是利用VB本身提供的
控件(CONTRALS);另一种方法是利用Windows API应用程序接口。Wind
ows API 主要提供了三个动态连接库( KERNEL.EXE、USER.EXE、GDI.
EXE)供开发人员调用,其中KERNEL.EXE 主要包括一些底层操作函数,
完成一些资源管理、任务、内存等操作;USER.EXE包含了一些与Windo
ws管理有关的函数,如通信、菜单、消息、光标、插入符、计时器以
及绝大多数非显示函数;GDI. EXE图形设备接口库,主要内容为与设备
输出有关的函数。和串口通信有关系的函数Buil idCommDCB、ClearC
ommBreak、SetCommBreak、FlushComm、GetCommError、GetCommSta
te、WriteComm、ReadComm、SetCommState、CloseComm等均在 \Wind
ows\system 子目录下的USER.EXE动态连接库中,在VB调用之前应该先
在全局变量定义处声明API通信函数、定义常量。
在VB的控件工具箱中,提供了一个使用非常方便的串行通信控件M
Scomm,它提供了一个事件OnComm,该事件可以截取串口的任何消息,转
入事件处理程序。OnComm事件是唯一的,OnComm可以捕获通信时发生
的串口事件和错误信息,当有串口事件或错误发生时,VB会立刻触发一
个OnComm事件,程序就会自动转入OnComm事件处理程序中。CommEvent
属性是OnComm事件的指示器,该属性在设计时不能使用,在程序运行时
为只读,CommEvent 属性存有最近的事件或错误的数值代码,可以在程
序中随时读取CommEvent 属性值来了解通信的状况,OnComm事件是和C
ommEvent属性密切相关、一起使用,当任何一个OnComm 事件或错误发
生时,都会使得CommEvent属性值改变,在OnComm事件处理过程中,可以
通过判断Comm Event属性值,对于不同的属性值转入不同的事件处理
过程,一般采用的办法是SELECT CA SE.......END SELECT。由于在无
线通信中没有使用有线Modem,CommEvent 属性涉及到的有线Modem的
属性数值代码和本项目无关。
二、无线传输接口和协议
用Intel 586/120的PC机为上位机,通信程序用VB开发,用8031单
片机作CPU、 AD574 作数据采集的下位机,上位机作数据接收和数据
处理中心站,下位机实时采集数据之后,进行简单的数据平均计算,当
收到上位机发来的发送指令之后,开始向上位机发送数据。
上位机无线通信接口使用的是一块插在ISA扩展槽中的无线Modem
ZX-02,无线Modem 与Kenwood公司的TK-378无线对讲机相连,数字信
号通过无线Modem调制成为音频信号之后,送到TK-378无线对讲机上的
MIC口。
下位机有一台外置无线Modem ZX-01,单片机的数字信号经过串口
送入无线Modem,Mo dem对信号进行调制后送入Kenwood TK-378无线对
讲机上,接收数据的方式与上述相同,由TK-378收到信号后,经无线Mod
em将音频信号解调为数字信号进入计算机或单片机处理。
由于在通信网络中,并非点对点的通信,而是一点对多点的广播式
的通信方式,因此, 我们在通信协议中增加站点识别码,给下位机编码
可以保证网络通信的有序性。
由于无线通信可能会有空间的燥声干扰,因此,采取了多项抗干扰
措施,首先是包头识别码,在发送了传输命令之后,下位机开始以打包
的形式传输数据,每一包都有一个包头和包尾识别码,假如识别码有误
,这一次的传输为不正常数据处理。打包发送另外一个原因是TK-270
对讲机连续发送数据的时间不能超过一分钟,超过一分钟就会自动中
断发送,因此,当数据较多时不打包连续发送的时间就会超过一分钟,
发送数据中断。
在下位机中有32KB的NVRAM,可以保存32KB的数据。该数据可以由
上位机发送清除命令的方法清除掉,当32KB满了以后,最早放入的数据
就会丢掉,由于采样速率不高,在慢采的情况下,两天的时间才能存满,
这样不论上位机或下位机出现断电、死机等问题,数据不会丢失掉。
由于使用的无线Modem所限,传输速率只能达到2400bps,而在我们
的使用中1200bps、N Parity、8 DATA、1 STOP为较稳定状态。
上位机向下位机发送的命令有校时命令、请求内存命令、发送数
据命令、快采命令和慢采命令等。发送命令有两种发送方式,即手动
方式和自动方式,自动方式是由定时器来完成的。
三、 应用实例
由于该项目的软件源代码较长,我们只拿出和串口通信有关的程
序片段来供大家参考。在我们的工作中实践了三种通信方式,即查询
方式、事件驱动方式、事件驱动转查询方式。这三种方式各有利弊,
查询方式方便可靠,可利用协议或设定时钟进入和退出查询状态,但不
是资源的有效利用方式;事件触发方式对于定长通信非常有效,但定长
通信在有些场合不实用;事件驱动转查询方式既有事件驱动的特点又
有转查询方式特点,可以说是集二者之长,有效利用资源。下面着重介
绍事件驱动转查询方式。
首先在公共模块中定义和ONCOMM有关的参数:
Global Const MSCOMM_EV_RECEIVE = 2‘收到 Rthreshold 个字
符。该事件将持续产生直到用 Input 属性从接收缓冲区中删除数据

Global Const MSCOMM_ER_RXOVER = 1008‘接受缓冲区溢出,接
收缓冲区没有空间。
Global Const MSCOMM_ER_TXFULL = 1010 ‘传输缓冲区已满。
传输字符时传输缓冲区已满。
在启动过程中对串口和输入输出缓冲区初始化:
Sub Form_Load ()
comm1.Settings = "1200,n,8,1" 设定波特率1200bps,无校验,8
位数据位,1位停止位
comm1.CommPort = 1 串口1
comm1.InputLen = 1 一次从输入缓冲区中读取一个字符
comm1.InBufferSize = 512 定义输入缓冲区为512字节(bytes)
comm1.InBufferCount = 0 清空输入缓冲区
comm1.OutBufferCount = 0 清空输出缓冲区
comm1.PortOpen = True 启动串口
End Sub
下面是发送数传命令的子过程,启动该过程由一个定时器控制:
Sub sample_data ()
comm1.RTSEnable = True 将Modem的PTT置高,同时打开对讲机
Call time_delay 适当延时
comm1.Output = "*TRNS" + Chr$(13) 发送命令
Do
该循环用来检测命令是否全部发送完毕
Loop Until comm1.OutBufferCount = 0
Call time_delay
适当延时
comm1.RTSEnable = False 将Modem的PTT置低,将对讲机改为接
受状态
comm1.InBufferCount = 0 清空接收缓冲区
comm1.Rthreshold = 1 设定Rthreshold = 1,等待出发OnComm事

End Sub
在OnComm编写接收和处理代码:
Sub Comm1_OnComm ()
Select Case comm1.CommEvent
Case MSCOMM_ER_RXOVER 接收缓冲区溢出,可插入相应的代码
Case MSCOMM_ER_TXFULL 传输缓冲区已满,可插入处理代码
Case MSCOMM_ER_RECEIVE 收到1个字符,可进入以下处理过程
comm1.Rthreshold = 0 不再响应OnComm事件,转入查寻方式接收
ii = 0
iii = 0: sinn = Chr$(42): sinn1 = Chr$(42)初始化变量
Do
duration = Timer + .2 设定超时退出的时间值
iii = iii + 1
sinn1 = sinn
Do 该循环判断输入缓冲区是否有数据或是否超时
'dummy% = DoEvents()
Loop Until comm1.InBufferCount >= 1 Or Timer >= duration
If Timer >= duration Then
iii = iii - 1
overtime = True 确定超时退出,并非正常退出
Exit Do
End If
sinn = comm1.Input 正常退出,取值付给sinn
If header_er = 0 Then
GoTo test_header
End If
If Asc(sinn) > 175 Or Asc(sinn) < 160 Then 判断包头是否
正确
iii = 0
GoTo station_number_er 包头不正确,丢掉该数据
End If
test_header:
header_er = 0 包头正确,接受到包头,header_er=0
ssin(iii) = sinn 'put date into string dimantion
station_number = Asc(ssin(1))
package_number = Asc(ssin(2))
sentence_number = Asc(ssin(3))
last_character = Asc(ssin(iii))
station_number_er: Loop Until sinn1 = Chr$(10) and
sinn = Chr$(13) 当收到正确的包尾0A,0D后退出
If overtime = True Then 如果超时非正常退出
overtime = False
comm1.Rthreshold = 1 重新启动OnComm事件,等待接收下一包
timer6_wait.Enabled = True
GoTo endsub
End If
以下是接收数据正确以后的数据处理程序,包括解码和计算。
If package_number = 1 Then 如果接收数据正确,而且是最后一

timer2_sample.Enabled = True 启动控制数传命令定时器
comm1.RTSEnable = True 置PTT为高
comm1.OutBufferCount = 0 清空输出缓冲区
Call time_delay
适当延时
comm1.Output = "*MACK" + Chr$(13)发送清内存命令
Do
检测是否发送完毕
Loop Until comm1.OutBufferCount = 0
Call time_delay 适当延时
comm1.RTSEnable = False 置PTT为低
End If
If package_number > 1 Then 如果不是最后一包
comm1.Rthreshold = 1
启动OnComm准备接收下一包
timer6_wait.Enabled = True
End If
endsub: header_er = 1: comm1.InBufferCount = 0
'empty inbuffer protect from bed data on the inbuffer
End Select
End Sub

7,759

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧