一种调用Win95 API实现串行通信的查询方法

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 showman 的 blog

一种调用win95 api实现串行通信的查询方法

摘要 在win95中,串行通信的机制相对windows 3.x已作了改进和标准化。本文结合一个实例从调用api接口的角度,具体阐述了一种用简单的串口查询法来实现串行通信的思路。

关键词 api、串行通信、visual basic、多线程、win95

visual basic是一个windows系统下流行的应用程序开发平台。其方便的界面设计使程序员能节省大量时间,并把最大的精力集中在应用程序核心代码的编写上。vb4目前已成为win95系统下一种最高效灵活的主流开发工具之一,而调用api应用程序接口则正是对vb4功能的强有力扩充,它使得vb4能够直接控制和处理pc硬件资源,而不必依赖于ms-dos系统服务。可以说,正是借助于api使得vb即能隐藏大部分windows编程的难点,同时又提供了使用windows环境的灵活性。

因而在串行通信中使用调用api接口的方法是实现在vb中直接控制串行口硬件的简便可行的解决方案。

一、关于win95的多线程及32位api的描述

微软于95年推出的win95操作系统是对其旧版本windows性能的一次全面提升。它率先提出并实现了应用程序多线程的编程思想,这是win95系统区别于其他系统最显著和最重要的特征之一。进程是应用程序的执行实例,而线程则是进程内部执行的路径,从根本上说,线程是可由系统调度的一个最简单的代码单元,每个线程有自己的一组cpu寄存器和堆栈。win95为每个独立的线程安排一些cpu时间,操作系统以轮转方式向线程提供时间片,这样每个线程从外观上看就象都在同时运行一样。一个多线程的应用程序实际上在其内部实现了多任务扩展,为代码赋予了并行执行的特性。

多线程应用程序的主要优势就是可以用尽量少的时间对用户的要求作出响应。多线程并不代表代码会运行得更快,准确地说,它仅仅意味着应用程序可以更好地利用系统资源(调度机制、内存和物理设备)。从而能建立一个非常灵活的应用程序,它能随时接收用户输入,并得到满意的效果。

因此为了在32位的win95系统中建立更快速、可靠和健壮的代码,多线程技术是必须掌握的关键技术之一。

api(即应用编程接口)是附带在win95内部的一个极其重要的组成部分。win95的32位api主要是一系列很复杂的函数、消息的集合。它可以看作是win95系统为在其下运行的各种开发系统提供的开放式通用功能增强接口。它使编程人员可以更容易地用不同类型的语言编制出运行在win95系统上的应用程序。

与windows 3.x的16位api相比,附带在win95中的win32 api是api质量最显著的一次提升。它正逐渐成为应用广泛的一种目标平台。win32 api包括了1000多个api调用,加上api附带的几百种windows常量、消息和数据类型结构,使用户拥有了一系列用于win95编程的复杂的而又有效的工具。

api接口是对vb功能的最重要的扩充。几乎所有用vb的标准功能不能处理的问题都可以用调用api接口函数的方法来解决。api接口调用不仅给vb提供了大量可扩充的功能强劲的函数。更为重要的是,它提供了一种最切实地扩充vb功能的思路和方法。尤其在一些直接和硬件交互的低级操作中,调用api接口函数往往是唯一的可行办法。

总之,调用api接口并结合多线程的编程技术已成了win95环境下最流行的一种高级技术。大多数应用程序的代码都不得不涉及到这个高级专题。

二、win95串行通信的工作机理

常用的dos系统主要是工作在响应中断方式。pc机串行通信程序大多利用其bios块的int14h中断,以查询串口的方式完成异步串行通信。

与dos响应中断的工作方式不同,windows是一个事件驱动的,并与设备无关的多用户操作系统。同时windows禁止应用程序直接和硬件交互,程序员只能通过windows提供的各类驱动程序来管理硬件。在这种情况下,windows系统充当了应用程序与硬件之间的中介。

windows系统函数即包含了通信支持中断功能。win95系统为每个通信设备开辟了用户定义的输入输出缓冲区(即读/写缓冲区),数据进出通信口均由系统后台来完成。应用程序只需完成对输入输出缓冲区操作就可以了。实际过程是每接收一个字符就产生一个低级硬件中断,win95系统中的串行驱动程序就取得了控制权,并将接收到的字符放入输入数据缓冲区。然后将控制权返还正在运行的应用程序。如果输入缓冲区数据已满,串行驱动程序用当前定义的流控制机制通知发送方停止发送数据。队列中的数据按“先进先出”的次序处理。

windows 3.x提供了comm.drv驱动程序以支持串行通信,而win95已对这些串行通信的标准函数作了改进和标准化,并把对串口的支持与基本输入输出驱动程序集成为一体。win95像对文件操作一样来实现对串口的操作,两者使用相同的打开、关闭及读写函数。

以下是相应的串行通信函数在16位和32位api中的比较:

apitable.jpg (39884 bytes)

16位的api只提供了16个与通信相关的函数。与这些16位的通信函数相比,win95提供的通信函数更为丰富,功能更为强大。

在win95系统中,串行口和串行通信驱动程序是通过一个数据结构进行配置的,这个数据结构被称为设备控制块( device control block ),简称dcb。win95的dcb比windows 3.x的dcb更复杂,对数据结构的定义更完善。

下面是win95中设备控制块数据结构的定义:

type dcb ’在win95 api中有详细定义,可从vb4的api text viewer中得到
dcblength as long
baudrate as long
fbinary as long
fparity as long
……
evtchar as byte
end type
win95为获取通讯端口的状态提供了重要的getcommstate函数。函数把端口的配置信息装入一个设备控制块dcb,从而获得端口的配置情况。

另一个重要的comstat结构则通常被用来描述串口当前的状态。其定义如下:

type comstat ’在win95 api中有详细定义,可从vb4的api text viewer中得到
fctshold as long
fdsrhold as long
……
cbinque as long
cboutque as long
end type
通常可按以下四步实现串行通信:

(1) 按协议的设置初始化并打开串行口,这样做就是通知windows本应用程序需要这个串口,
并封锁其他应用程序使它们不能使用此串口。
(2) 配置这个串行口。
(3) 在串口上往返地传输数据,并在传输过程中进行校验。
(4) 不需要此串口时,关闭串口。即释放串口以供其它应用程序使用。
在这四个步骤中,主要的程序代码集中在第(3)步。

三、vb串口查询法的实现方法

(1)串口查询法的机理

vb的串口查询法是一种主要工作在查询方式下的实现方法。当通信程序工作在“查询”方式时,可以不考虑win95的进程和线程的问题。仅在串口有数据时,去读串口缓冲区就可以了。这种方法下确定串口读取的时机、握手协议及软件纠错的实现是程序员应考虑的主要问题。

以下是读取接收缓冲区数据时的流程图:

api1.jpg (19036 bytes)

由于这种方法主要工作在查询方式。程序员必须完成相当一部分通信状态的检测工作,许多细节(甚至包括通信过程中的字符属性的转换)也必须通过程序代码完成。因而相比较通信控件方法而言,这种查询方法对通信双方协议的依赖性尤其大。双方通信协议的约定对程序实现的难易程度影响很大。

串口查询法中,一般串口初始化的流程如下:

api2.jpg (25658 bytes)

值得注意的一点是,此方法下协议的约定必须满足以下条件:即甲方发送时,乙方必须在甲方发送动作之前进入循环接收状态,直到接收到字符后通过对串口读取函数readfile返回值的判断跳出循环状态。

同时,一般为了不使系统因循环等待接收而进入“死循环”状态,可以人为设置读取串口的循环次数,一般1000~10000次即可。

vb4是一种极为灵活的高级语言,因而在这种方法下可以方便地引入汇编语言的思维,利用其goto转向语句方便地控制程序的流程。非常灵活方便。

(2)与通信线程法的比较

通信线程法通过在应用程序的主线程外再创建一个通信线程来监视通信设备,其最大优点是程序对接收数据具有自主觉察能力。一旦通信线程查询到数据已发送到串口上,线程自动向应用程序发送一个数据接收到的消息,应用程序可用该消息来读取通信设备传来的数据。并且使用通信线程还不占用cpu时间。这样系统实际上具有了同时控制多个通信设备(如modem)的能力。

通信线程法在c++的开发系统中用得较多,这与c++的特点和优势密切相关。

由于vb自身的特点,创建通信线程的方法对vb而言,实现上有相当的难度。而且这样做,也无异于牺牲vb的简单方便性而去迁就程序的复杂性,很不经济。串口查询法容易地引入了汇编语言的一些思维,实现了最精简的程序代码。它不一定是最完善,但对一般vb程序员来说,这种方法确实达到了简化思路和降低难度的目的。因而实现简单。

四、串口查询法的程序实例

现约定甲方是pc机,乙方是单片机系统(如电卡电度表系统的读卡器)。通信格式设置为2400波特率,8位数据位,1位停止位,无奇偶校验。

下面是一个约定好通讯协议的程序实例,协议如下面流程图所示:

api3.jpg (34932 bytes)

以下是甲方(pc机)的几个子函数的程序实例。
private function opentheport(cport as string,cbaud as string,cparity as string,cdata as string,tstops as string)as boolean
dim lresult as long
dim lhandle as long
dim dcb_comm as dcb
dim cdcbconfig as string
lhandle = createfile(cport,generic_read or generic_write,0&,0&,open_existing,0&,0&)
if lhandle = -1 then ’打开串口失败
opentheport = false
msgbox “串口可能正被其他应用程序占用!”
lresult = closehandle(lhandle) ’先关闭串口后再打开
if lresult = 0 then
opentheport
exit function
end if
end if
cdcbconfig.band = 2400 ’设置dcb
cdcbconfig.parity = none
cdcbconfig.data = 8
cdcbconfig.stop = 1
lresult = buildcommdcb(cdcbconfig,dcb_comm) ’按用户设定配置一个dcb结构
if lresult = 0 then
opentheport = false
msgbox “无法建立dcb设备控制块”
exit function
end if
lresult = setcommstate(lhandle,dcb_comm) ’实际设置一个串口的dcb
if lresult = 0 then
opentheport = false
msgbox “无法建立dcb设备控制块”
exit function
end if
opentheport = true
end function

private sub sendhand ( ) ’发送握手信号的子过程
dim nchars as long
static readbuff as string * 1
static writebuff as string * 1
dim lpdcb as dcb
dim lret as long
dim lhandle as long
dim lpoverlapped as overlapped
dim rnum as integer

msgbox “请把电卡读卡器插在串口2上!”,48,“提示窗口”
lhandle = opentheport(comm1,2400,none,8,1)
lret = purgecomm( lhandle,1 ) ’清输出缓冲区
lret = purgecomm( lhandle,0 ) ’清输入缓冲区
lret = getcommstate ( lhandle,lpdcb ) ’获得通讯口的状态
shand:
writebuff$ = chr$(&h8f)
lret = writefile ( lhandle,writebuff$,1,nchars,lpoverlapped ) ’送握手信号入串口缓冲区
if lret <= 0 then
msgbox “发送操作出错,电卡握手信号未发送成功”, 16
goto shand ’不成功则重发
else
goto qtest
end if
goto shand
qtest:
readbuff$ =“ ” ’清除缓冲区为空
do while lhandle ’循环查询串口
rnum = 0 ’设置读串口次数的指针为0
readagain:
lret = readfile( lhandle,readbuff$,1,nchars,lpoverlapped )
if lret < 0 then
msgbox “读取应答信号时出错”, 16
end if
if lret = 0 then
if rnum > 1000 then ’只读1000次串口,以免陷入死循环
msgbox "电卡没有插接好或电卡没有接在串口上!"
goto closep
end if
rnum = rnum + 1
goto readagain
end if
if hex$(asc(readbuff)) <> hex$(&hff) then
goto shand ’回送码不正确则返回继续发送握手信号
else
label1.caption = “握手信号是:”+hex$(asc(readbuff$))
msgbox “握手信号正确,已正确联机”
goto closep
end if
loop
closep: lret = closehandle( lhandle )
if lret = 0 then
msgbox “串行通讯口关闭成功”,48,“提示窗口”
end if
end sub

这里,要注意的是:当pc机与单片机系统通信时,单片机数据存储区( ram )内的数据是十六进制,在信号线上传输的是十六进制数的ascii码的二进制形式;而windows系统下使用的是ansi码,ansi码仅在前126个与ascii码相同。即在win95下接收到的是十六进制数的ascii码的字符串,可先转换为ansi码后再在win95下还原为十六进制数。

具体如下:code$ = hex$( asc ( readbuff$ ) )

另外,由于32位api函数参数的数据类型的变化,所有整形参数都被换为长整型(long)以支持32位的处理,这一点在设置返回值尤其如此。

同时,这些api函数、类型及常量在调用之前,必须先声明再使用。

五、结束语

由上可见,调用api接口的vb串口查询法具有实现简便、难度低、代码精简的突出优点。很适合一般vb程序员在编写通信程序时使用。

参考文献
1 美 darwin boyle等 《 visual basic 4 developer's guide 》
北京:机械工业出版社 1997.1
2 美 stefano maruzzi 《the microsoft windows 95 developer's guide》
北京:机械工业出版社 1997.1

本文关键:VB
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top